#include "config.h"
#include "system.h"
#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 "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"
#include "hashtab.h"
#include "tree-ssa-propagate.h"
static const int initial_cfg_capacity = 20;
struct edge_to_cases_elt
{
edge e;
tree case_labels;
};
static htab_t edge_to_cases;
struct cfg_stats_d
{
long num_merged_labels;
};
static struct cfg_stats_d cfg_stats;
static bool found_computed_goto;
static basic_block create_bb (void *, void *, basic_block);
static void make_blocks (tree);
static void factor_computed_gotos (void);
static void make_edges (void);
static void make_cond_expr_edges (basic_block);
static void make_switch_expr_edges (basic_block);
static void make_goto_expr_edges (basic_block);
static edge tree_redirect_edge_and_branch (edge, basic_block);
static edge tree_try_redirect_by_replacing_jump (edge, basic_block);
static unsigned int split_critical_edges (void);
static inline bool stmt_starts_bb_p (tree, tree);
static int tree_verify_flow_info (void);
static void tree_make_forwarder_block (edge);
static void tree_cfg2vcg (FILE *);
static inline void change_bb_for_stmt (tree t, basic_block bb);
static void tree_merge_blocks (basic_block, basic_block);
static bool tree_can_merge_blocks_p (basic_block, basic_block);
static void remove_bb (basic_block);
static edge find_taken_edge_computed_goto (basic_block, tree);
static edge find_taken_edge_cond_expr (basic_block, tree);
static edge find_taken_edge_switch_expr (basic_block, tree);
static tree find_case_label_for_value (tree, tree);
void
init_empty_tree_cfg (void)
{
init_flow ();
profile_status = PROFILE_ABSENT;
n_basic_blocks = NUM_FIXED_BLOCKS;
last_basic_block = NUM_FIXED_BLOCKS;
basic_block_info = VEC_alloc (basic_block, gc, initial_cfg_capacity);
VEC_safe_grow (basic_block, gc, basic_block_info, initial_cfg_capacity);
memset (VEC_address (basic_block, basic_block_info), 0,
sizeof (basic_block) * initial_cfg_capacity);
label_to_block_map = VEC_alloc (basic_block, gc, initial_cfg_capacity);
VEC_safe_grow (basic_block, gc, label_to_block_map, initial_cfg_capacity);
memset (VEC_address (basic_block, label_to_block_map),
0, sizeof (basic_block) * initial_cfg_capacity);
SET_BASIC_BLOCK (ENTRY_BLOCK, ENTRY_BLOCK_PTR);
SET_BASIC_BLOCK (EXIT_BLOCK, EXIT_BLOCK_PTR);
ENTRY_BLOCK_PTR->next_bb = EXIT_BLOCK_PTR;
EXIT_BLOCK_PTR->prev_bb = ENTRY_BLOCK_PTR;
}
static void
build_tree_cfg (tree *tp)
{
tree_register_cfg_hooks ();
memset ((void *) &cfg_stats, 0, sizeof (cfg_stats));
init_empty_tree_cfg ();
found_computed_goto = 0;
make_blocks (*tp);
if (found_computed_goto)
factor_computed_gotos ();
if (n_basic_blocks == NUM_FIXED_BLOCKS)
create_empty_bb (ENTRY_BLOCK_PTR);
if (VEC_length (basic_block, basic_block_info) < (size_t) n_basic_blocks)
{
size_t old_size = VEC_length (basic_block, basic_block_info);
basic_block *p;
VEC_safe_grow (basic_block, gc, basic_block_info, n_basic_blocks);
p = VEC_address (basic_block, basic_block_info);
memset (&p[old_size], 0,
sizeof (basic_block) * (n_basic_blocks - old_size));
}
cleanup_dead_labels ();
group_case_labels ();
make_edges ();
{
int local_dump_flags;
FILE *vcg_file = dump_begin (TDI_vcg, &local_dump_flags);
if (vcg_file)
{
tree_cfg2vcg (vcg_file);
dump_end (TDI_vcg, vcg_file);
}
}
#ifdef ENABLE_CHECKING
verify_stmts ();
#endif
if (dump_file)
dump_tree_cfg (dump_file, dump_flags);
}
static unsigned int
execute_build_cfg (void)
{
build_tree_cfg (&DECL_SAVED_TREE (current_function_decl));
return 0;
}
struct tree_opt_pass pass_build_cfg =
{
"cfg",
NULL,
execute_build_cfg,
NULL,
NULL,
0,
TV_TREE_CFG,
PROP_gimple_leh,
PROP_cfg,
0,
0,
TODO_verify_stmts,
0
};
static void
factor_computed_gotos (void)
{
basic_block bb;
tree factored_label_decl = NULL;
tree var = NULL;
tree factored_computed_goto_label = NULL;
tree factored_computed_goto = NULL;
FOR_EACH_BB (bb)
{
block_stmt_iterator bsi = bsi_last (bb);
tree last;
if (bsi_end_p (bsi))
continue;
last = bsi_stmt (bsi);
if (last == factored_computed_goto)
continue;
if (computed_goto_p (last))
{
tree assignment;
if (! factored_computed_goto)
{
basic_block new_bb = create_empty_bb (bb);
block_stmt_iterator new_bsi = bsi_start (new_bb);
var = create_tmp_var (ptr_type_node, "gotovar");
factored_label_decl = create_artificial_label ();
factored_computed_goto_label
= build1 (LABEL_EXPR, void_type_node, factored_label_decl);
bsi_insert_after (&new_bsi, factored_computed_goto_label,
BSI_NEW_STMT);
factored_computed_goto = build1 (GOTO_EXPR, void_type_node, var);
bsi_insert_after (&new_bsi, factored_computed_goto,
BSI_NEW_STMT);
}
assignment = build2 (MODIFY_EXPR, ptr_type_node,
var, GOTO_DESTINATION (last));
if (TREE_CODE_CLASS(TREE_CODE(GOTO_DESTINATION(last))) ==
tcc_declaration)
{
extern tree debug_find_var_in_block_tree (tree, tree);
tree block =
debug_find_var_in_block_tree(GOTO_DESTINATION(last),
DECL_INITIAL(current_function_decl));
TREE_BLOCK(assignment) = block;
}
bsi_insert_before (&bsi, assignment, BSI_SAME_STMT);
GOTO_DESTINATION (last) = factored_label_decl;
}
}
}
static void
make_blocks (tree stmt_list)
{
tree_stmt_iterator i = tsi_start (stmt_list);
tree stmt = NULL;
bool start_new_block = true;
bool first_stmt_of_list = true;
basic_block bb = ENTRY_BLOCK_PTR;
while (!tsi_end_p (i))
{
tree prev_stmt;
prev_stmt = stmt;
stmt = tsi_stmt (i);
if (start_new_block || stmt_starts_bb_p (stmt, prev_stmt))
{
if (!first_stmt_of_list)
stmt_list = tsi_split_statement_list_before (&i);
bb = create_basic_block (stmt_list, NULL, bb);
start_new_block = false;
}
set_bb_for_stmt (stmt, bb);
if (computed_goto_p (stmt))
found_computed_goto = true;
if (stmt_ends_bb_p (stmt))
start_new_block = true;
tsi_next (&i);
first_stmt_of_list = false;
}
}
static basic_block
create_bb (void *h, void *e, basic_block after)
{
basic_block bb;
gcc_assert (!e);
bb = alloc_block ();
bb->index = last_basic_block;
bb->flags = BB_NEW;
bb->stmt_list = h ? (tree) h : alloc_stmt_list ();
link_block (bb, after);
if ((size_t) last_basic_block == VEC_length (basic_block, basic_block_info))
{
size_t old_size = VEC_length (basic_block, basic_block_info);
size_t new_size = last_basic_block + (last_basic_block + 3) / 4;
basic_block *p;
VEC_safe_grow (basic_block, gc, basic_block_info, new_size);
p = VEC_address (basic_block, basic_block_info);
memset (&p[old_size], 0, sizeof (basic_block) * (new_size - old_size));
}
SET_BASIC_BLOCK (last_basic_block, bb);
n_basic_blocks++;
last_basic_block++;
return bb;
}
void
fold_cond_expr_cond (void)
{
basic_block bb;
FOR_EACH_BB (bb)
{
tree stmt = last_stmt (bb);
if (stmt
&& TREE_CODE (stmt) == COND_EXPR)
{
tree cond;
bool zerop, onep;
fold_defer_overflow_warnings ();
cond = fold (COND_EXPR_COND (stmt));
zerop = integer_zerop (cond);
onep = integer_onep (cond);
fold_undefer_overflow_warnings (((zerop || onep)
&& !TREE_NO_WARNING (stmt)),
stmt,
WARN_STRICT_OVERFLOW_CONDITIONAL);
if (zerop)
COND_EXPR_COND (stmt) = boolean_false_node;
else if (onep)
COND_EXPR_COND (stmt) = boolean_true_node;
}
}
}
static void
make_edges (void)
{
basic_block bb;
struct omp_region *cur_region = NULL;
make_edge (ENTRY_BLOCK_PTR, BASIC_BLOCK (NUM_FIXED_BLOCKS), EDGE_FALLTHRU);
FOR_EACH_BB (bb)
{
tree last = last_stmt (bb);
bool fallthru;
if (last)
{
enum tree_code code = TREE_CODE (last);
switch (code)
{
case GOTO_EXPR:
make_goto_expr_edges (bb);
fallthru = false;
break;
case RETURN_EXPR:
make_edge (bb, EXIT_BLOCK_PTR, 0);
fallthru = false;
break;
case COND_EXPR:
make_cond_expr_edges (bb);
fallthru = false;
break;
case SWITCH_EXPR:
make_switch_expr_edges (bb);
fallthru = false;
break;
case RESX_EXPR:
make_eh_edges (last);
fallthru = false;
break;
case CALL_EXPR:
if (tree_can_make_abnormal_goto (last))
make_abnormal_goto_edges (bb, true);
make_eh_edges (last);
fallthru = !(call_expr_flags (last) & ECF_NORETURN);
break;
case MODIFY_EXPR:
if (is_ctrl_altering_stmt (last))
{
if (tree_can_make_abnormal_goto (last))
make_abnormal_goto_edges (bb, true);
make_eh_edges (last);
}
fallthru = true;
break;
case OMP_PARALLEL:
case OMP_FOR:
case OMP_SINGLE:
case OMP_MASTER:
case OMP_ORDERED:
case OMP_CRITICAL:
case OMP_SECTION:
cur_region = new_omp_region (bb, code, cur_region);
fallthru = true;
break;
case OMP_SECTIONS:
cur_region = new_omp_region (bb, code, cur_region);
fallthru = false;
break;
case OMP_RETURN:
cur_region->exit = bb;
fallthru = cur_region->type != OMP_SECTION;
cur_region = cur_region->outer;
break;
case OMP_CONTINUE:
cur_region->cont = bb;
switch (cur_region->type)
{
case OMP_FOR:
break;
case OMP_SECTIONS:
{
struct omp_region *i;
for (i = cur_region->inner; i ; i = i->next)
{
gcc_assert (i->type == OMP_SECTION);
make_edge (cur_region->entry, i->entry, 0);
make_edge (i->exit, bb, EDGE_FALLTHRU);
}
}
break;
default:
gcc_unreachable ();
}
fallthru = true;
break;
default:
gcc_assert (!stmt_ends_bb_p (last));
fallthru = true;
}
}
else
fallthru = true;
if (fallthru)
make_edge (bb, bb->next_bb, EDGE_FALLTHRU);
}
if (root_omp_region)
free_omp_regions ();
fold_cond_expr_cond ();
cleanup_tree_cfg ();
}
static void
make_cond_expr_edges (basic_block bb)
{
tree entry = last_stmt (bb);
basic_block then_bb, else_bb;
tree then_label, else_label;
edge e;
gcc_assert (entry);
gcc_assert (TREE_CODE (entry) == COND_EXPR);
then_label = GOTO_DESTINATION (COND_EXPR_THEN (entry));
else_label = GOTO_DESTINATION (COND_EXPR_ELSE (entry));
then_bb = label_to_block (then_label);
else_bb = label_to_block (else_label);
e = make_edge (bb, then_bb, EDGE_TRUE_VALUE);
#ifdef USE_MAPPED_LOCATION
e->goto_locus = EXPR_LOCATION (COND_EXPR_THEN (entry));
#else
e->goto_locus = EXPR_LOCUS (COND_EXPR_THEN (entry));
#endif
e = make_edge (bb, else_bb, EDGE_FALSE_VALUE);
if (e)
{
#ifdef USE_MAPPED_LOCATION
e->goto_locus = EXPR_LOCATION (COND_EXPR_ELSE (entry));
#else
e->goto_locus = EXPR_LOCUS (COND_EXPR_ELSE (entry));
#endif
}
}
static hashval_t
edge_to_cases_hash (const void *p)
{
edge e = ((struct edge_to_cases_elt *)p)->e;
return htab_hash_pointer (e);
}
static int
edge_to_cases_eq (const void *p1, const void *p2)
{
edge e1 = ((struct edge_to_cases_elt *)p1)->e;
edge e2 = ((struct edge_to_cases_elt *)p2)->e;
return e1 == e2;
}
static void
edge_to_cases_cleanup (void *p)
{
struct edge_to_cases_elt *elt = (struct edge_to_cases_elt *) p;
tree t, next;
for (t = elt->case_labels; t; t = next)
{
next = TREE_CHAIN (t);
TREE_CHAIN (t) = NULL;
}
free (p);
}
void
start_recording_case_labels (void)
{
gcc_assert (edge_to_cases == NULL);
edge_to_cases = htab_create (37,
edge_to_cases_hash,
edge_to_cases_eq,
edge_to_cases_cleanup);
}
static bool
recording_case_labels_p (void)
{
return (edge_to_cases != NULL);
}
void
end_recording_case_labels (void)
{
htab_delete (edge_to_cases);
edge_to_cases = NULL;
}
static void
record_switch_edge (edge e, tree case_label)
{
struct edge_to_cases_elt *elt;
void **slot;
elt = XNEW (struct edge_to_cases_elt);
elt->e = e;
elt->case_labels = case_label;
slot = htab_find_slot (edge_to_cases, elt, INSERT);
if (*slot == NULL)
{
*slot = (void *)elt;
}
else
{
free (elt);
elt = (struct edge_to_cases_elt *) *slot;
TREE_CHAIN (case_label) = elt->case_labels;
elt->case_labels = case_label;
}
}
static tree
get_cases_for_edge (edge e, tree t)
{
struct edge_to_cases_elt elt, *elt_p;
void **slot;
size_t i, n;
tree vec;
if (!recording_case_labels_p ())
return NULL;
restart:
elt.e = e;
elt.case_labels = NULL;
slot = htab_find_slot (edge_to_cases, &elt, NO_INSERT);
if (slot)
{
elt_p = (struct edge_to_cases_elt *)*slot;
return elt_p->case_labels;
}
vec = SWITCH_LABELS (t);
n = TREE_VEC_LENGTH (vec);
for (i = 0; i < n; i++)
{
tree lab = CASE_LABEL (TREE_VEC_ELT (vec, i));
basic_block label_bb = label_to_block (lab);
record_switch_edge (find_edge (e->src, label_bb), TREE_VEC_ELT (vec, i));
}
goto restart;
}
static void
make_switch_expr_edges (basic_block bb)
{
tree entry = last_stmt (bb);
size_t i, n;
tree vec;
vec = SWITCH_LABELS (entry);
n = TREE_VEC_LENGTH (vec);
for (i = 0; i < n; ++i)
{
tree lab = CASE_LABEL (TREE_VEC_ELT (vec, i));
basic_block label_bb = label_to_block (lab);
make_edge (bb, label_bb, 0);
}
}
basic_block
label_to_block_fn (struct function *ifun, tree dest)
{
int uid = LABEL_DECL_UID (dest);
if ((errorcount || sorrycount) && uid < 0)
{
block_stmt_iterator bsi =
bsi_start (BASIC_BLOCK (NUM_FIXED_BLOCKS));
tree stmt;
stmt = build1 (LABEL_EXPR, void_type_node, dest);
bsi_insert_before (&bsi, stmt, BSI_NEW_STMT);
uid = LABEL_DECL_UID (dest);
}
if (VEC_length (basic_block, ifun->cfg->x_label_to_block_map)
<= (unsigned int) uid)
return NULL;
return VEC_index (basic_block, ifun->cfg->x_label_to_block_map, uid);
}
void
make_abnormal_goto_edges (basic_block bb, bool for_call)
{
basic_block target_bb;
block_stmt_iterator bsi;
FOR_EACH_BB (target_bb)
for (bsi = bsi_start (target_bb); !bsi_end_p (bsi); bsi_next (&bsi))
{
tree target = bsi_stmt (bsi);
if (TREE_CODE (target) != LABEL_EXPR)
break;
target = LABEL_EXPR_LABEL (target);
if ((FORCED_LABEL (target) && !for_call)
|| (DECL_NONLOCAL (target) && for_call))
{
make_edge (bb, target_bb, EDGE_ABNORMAL);
break;
}
}
}
static void
make_goto_expr_edges (basic_block bb)
{
block_stmt_iterator last = bsi_last (bb);
tree goto_t = bsi_stmt (last);
if (simple_goto_p (goto_t))
{
tree dest = GOTO_DESTINATION (goto_t);
edge e = make_edge (bb, label_to_block (dest), EDGE_FALLTHRU);
#ifdef USE_MAPPED_LOCATION
e->goto_locus = EXPR_LOCATION (goto_t);
#else
e->goto_locus = EXPR_LOCUS (goto_t);
#endif
bsi_remove (&last, true);
return;
}
make_abnormal_goto_edges (bb, false);
}
static tree *label_for_bb;
static void
update_eh_label (struct eh_region *region)
{
tree old_label = get_eh_region_tree_label (region);
if (old_label)
{
tree new_label;
basic_block bb = label_to_block (old_label);
if (! bb)
return;
new_label = label_for_bb[bb->index];
set_eh_region_tree_label (region, new_label);
}
}
static tree
main_block_label (tree label)
{
basic_block bb = label_to_block (label);
if (!label_for_bb[bb->index])
label_for_bb[bb->index] = label;
return label_for_bb[bb->index];
}
void
cleanup_dead_labels (void)
{
basic_block bb;
label_for_bb = XCNEWVEC (tree, last_basic_block);
FOR_EACH_BB (bb)
{
block_stmt_iterator i;
for (i = bsi_start (bb); !bsi_end_p (i); bsi_next (&i))
{
tree label, stmt = bsi_stmt (i);
if (TREE_CODE (stmt) != LABEL_EXPR)
break;
label = LABEL_EXPR_LABEL (stmt);
if (! label_for_bb[bb->index])
{
label_for_bb[bb->index] = label;
continue;
}
if (! DECL_ARTIFICIAL (label)
&& DECL_ARTIFICIAL (label_for_bb[bb->index]))
{
label_for_bb[bb->index] = label;
break;
}
}
}
FOR_EACH_BB (bb)
{
tree stmt = last_stmt (bb);
if (!stmt)
continue;
switch (TREE_CODE (stmt))
{
case COND_EXPR:
{
tree true_branch, false_branch;
true_branch = COND_EXPR_THEN (stmt);
false_branch = COND_EXPR_ELSE (stmt);
GOTO_DESTINATION (true_branch)
= main_block_label (GOTO_DESTINATION (true_branch));
GOTO_DESTINATION (false_branch)
= main_block_label (GOTO_DESTINATION (false_branch));
break;
}
case SWITCH_EXPR:
{
size_t i;
tree vec = SWITCH_LABELS (stmt);
size_t n = TREE_VEC_LENGTH (vec);
for (i = 0; i < n; ++i)
{
tree elt = TREE_VEC_ELT (vec, i);
tree label = main_block_label (CASE_LABEL (elt));
CASE_LABEL (elt) = label;
}
break;
}
case GOTO_EXPR:
if (! computed_goto_p (stmt))
{
GOTO_DESTINATION (stmt)
= main_block_label (GOTO_DESTINATION (stmt));
break;
}
default:
break;
}
}
for_each_eh_region (update_eh_label);
\
\
FOR_EACH_BB (bb)
{
block_stmt_iterator i;
tree label_for_this_bb = label_for_bb[bb->index];
if (! label_for_this_bb)
continue;
for (i = bsi_start (bb); !bsi_end_p (i); )
{
tree label, stmt = bsi_stmt (i);
if (TREE_CODE (stmt) != LABEL_EXPR)
break;
label = LABEL_EXPR_LABEL (stmt);
if (label == label_for_this_bb
|| ! DECL_ARTIFICIAL (label)
\
|| DECL_ATTRIBUTES (label)
|| DECL_USER_ALIGN (label)
\
|| DECL_NONLOCAL (label)
|| FORCED_LABEL (label))
bsi_next (&i);
else
bsi_remove (&i, true);
}
}
free (label_for_bb);
}
void
group_case_labels (void)
{
basic_block bb;
FOR_EACH_BB (bb)
{
tree stmt = last_stmt (bb);
if (stmt && TREE_CODE (stmt) == SWITCH_EXPR)
{
tree labels = SWITCH_LABELS (stmt);
int old_size = TREE_VEC_LENGTH (labels);
int i, j, new_size = old_size;
tree default_case = TREE_VEC_ELT (labels, old_size - 1);
tree default_label;
default_label = CASE_LABEL (default_case);
i = 0;
while (i < old_size - 1)
{
tree base_case, base_label, base_high;
base_case = TREE_VEC_ELT (labels, i);
gcc_assert (base_case);
base_label = CASE_LABEL (base_case);
if (base_label == default_label)
{
TREE_VEC_ELT (labels, i) = NULL_TREE;
i++;
new_size--;
continue;
}
base_high = CASE_HIGH (base_case) ?
CASE_HIGH (base_case) : CASE_LOW (base_case);
i++;
while (i < old_size - 1)
{
tree merge_case = TREE_VEC_ELT (labels, i);
tree merge_label = CASE_LABEL (merge_case);
tree t = int_const_binop (PLUS_EXPR, base_high,
integer_one_node, 1);
if (merge_label == base_label
&& tree_int_cst_equal (CASE_LOW (merge_case), t))
{
base_high = CASE_HIGH (merge_case) ?
CASE_HIGH (merge_case) : CASE_LOW (merge_case);
CASE_HIGH (base_case) = base_high;
TREE_VEC_ELT (labels, i) = NULL_TREE;
new_size--;
i++;
}
else
break;
}
}
for (i = 0, j = 0; i < new_size; i++)
{
while (! TREE_VEC_ELT (labels, j))
j++;
TREE_VEC_ELT (labels, i) = TREE_VEC_ELT (labels, j++);
}
TREE_VEC_LENGTH (labels) = new_size;
}
}
}
static bool
tree_can_merge_blocks_p (basic_block a, basic_block b)
{
tree stmt;
block_stmt_iterator bsi;
tree phi;
if (!single_succ_p (a))
return false;
if (single_succ_edge (a)->flags & EDGE_ABNORMAL)
return false;
if (single_succ (a) != b)
return false;
if (!single_pred_p (b))
return false;
if (b == EXIT_BLOCK_PTR)
return false;
stmt = last_stmt (a);
if (stmt && stmt_ends_bb_p (stmt))
return false;
if (stmt && TREE_CODE (stmt) == LABEL_EXPR
&& DECL_NONLOCAL (LABEL_EXPR_LABEL (stmt)))
return false;
phi = phi_nodes (b);
if (phi)
{
if (need_ssa_update_p ())
return false;
for (; phi; phi = PHI_CHAIN (phi))
if (!is_gimple_reg (PHI_RESULT (phi))
&& !may_propagate_copy (PHI_RESULT (phi), PHI_ARG_DEF (phi, 0)))
return false;
}
for (bsi = bsi_start (b); !bsi_end_p (bsi); bsi_next (&bsi))
{
stmt = bsi_stmt (bsi);
if (TREE_CODE (stmt) != LABEL_EXPR)
break;
if (!DECL_ARTIFICIAL (LABEL_EXPR_LABEL (stmt)))
return false;
}
if (current_loops
&& b->loop_father->latch == b)
return false;
return true;
}
void
replace_uses_by (tree name, tree val)
{
imm_use_iterator imm_iter;
use_operand_p use;
tree stmt;
edge e;
unsigned i;
FOR_EACH_IMM_USE_STMT (stmt, imm_iter, name)
{
FOR_EACH_IMM_USE_ON_STMT (use, imm_iter)
{
replace_exp (use, val);
if (TREE_CODE (stmt) == PHI_NODE)
{
e = PHI_ARG_EDGE (stmt, PHI_ARG_INDEX_FROM_USE (use));
if (e->flags & EDGE_ABNORMAL)
{
gcc_assert (!is_gimple_reg (name));
SSA_NAME_OCCURS_IN_ABNORMAL_PHI (val) = 1;
}
}
}
if (TREE_CODE (stmt) != PHI_NODE)
{
tree rhs;
fold_stmt_inplace (stmt);
rhs = get_rhs (stmt);
if (TREE_CODE (rhs) == ADDR_EXPR)
recompute_tree_invariant_for_addr_expr (rhs);
maybe_clean_or_replace_eh_stmt (stmt, stmt);
mark_new_vars_to_rename (stmt);
}
}
gcc_assert (num_imm_uses (name) == 0);
if (current_loops)
{
struct loop *loop;
for (i = 0; i < current_loops->num; i++)
{
loop = current_loops->parray[i];
if (loop)
substitute_in_loop_info (loop, name, val);
}
}
}
static void
tree_merge_blocks (basic_block a, basic_block b)
{
block_stmt_iterator bsi;
tree_stmt_iterator last;
tree phi;
if (dump_file)
fprintf (dump_file, "Merging blocks %d and %d\n", a->index, b->index);
bsi = bsi_last (a);
for (phi = phi_nodes (b); phi; phi = phi_nodes (b))
{
tree def = PHI_RESULT (phi), use = PHI_ARG_DEF (phi, 0);
tree copy;
bool may_replace_uses = may_propagate_copy (def, use);
if (current_loops
&& is_gimple_reg (def)
&& TREE_CODE (use) == SSA_NAME
&& a->loop_father != b->loop_father)
may_replace_uses = false;
if (!may_replace_uses)
{
gcc_assert (is_gimple_reg (def));
copy = build2 (MODIFY_EXPR, void_type_node, def, use);
bsi_insert_after (&bsi, copy, BSI_NEW_STMT);
SET_PHI_RESULT (phi, NULL_TREE);
SSA_NAME_DEF_STMT (def) = copy;
}
else
replace_uses_by (def, use);
remove_phi_node (phi, NULL);
}
move_block_after (b, a);
gcc_assert (single_succ_edge (a)->flags & EDGE_FALLTHRU);
gcc_assert (!last_stmt (a) || !stmt_ends_bb_p (last_stmt (a)));
for (bsi = bsi_start (b); !bsi_end_p (bsi);)
{
if (TREE_CODE (bsi_stmt (bsi)) == LABEL_EXPR)
{
tree label = bsi_stmt (bsi);
bsi_remove (&bsi, false);
if (FORCED_LABEL (LABEL_EXPR_LABEL (label)))
{
block_stmt_iterator dest_bsi = bsi_start (a);
bsi_insert_before (&dest_bsi, label, BSI_NEW_STMT);
}
}
else
{
change_bb_for_stmt (bsi_stmt (bsi), a);
bsi_next (&bsi);
}
}
last = tsi_last (a->stmt_list);
tsi_link_after (&last, b->stmt_list, TSI_NEW_STMT);
b->stmt_list = NULL;
}
basic_block
single_noncomplex_succ (basic_block bb)
{
edge e0, e1;
if (EDGE_COUNT (bb->succs) != 2)
return bb;
e0 = EDGE_SUCC (bb, 0);
e1 = EDGE_SUCC (bb, 1);
if (e0->flags & EDGE_COMPLEX)
return e1->dest;
if (e1->flags & EDGE_COMPLEX)
return e0->dest;
return bb;
}
struct rus_data
{
tree *last_goto;
bool repeat;
bool may_throw;
bool may_branch;
bool has_label;
};
static void remove_useless_stmts_1 (tree *, struct rus_data *);
static bool
remove_useless_stmts_warn_notreached (tree stmt)
{
if (EXPR_HAS_LOCATION (stmt))
{
location_t loc = EXPR_LOCATION (stmt);
if (LOCATION_LINE (loc) > 0)
{
warning (0, "%Hwill never be executed", &loc);
return true;
}
}
switch (TREE_CODE (stmt))
{
case STATEMENT_LIST:
{
tree_stmt_iterator i;
for (i = tsi_start (stmt); !tsi_end_p (i); tsi_next (&i))
if (remove_useless_stmts_warn_notreached (tsi_stmt (i)))
return true;
}
break;
case COND_EXPR:
if (remove_useless_stmts_warn_notreached (COND_EXPR_COND (stmt)))
return true;
if (remove_useless_stmts_warn_notreached (COND_EXPR_THEN (stmt)))
return true;
if (remove_useless_stmts_warn_notreached (COND_EXPR_ELSE (stmt)))
return true;
break;
case TRY_FINALLY_EXPR:
case TRY_CATCH_EXPR:
if (remove_useless_stmts_warn_notreached (TREE_OPERAND (stmt, 0)))
return true;
if (remove_useless_stmts_warn_notreached (TREE_OPERAND (stmt, 1)))
return true;
break;
case CATCH_EXPR:
return remove_useless_stmts_warn_notreached (CATCH_BODY (stmt));
case EH_FILTER_EXPR:
return remove_useless_stmts_warn_notreached (EH_FILTER_FAILURE (stmt));
case BIND_EXPR:
return remove_useless_stmts_warn_notreached (BIND_EXPR_BLOCK (stmt));
default:
break;
}
return false;
}
static void
remove_useless_stmts_cond (tree *stmt_p, struct rus_data *data)
{
tree then_clause, else_clause, cond;
bool save_has_label, then_has_label, else_has_label;
save_has_label = data->has_label;
data->has_label = false;
data->last_goto = NULL;
remove_useless_stmts_1 (&COND_EXPR_THEN (*stmt_p), data);
then_has_label = data->has_label;
data->has_label = false;
data->last_goto = NULL;
remove_useless_stmts_1 (&COND_EXPR_ELSE (*stmt_p), data);
else_has_label = data->has_label;
data->has_label = save_has_label | then_has_label | else_has_label;
then_clause = COND_EXPR_THEN (*stmt_p);
else_clause = COND_EXPR_ELSE (*stmt_p);
cond = fold (COND_EXPR_COND (*stmt_p));
if (!TREE_SIDE_EFFECTS (then_clause) && !TREE_SIDE_EFFECTS (else_clause))
{
*stmt_p = build_empty_stmt ();
data->repeat = true;
}
else if (integer_nonzerop (cond) && !else_has_label)
{
if (warn_notreached)
remove_useless_stmts_warn_notreached (else_clause);
*stmt_p = then_clause;
data->repeat = true;
}
else if (integer_zerop (cond) && !then_has_label)
{
if (warn_notreached)
remove_useless_stmts_warn_notreached (then_clause);
*stmt_p = else_clause;
data->repeat = true;
}
else
{
tree then_stmt = expr_only (then_clause);
tree else_stmt = expr_only (else_clause);
if (then_stmt && else_stmt
&& TREE_CODE (then_stmt) == GOTO_EXPR
&& TREE_CODE (else_stmt) == GOTO_EXPR
&& (GOTO_DESTINATION (then_stmt) == GOTO_DESTINATION (else_stmt)))
{
*stmt_p = then_stmt;
data->repeat = true;
}
else if (TREE_CODE (cond) == VAR_DECL || TREE_CODE (cond) == PARM_DECL)
{
if (else_stmt
&& TREE_CODE (else_stmt) == MODIFY_EXPR
&& TREE_OPERAND (else_stmt, 0) == cond
&& integer_zerop (TREE_OPERAND (else_stmt, 1)))
COND_EXPR_ELSE (*stmt_p) = alloc_stmt_list ();
}
else if ((TREE_CODE (cond) == EQ_EXPR || TREE_CODE (cond) == NE_EXPR)
&& (TREE_CODE (TREE_OPERAND (cond, 0)) == VAR_DECL
|| TREE_CODE (TREE_OPERAND (cond, 0)) == PARM_DECL)
&& TREE_CONSTANT (TREE_OPERAND (cond, 1)))
{
tree stmt = (TREE_CODE (cond) == EQ_EXPR
? then_stmt : else_stmt);
tree *location = (TREE_CODE (cond) == EQ_EXPR
? &COND_EXPR_THEN (*stmt_p)
: &COND_EXPR_ELSE (*stmt_p));
if (stmt
&& TREE_CODE (stmt) == MODIFY_EXPR
&& TREE_OPERAND (stmt, 0) == TREE_OPERAND (cond, 0)
&& TREE_OPERAND (stmt, 1) == TREE_OPERAND (cond, 1))
*location = alloc_stmt_list ();
}
}
data->last_goto = NULL;
}
static void
remove_useless_stmts_tf (tree *stmt_p, struct rus_data *data)
{
bool save_may_branch, save_may_throw;
bool this_may_branch, this_may_throw;
save_may_branch = data->may_branch;
save_may_throw = data->may_throw;
data->may_branch = false;
data->may_throw = false;
data->last_goto = NULL;
remove_useless_stmts_1 (&TREE_OPERAND (*stmt_p, 0), data);
this_may_branch = data->may_branch;
this_may_throw = data->may_throw;
data->may_branch |= save_may_branch;
data->may_throw |= save_may_throw;
data->last_goto = NULL;
remove_useless_stmts_1 (&TREE_OPERAND (*stmt_p, 1), data);
if (!TREE_SIDE_EFFECTS (TREE_OPERAND (*stmt_p, 0)))
{
*stmt_p = TREE_OPERAND (*stmt_p, 1);
data->repeat = true;
}
else if (!TREE_SIDE_EFFECTS (TREE_OPERAND (*stmt_p, 1)))
{
*stmt_p = TREE_OPERAND (*stmt_p, 0);
data->repeat = true;
}
else if (!this_may_branch && !this_may_throw)
{
tree stmt = *stmt_p;
*stmt_p = TREE_OPERAND (stmt, 0);
append_to_statement_list (TREE_OPERAND (stmt, 1), stmt_p);
data->repeat = true;
}
}
static void
remove_useless_stmts_tc (tree *stmt_p, struct rus_data *data)
{
bool save_may_throw, this_may_throw;
tree_stmt_iterator i;
tree stmt;
save_may_throw = data->may_throw;
data->may_throw = false;
data->last_goto = NULL;
remove_useless_stmts_1 (&TREE_OPERAND (*stmt_p, 0), data);
this_may_throw = data->may_throw;
data->may_throw = save_may_throw;
if (!this_may_throw)
{
if (warn_notreached)
remove_useless_stmts_warn_notreached (TREE_OPERAND (*stmt_p, 1));
*stmt_p = TREE_OPERAND (*stmt_p, 0);
data->repeat = true;
return;
}
this_may_throw = true;
i = tsi_start (TREE_OPERAND (*stmt_p, 1));
stmt = tsi_stmt (i);
data->last_goto = NULL;
switch (TREE_CODE (stmt))
{
case CATCH_EXPR:
for (; !tsi_end_p (i); tsi_next (&i))
{
stmt = tsi_stmt (i);
if (CATCH_TYPES (stmt) == NULL)
this_may_throw = false;
data->last_goto = NULL;
remove_useless_stmts_1 (&CATCH_BODY (stmt), data);
}
break;
case EH_FILTER_EXPR:
if (EH_FILTER_MUST_NOT_THROW (stmt))
this_may_throw = false;
else if (EH_FILTER_TYPES (stmt) == NULL)
this_may_throw = false;
remove_useless_stmts_1 (&EH_FILTER_FAILURE (stmt), data);
break;
default:
remove_useless_stmts_1 (&TREE_OPERAND (*stmt_p, 1), data);
if (!TREE_SIDE_EFFECTS (TREE_OPERAND (*stmt_p, 1)))
{
*stmt_p = TREE_OPERAND (*stmt_p, 0);
data->repeat = true;
}
break;
}
data->may_throw |= this_may_throw;
}
static void
remove_useless_stmts_bind (tree *stmt_p, struct rus_data *data)
{
tree block;
remove_useless_stmts_1 (&BIND_EXPR_BODY (*stmt_p), data);
block = BIND_EXPR_BLOCK (*stmt_p);
if (BIND_EXPR_VARS (*stmt_p) == NULL_TREE
&& *stmt_p != DECL_SAVED_TREE (current_function_decl)
&& (! block
|| ! BLOCK_ABSTRACT_ORIGIN (block)
|| (TREE_CODE (BLOCK_ABSTRACT_ORIGIN (block))
!= FUNCTION_DECL)))
{
*stmt_p = BIND_EXPR_BODY (*stmt_p);
data->repeat = true;
}
}
static void
remove_useless_stmts_goto (tree *stmt_p, struct rus_data *data)
{
tree dest = GOTO_DESTINATION (*stmt_p);
data->may_branch = true;
data->last_goto = NULL;
if (TREE_CODE (dest) == LABEL_DECL)
data->last_goto = stmt_p;
}
static void
remove_useless_stmts_label (tree *stmt_p, struct rus_data *data)
{
tree label = LABEL_EXPR_LABEL (*stmt_p);
data->has_label = true;
if (DECL_NONLOCAL (label))
data->last_goto = NULL;
else if (data->last_goto && GOTO_DESTINATION (*data->last_goto) == label)
{
*data->last_goto = build_empty_stmt ();
data->repeat = true;
}
}
static void
update_call_expr_flags (tree call)
{
tree decl = get_callee_fndecl (call);
if (!decl)
return;
if (call_expr_flags (call) & (ECF_CONST | ECF_PURE))
TREE_SIDE_EFFECTS (call) = 0;
if (TREE_NOTHROW (decl))
TREE_NOTHROW (call) = 1;
}
void
notice_special_calls (tree t)
{
int flags = call_expr_flags (t);
if (flags & ECF_MAY_BE_ALLOCA)
current_function_calls_alloca = true;
if (flags & ECF_RETURNS_TWICE)
current_function_calls_setjmp = true;
}
void
clear_special_calls (void)
{
current_function_calls_alloca = false;
current_function_calls_setjmp = false;
}
static void
remove_useless_stmts_1 (tree *tp, struct rus_data *data)
{
tree t = *tp, op;
switch (TREE_CODE (t))
{
case COND_EXPR:
remove_useless_stmts_cond (tp, data);
break;
case TRY_FINALLY_EXPR:
remove_useless_stmts_tf (tp, data);
break;
case TRY_CATCH_EXPR:
remove_useless_stmts_tc (tp, data);
break;
case BIND_EXPR:
remove_useless_stmts_bind (tp, data);
break;
case GOTO_EXPR:
remove_useless_stmts_goto (tp, data);
break;
case LABEL_EXPR:
remove_useless_stmts_label (tp, data);
break;
case RETURN_EXPR:
fold_stmt (tp);
data->last_goto = NULL;
data->may_branch = true;
break;
case CALL_EXPR:
fold_stmt (tp);
data->last_goto = NULL;
notice_special_calls (t);
update_call_expr_flags (t);
if (tree_could_throw_p (t))
data->may_throw = true;
break;
case MODIFY_EXPR:
data->last_goto = NULL;
fold_stmt (tp);
op = get_call_expr_in (t);
if (op)
{
update_call_expr_flags (op);
notice_special_calls (op);
}
if (tree_could_throw_p (t))
data->may_throw = true;
break;
case STATEMENT_LIST:
{
tree_stmt_iterator i = tsi_start (t);
while (!tsi_end_p (i))
{
t = tsi_stmt (i);
if (IS_EMPTY_STMT (t))
{
tsi_delink (&i);
continue;
}
remove_useless_stmts_1 (tsi_stmt_ptr (i), data);
t = tsi_stmt (i);
if (TREE_CODE (t) == STATEMENT_LIST)
{
tsi_link_before (&i, t, TSI_SAME_STMT);
tsi_delink (&i);
}
else
tsi_next (&i);
}
}
break;
case ASM_EXPR:
fold_stmt (tp);
data->last_goto = NULL;
break;
default:
data->last_goto = NULL;
break;
}
}
static unsigned int
remove_useless_stmts (void)
{
struct rus_data data;
clear_special_calls ();
do
{
memset (&data, 0, sizeof (data));
remove_useless_stmts_1 (&DECL_SAVED_TREE (current_function_decl), &data);
}
while (data.repeat);
return 0;
}
struct tree_opt_pass pass_remove_useless_stmts =
{
"useless",
NULL,
remove_useless_stmts,
NULL,
NULL,
0,
0,
PROP_gimple_any,
0,
0,
0,
TODO_dump_func,
0
};
static void
remove_phi_nodes_and_edges_for_unreachable_block (basic_block bb)
{
tree phi;
phi = phi_nodes (bb);
while (phi)
{
tree next = PHI_CHAIN (phi);
remove_phi_node (phi, NULL_TREE);
phi = next;
}
while (EDGE_COUNT (bb->succs) > 0)
remove_edge (EDGE_SUCC (bb, 0));
}
static void
remove_bb (basic_block bb)
{
block_stmt_iterator i;
#ifdef USE_MAPPED_LOCATION
source_location loc = UNKNOWN_LOCATION;
#else
source_locus loc = 0;
#endif
if (dump_file)
{
fprintf (dump_file, "Removing basic block %d\n", bb->index);
if (dump_flags & TDF_DETAILS)
{
dump_bb (bb, dump_file, 0);
fprintf (dump_file, "\n");
}
}
if (current_loops)
{
struct loop *loop = bb->loop_father;
if (loop->latch == bb
|| loop->header == bb)
{
loop->latch = NULL;
loop->header = NULL;
free_numbers_of_iterations_estimates_loop (loop);
}
}
for (i = bsi_start (bb); !bsi_end_p (i);)
{
tree stmt = bsi_stmt (i);
if (TREE_CODE (stmt) == LABEL_EXPR
&& (FORCED_LABEL (LABEL_EXPR_LABEL (stmt))
|| DECL_NONLOCAL (LABEL_EXPR_LABEL (stmt))))
{
basic_block new_bb;
block_stmt_iterator new_bsi;
if (DECL_NONLOCAL (LABEL_EXPR_LABEL (stmt)))
{
DECL_NONLOCAL (LABEL_EXPR_LABEL (stmt)) = 0;
FORCED_LABEL (LABEL_EXPR_LABEL (stmt)) = 1;
}
new_bb = bb->prev_bb;
new_bsi = bsi_start (new_bb);
bsi_remove (&i, false);
bsi_insert_before (&new_bsi, stmt, BSI_NEW_STMT);
}
else
{
if (in_ssa_p)
release_defs (stmt);
bsi_remove (&i, true);
}
if (TREE_CODE (stmt) != GOTO_EXPR && EXPR_HAS_LOCATION (stmt) && !loc)
{
#ifdef USE_MAPPED_LOCATION
if (EXPR_HAS_LOCATION (stmt))
loc = EXPR_LOCATION (stmt);
#else
source_locus t;
t = EXPR_LOCUS (stmt);
if (t && LOCATION_LINE (*t) > 0)
loc = t;
#endif
}
}
#ifdef USE_MAPPED_LOCATION
if (loc > BUILTINS_LOCATION)
warning (OPT_Wunreachable_code, "%Hwill never be executed", &loc);
#else
if (loc)
warning (OPT_Wunreachable_code, "%Hwill never be executed", loc);
#endif
remove_phi_nodes_and_edges_for_unreachable_block (bb);
}
edge
find_taken_edge (basic_block bb, tree val)
{
tree stmt;
stmt = last_stmt (bb);
gcc_assert (stmt);
gcc_assert (is_ctrl_stmt (stmt));
gcc_assert (val);
if (! is_gimple_min_invariant (val))
return NULL;
if (TREE_CODE (stmt) == COND_EXPR)
return find_taken_edge_cond_expr (bb, val);
if (TREE_CODE (stmt) == SWITCH_EXPR)
return find_taken_edge_switch_expr (bb, val);
if (computed_goto_p (stmt))
{
if ((TREE_CODE (val) == ADDR_EXPR || TREE_CODE (val) == LABEL_EXPR)
&& TREE_CODE (TREE_OPERAND (val, 0)) == LABEL_DECL)
return find_taken_edge_computed_goto (bb, TREE_OPERAND (val, 0));
return NULL;
}
gcc_unreachable ();
}
static edge
find_taken_edge_computed_goto (basic_block bb, tree val)
{
basic_block dest;
edge e = NULL;
dest = label_to_block (val);
if (dest)
{
e = find_edge (bb, dest);
gcc_assert (e != NULL);
}
return e;
}
static edge
find_taken_edge_cond_expr (basic_block bb, tree val)
{
edge true_edge, false_edge;
extract_true_false_edges_from_block (bb, &true_edge, &false_edge);
gcc_assert (TREE_CODE (val) == INTEGER_CST);
return (zero_p (val) ? false_edge : true_edge);
}
static edge
find_taken_edge_switch_expr (basic_block bb, tree val)
{
tree switch_expr, taken_case;
basic_block dest_bb;
edge e;
switch_expr = last_stmt (bb);
taken_case = find_case_label_for_value (switch_expr, val);
dest_bb = label_to_block (CASE_LABEL (taken_case));
e = find_edge (bb, dest_bb);
gcc_assert (e);
return e;
}
static tree
find_case_label_for_value (tree switch_expr, tree val)
{
tree vec = SWITCH_LABELS (switch_expr);
size_t low, high, n = TREE_VEC_LENGTH (vec);
tree default_case = TREE_VEC_ELT (vec, n - 1);
for (low = -1, high = n - 1; high - low > 1; )
{
size_t i = (high + low) / 2;
tree t = TREE_VEC_ELT (vec, i);
int cmp;
cmp = tree_int_cst_compare (CASE_LOW (t), val);
if (cmp > 0)
high = i;
else
low = i;
if (CASE_HIGH (t) == NULL)
{
if (cmp == 0)
return t;
}
else
{
if (cmp <= 0 && tree_int_cst_compare (CASE_HIGH (t), val) >= 0)
return t;
}
}
return default_case;
}
void
tree_dump_bb (basic_block bb, FILE *outf, int indent)
{
dump_generic_bb (outf, bb, indent, TDF_VOPS);
}
void
debug_tree_bb (basic_block bb)
{
dump_bb (bb, stderr, 0);
}
basic_block
debug_tree_bb_n (int n)
{
debug_tree_bb (BASIC_BLOCK (n));
return BASIC_BLOCK (n);
}
void
debug_tree_cfg (int flags)
{
dump_tree_cfg (stderr, flags);
}
void
dump_tree_cfg (FILE *file, int flags)
{
if (flags & TDF_DETAILS)
{
const char *funcname
= lang_hooks.decl_printable_name (current_function_decl, 2);
fputc ('\n', file);
fprintf (file, ";; Function %s\n\n", funcname);
fprintf (file, ";; \n%d basic blocks, %d edges, last basic block %d.\n\n",
n_basic_blocks, n_edges, last_basic_block);
brief_dump_cfg (file);
fprintf (file, "\n");
}
if (flags & TDF_STATS)
dump_cfg_stats (file);
dump_function_to_file (current_function_decl, file, flags | TDF_BLOCKS);
}
void
dump_cfg_stats (FILE *file)
{
static long max_num_merged_labels = 0;
unsigned long size, total = 0;
long num_edges;
basic_block bb;
const char * const fmt_str = "%-30s%-13s%12s\n";
const char * const fmt_str_1 = "%-30s%13d%11lu%c\n";
const char * const fmt_str_2 = "%-30s%13ld%11lu%c\n";
const char * const fmt_str_3 = "%-43s%11lu%c\n";
const char *funcname
= lang_hooks.decl_printable_name (current_function_decl, 2);
fprintf (file, "\nCFG Statistics for %s\n\n", funcname);
fprintf (file, "---------------------------------------------------------\n");
fprintf (file, fmt_str, "", " Number of ", "Memory");
fprintf (file, fmt_str, "", " instances ", "used ");
fprintf (file, "---------------------------------------------------------\n");
size = n_basic_blocks * sizeof (struct basic_block_def);
total += size;
fprintf (file, fmt_str_1, "Basic blocks", n_basic_blocks,
SCALE (size), LABEL (size));
num_edges = 0;
FOR_EACH_BB (bb)
num_edges += EDGE_COUNT (bb->succs);
size = num_edges * sizeof (struct edge_def);
total += size;
fprintf (file, fmt_str_2, "Edges", num_edges, SCALE (size), LABEL (size));
fprintf (file, "---------------------------------------------------------\n");
fprintf (file, fmt_str_3, "Total memory used by CFG data", SCALE (total),
LABEL (total));
fprintf (file, "---------------------------------------------------------\n");
fprintf (file, "\n");
if (cfg_stats.num_merged_labels > max_num_merged_labels)
max_num_merged_labels = cfg_stats.num_merged_labels;
fprintf (file, "Coalesced label blocks: %ld (Max so far: %ld)\n",
cfg_stats.num_merged_labels, max_num_merged_labels);
fprintf (file, "\n");
}
void
debug_cfg_stats (void)
{
dump_cfg_stats (stderr);
}
static void
tree_cfg2vcg (FILE *file)
{
edge e;
edge_iterator ei;
basic_block bb;
const char *funcname
= lang_hooks.decl_printable_name (current_function_decl, 2);
fprintf (file, "graph: { title: \"%s\"\n", funcname);
fprintf (file, "node: { title: \"ENTRY\" label: \"ENTRY\" }\n");
fprintf (file, "node: { title: \"EXIT\" label: \"EXIT\" }\n");
FOR_EACH_EDGE (e, ei, ENTRY_BLOCK_PTR->succs)
{
fprintf (file, "edge: { sourcename: \"ENTRY\" targetname: \"%d\"",
e->dest->index);
if (e->flags & EDGE_FAKE)
fprintf (file, " linestyle: dotted priority: 10");
else
fprintf (file, " linestyle: solid priority: 100");
fprintf (file, " }\n");
}
fputc ('\n', file);
FOR_EACH_BB (bb)
{
enum tree_code head_code, end_code;
const char *head_name, *end_name;
int head_line = 0;
int end_line = 0;
tree first = first_stmt (bb);
tree last = last_stmt (bb);
if (first)
{
head_code = TREE_CODE (first);
head_name = tree_code_name[head_code];
head_line = get_lineno (first);
}
else
head_name = "no-statement";
if (last)
{
end_code = TREE_CODE (last);
end_name = tree_code_name[end_code];
end_line = get_lineno (last);
}
else
end_name = "no-statement";
fprintf (file, "node: { title: \"%d\" label: \"#%d\\n%s (%d)\\n%s (%d)\"}\n",
bb->index, bb->index, head_name, head_line, end_name,
end_line);
FOR_EACH_EDGE (e, ei, bb->succs)
{
if (e->dest == EXIT_BLOCK_PTR)
fprintf (file, "edge: { sourcename: \"%d\" targetname: \"EXIT\"", bb->index);
else
fprintf (file, "edge: { sourcename: \"%d\" targetname: \"%d\"", bb->index, e->dest->index);
if (e->flags & EDGE_FAKE)
fprintf (file, " priority: 10 linestyle: dotted");
else
fprintf (file, " priority: 100 linestyle: solid");
fprintf (file, " }\n");
}
if (bb->next_bb != EXIT_BLOCK_PTR)
fputc ('\n', file);
}
fputs ("}\n\n", file);
}
bool
is_ctrl_stmt (tree t)
{
return (TREE_CODE (t) == COND_EXPR
|| TREE_CODE (t) == SWITCH_EXPR
|| TREE_CODE (t) == GOTO_EXPR
|| TREE_CODE (t) == RETURN_EXPR
|| TREE_CODE (t) == RESX_EXPR);
}
bool
is_ctrl_altering_stmt (tree t)
{
tree call;
gcc_assert (t);
call = get_call_expr_in (t);
if (call)
{
if (TREE_SIDE_EFFECTS (call) && current_function_has_nonlocal_label)
return true;
if (call_expr_flags (call) & ECF_NORETURN)
return true;
}
if (OMP_DIRECTIVE_P (t))
return true;
return tree_can_throw_internal (t);
}
bool
computed_goto_p (tree t)
{
return (TREE_CODE (t) == GOTO_EXPR
&& TREE_CODE (GOTO_DESTINATION (t)) != LABEL_DECL);
}
bool
simple_goto_p (tree t)
{
return (TREE_CODE (t) == GOTO_EXPR
&& TREE_CODE (GOTO_DESTINATION (t)) == LABEL_DECL);
}
bool
tree_can_make_abnormal_goto (tree t)
{
if (computed_goto_p (t))
return true;
if (TREE_CODE (t) == MODIFY_EXPR)
t = TREE_OPERAND (t, 1);
if (TREE_CODE (t) == WITH_SIZE_EXPR)
t = TREE_OPERAND (t, 0);
if (TREE_CODE (t) == CALL_EXPR)
return TREE_SIDE_EFFECTS (t) && current_function_has_nonlocal_label;
return false;
}
static inline bool
stmt_starts_bb_p (tree t, tree prev_t)
{
if (t == NULL_TREE)
return false;
if (TREE_CODE (t) == LABEL_EXPR)
{
if (DECL_NONLOCAL (LABEL_EXPR_LABEL (t))
|| FORCED_LABEL (LABEL_EXPR_LABEL (t)))
return true;
if (prev_t && TREE_CODE (prev_t) == LABEL_EXPR)
{
if (DECL_NONLOCAL (LABEL_EXPR_LABEL (prev_t)))
return true;
cfg_stats.num_merged_labels++;
return false;
}
else
return true;
}
return false;
}
bool
stmt_ends_bb_p (tree t)
{
return is_ctrl_stmt (t) || is_ctrl_altering_stmt (t);
}
void
disband_implicit_edges (void)
{
basic_block bb;
block_stmt_iterator last;
edge e;
edge_iterator ei;
tree stmt, label;
FOR_EACH_BB (bb)
{
last = bsi_last (bb);
stmt = last_stmt (bb);
if (stmt && TREE_CODE (stmt) == COND_EXPR)
{
e = find_edge (bb, bb->next_bb);
if (e)
{
if (e->flags & EDGE_TRUE_VALUE)
COND_EXPR_THEN (stmt) = build_empty_stmt ();
else if (e->flags & EDGE_FALSE_VALUE)
COND_EXPR_ELSE (stmt) = build_empty_stmt ();
else
gcc_unreachable ();
e->flags |= EDGE_FALLTHRU;
}
continue;
}
if (stmt && TREE_CODE (stmt) == RETURN_EXPR)
{
gcc_assert (single_succ_p (bb));
gcc_assert (single_succ (bb) == EXIT_BLOCK_PTR);
if (bb->next_bb == EXIT_BLOCK_PTR
&& !TREE_OPERAND (stmt, 0))
{
bsi_remove (&last, true);
single_succ_edge (bb)->flags |= EDGE_FALLTHRU;
}
continue;
}
if (stmt && is_ctrl_stmt (stmt))
continue;
FOR_EACH_EDGE (e, ei, bb->succs)
if (e->flags & EDGE_FALLTHRU)
break;
if (!e || e->dest == bb->next_bb)
continue;
gcc_assert (e->dest != EXIT_BLOCK_PTR);
label = tree_block_label (e->dest);
stmt = build1 (GOTO_EXPR, void_type_node, label);
#ifdef USE_MAPPED_LOCATION
SET_EXPR_LOCATION (stmt, e->goto_locus);
#else
SET_EXPR_LOCUS (stmt, e->goto_locus);
#endif
bsi_insert_after (&last, stmt, BSI_NEW_STMT);
e->flags &= ~EDGE_FALLTHRU;
}
}
void
delete_tree_cfg_annotations (void)
{
label_to_block_map = NULL;
}
tree
first_stmt (basic_block bb)
{
block_stmt_iterator i = bsi_start (bb);
return !bsi_end_p (i) ? bsi_stmt (i) : NULL_TREE;
}
tree
last_stmt (basic_block bb)
{
block_stmt_iterator b = bsi_last (bb);
return !bsi_end_p (b) ? bsi_stmt (b) : NULL_TREE;
}
tree *
last_stmt_ptr (basic_block bb)
{
block_stmt_iterator last = bsi_last (bb);
return !bsi_end_p (last) ? bsi_stmt_ptr (last) : NULL;
}
tree
last_and_only_stmt (basic_block bb)
{
block_stmt_iterator i = bsi_last (bb);
tree last, prev;
if (bsi_end_p (i))
return NULL_TREE;
last = bsi_stmt (i);
bsi_prev (&i);
if (bsi_end_p (i))
return last;
prev = bsi_stmt (i);
if (TREE_CODE (prev) == LABEL_EXPR)
return last;
else
return NULL_TREE;
}
void
set_bb_for_stmt (tree t, basic_block bb)
{
if (TREE_CODE (t) == PHI_NODE)
PHI_BB (t) = bb;
else if (TREE_CODE (t) == STATEMENT_LIST)
{
tree_stmt_iterator i;
for (i = tsi_start (t); !tsi_end_p (i); tsi_next (&i))
set_bb_for_stmt (tsi_stmt (i), bb);
}
else
{
stmt_ann_t ann = get_stmt_ann (t);
ann->bb = bb;
if (TREE_CODE (t) == LABEL_EXPR)
{
int uid;
t = LABEL_EXPR_LABEL (t);
uid = LABEL_DECL_UID (t);
if (uid == -1)
{
unsigned old_len = VEC_length (basic_block, label_to_block_map);
LABEL_DECL_UID (t) = uid = cfun->last_label_uid++;
if (old_len <= (unsigned) uid)
{
basic_block *addr;
unsigned new_len = 3 * uid / 2;
VEC_safe_grow (basic_block, gc, label_to_block_map,
new_len);
addr = VEC_address (basic_block, label_to_block_map);
memset (&addr[old_len],
0, sizeof (basic_block) * (new_len - old_len));
}
}
else
gcc_assert (!bb
|| !VEC_index (basic_block, label_to_block_map, uid));
VEC_replace (basic_block, label_to_block_map, uid, bb);
}
}
}
static inline void
change_bb_for_stmt (tree t, basic_block bb)
{
get_stmt_ann (t)->bb = bb;
if (TREE_CODE (t) == LABEL_EXPR)
VEC_replace (basic_block, label_to_block_map,
LABEL_DECL_UID (LABEL_EXPR_LABEL (t)), bb);
}
extern block_stmt_iterator
bsi_for_stmt (tree stmt)
{
block_stmt_iterator bsi;
for (bsi = bsi_start (bb_for_stmt (stmt)); !bsi_end_p (bsi); bsi_next (&bsi))
if (bsi_stmt (bsi) == stmt)
return bsi;
gcc_unreachable ();
}
static inline void
update_modified_stmts (tree t)
{
if (TREE_CODE (t) == STATEMENT_LIST)
{
tree_stmt_iterator i;
tree stmt;
for (i = tsi_start (t); !tsi_end_p (i); tsi_next (&i))
{
stmt = tsi_stmt (i);
update_stmt_if_modified (stmt);
}
}
else
update_stmt_if_modified (t);
}
void
bsi_insert_before (block_stmt_iterator *i, tree t, enum bsi_iterator_update m)
{
set_bb_for_stmt (t, i->bb);
update_modified_stmts (t);
tsi_link_before (&i->tsi, t, m);
}
void
bsi_insert_after (block_stmt_iterator *i, tree t, enum bsi_iterator_update m)
{
set_bb_for_stmt (t, i->bb);
update_modified_stmts (t);
tsi_link_after (&i->tsi, t, m);
}
void
bsi_remove (block_stmt_iterator *i, bool remove_eh_info)
{
tree t = bsi_stmt (*i);
set_bb_for_stmt (t, NULL);
delink_stmt_imm_use (t);
tsi_delink (&i->tsi);
mark_stmt_modified (t);
if (remove_eh_info)
remove_stmt_from_eh_region (t);
}
void
bsi_move_after (block_stmt_iterator *from, block_stmt_iterator *to)
{
tree stmt = bsi_stmt (*from);
bsi_remove (from, false);
bsi_insert_after (to, stmt, BSI_SAME_STMT);
}
void
bsi_move_before (block_stmt_iterator *from, block_stmt_iterator *to)
{
tree stmt = bsi_stmt (*from);
bsi_remove (from, false);
bsi_insert_before (to, stmt, BSI_SAME_STMT);
}
void
bsi_move_to_bb_end (block_stmt_iterator *from, basic_block bb)
{
block_stmt_iterator last = bsi_last (bb);
if (!bsi_end_p (last) && is_ctrl_stmt (bsi_stmt (last)))
bsi_move_before (from, &last);
else
bsi_move_after (from, &last);
}
void
bsi_replace (const block_stmt_iterator *bsi, tree stmt, bool update_eh_info)
{
int eh_region;
tree orig_stmt = bsi_stmt (*bsi);
SET_EXPR_LOCUS (stmt, EXPR_LOCUS (orig_stmt));
set_bb_for_stmt (stmt, bsi->bb);
if (update_eh_info)
{
eh_region = lookup_stmt_eh_region (orig_stmt);
if (eh_region >= 0)
{
remove_stmt_from_eh_region (orig_stmt);
add_stmt_to_eh_region (stmt, eh_region);
}
}
delink_stmt_imm_use (orig_stmt);
*bsi_stmt_ptr (*bsi) = stmt;
mark_stmt_modified (stmt);
update_modified_stmts (stmt);
}
static bool
tree_find_edge_insert_loc (edge e, block_stmt_iterator *bsi,
basic_block *new_bb)
{
basic_block dest, src;
tree tmp;
dest = e->dest;
restart:
if (single_pred_p (dest)
&& ! phi_nodes (dest)
&& dest != EXIT_BLOCK_PTR)
{
*bsi = bsi_start (dest);
if (bsi_end_p (*bsi))
return true;
tmp = bsi_stmt (*bsi);
while (TREE_CODE (tmp) == LABEL_EXPR)
{
bsi_next (bsi);
if (bsi_end_p (*bsi))
break;
tmp = bsi_stmt (*bsi);
}
if (bsi_end_p (*bsi))
{
*bsi = bsi_last (dest);
return true;
}
else
return false;
}
src = e->src;
if ((e->flags & EDGE_ABNORMAL) == 0
&& single_succ_p (src)
&& src != ENTRY_BLOCK_PTR)
{
*bsi = bsi_last (src);
if (bsi_end_p (*bsi))
return true;
tmp = bsi_stmt (*bsi);
if (!stmt_ends_bb_p (tmp))
return true;
if (TREE_CODE (tmp) == RETURN_EXPR)
{
tree op = TREE_OPERAND (tmp, 0);
if (op && !is_gimple_val (op))
{
gcc_assert (TREE_CODE (op) == MODIFY_EXPR);
bsi_insert_before (bsi, op, BSI_NEW_STMT);
TREE_OPERAND (tmp, 0) = TREE_OPERAND (op, 0);
}
bsi_prev (bsi);
return true;
}
}
dest = split_edge (e);
if (new_bb)
*new_bb = dest;
e = single_pred_edge (dest);
goto restart;
}
void
bsi_commit_edge_inserts (void)
{
basic_block bb;
edge e;
edge_iterator ei;
bsi_commit_one_edge_insert (single_succ_edge (ENTRY_BLOCK_PTR), NULL);
FOR_EACH_BB (bb)
FOR_EACH_EDGE (e, ei, bb->succs)
bsi_commit_one_edge_insert (e, NULL);
}
void
bsi_commit_one_edge_insert (edge e, basic_block *new_bb)
{
if (new_bb)
*new_bb = NULL;
if (PENDING_STMT (e))
{
block_stmt_iterator bsi;
tree stmt = PENDING_STMT (e);
PENDING_STMT (e) = NULL_TREE;
if (tree_find_edge_insert_loc (e, &bsi, new_bb))
bsi_insert_after (&bsi, stmt, BSI_NEW_STMT);
else
bsi_insert_before (&bsi, stmt, BSI_NEW_STMT);
}
}
void
bsi_insert_on_edge (edge e, tree stmt)
{
append_to_statement_list (stmt, &PENDING_STMT (e));
}
basic_block
bsi_insert_on_edge_immediate (edge e, tree stmt)
{
block_stmt_iterator bsi;
basic_block new_bb = NULL;
gcc_assert (!PENDING_STMT (e));
if (tree_find_edge_insert_loc (e, &bsi, &new_bb))
bsi_insert_after (&bsi, stmt, BSI_NEW_STMT);
else
bsi_insert_before (&bsi, stmt, BSI_NEW_STMT);
return new_bb;
}
static void
reinstall_phi_args (edge new_edge, edge old_edge)
{
tree var, phi;
if (!PENDING_STMT (old_edge))
return;
for (var = PENDING_STMT (old_edge), phi = phi_nodes (new_edge->dest);
var && phi;
var = TREE_CHAIN (var), phi = PHI_CHAIN (phi))
{
tree result = TREE_PURPOSE (var);
tree arg = TREE_VALUE (var);
gcc_assert (result == PHI_RESULT (phi));
add_phi_arg (phi, arg, new_edge);
}
PENDING_STMT (old_edge) = NULL;
}
static basic_block
split_edge_bb_loc (edge edge_in)
{
basic_block dest = edge_in->dest;
if (dest->prev_bb && find_edge (dest->prev_bb, dest))
return edge_in->src;
else
return dest->prev_bb;
}
static basic_block
tree_split_edge (edge edge_in)
{
basic_block new_bb, after_bb, dest;
edge new_edge, e;
gcc_assert (!(edge_in->flags & EDGE_ABNORMAL));
dest = edge_in->dest;
after_bb = split_edge_bb_loc (edge_in);
new_bb = create_empty_bb (after_bb);
new_bb->frequency = EDGE_FREQUENCY (edge_in);
new_bb->count = edge_in->count;
new_edge = make_edge (new_bb, dest, EDGE_FALLTHRU);
new_edge->probability = REG_BR_PROB_BASE;
new_edge->count = edge_in->count;
e = redirect_edge_and_branch (edge_in, new_bb);
gcc_assert (e);
reinstall_phi_args (new_edge, e);
return new_bb;
}
static bool
has_label_p (basic_block bb, tree label)
{
block_stmt_iterator bsi;
for (bsi = bsi_start (bb); !bsi_end_p (bsi); bsi_next (&bsi))
{
tree stmt = bsi_stmt (bsi);
if (TREE_CODE (stmt) != LABEL_EXPR)
return false;
if (LABEL_EXPR_LABEL (stmt) == label)
return true;
}
return false;
}
static tree
verify_expr (tree *tp, int *walk_subtrees, void *data ATTRIBUTE_UNUSED)
{
tree t = *tp, x;
bool in_phi = (data != NULL);
if (TYPE_P (t))
*walk_subtrees = 0;
#define CHECK_OP(N, MSG) \
do { if (!is_gimple_val (TREE_OPERAND (t, N))) \
{ error (MSG); return TREE_OPERAND (t, N); }} while (0)
switch (TREE_CODE (t))
{
case SSA_NAME:
if (SSA_NAME_IN_FREE_LIST (t))
{
error ("SSA name in freelist but still referenced");
return *tp;
}
break;
case ASSERT_EXPR:
x = fold (ASSERT_EXPR_COND (t));
if (x == boolean_false_node)
{
error ("ASSERT_EXPR with an always-false condition");
return *tp;
}
break;
case MODIFY_EXPR:
x = TREE_OPERAND (t, 0);
if (TREE_CODE (x) == BIT_FIELD_REF
&& is_gimple_reg (TREE_OPERAND (x, 0)))
{
error ("GIMPLE register modified with BIT_FIELD_REF");
return t;
}
break;
case ADDR_EXPR:
{
bool old_invariant;
bool old_constant;
bool old_side_effects;
bool new_invariant;
bool new_constant;
bool new_side_effects;
if (in_phi)
break;
old_invariant = TREE_INVARIANT (t);
old_constant = TREE_CONSTANT (t);
old_side_effects = TREE_SIDE_EFFECTS (t);
recompute_tree_invariant_for_addr_expr (t);
new_invariant = TREE_INVARIANT (t);
new_side_effects = TREE_SIDE_EFFECTS (t);
new_constant = TREE_CONSTANT (t);
if (old_invariant != new_invariant)
{
error ("invariant not recomputed when ADDR_EXPR changed");
return t;
}
if (old_constant != new_constant)
{
error ("constant not recomputed when ADDR_EXPR changed");
return t;
}
if (old_side_effects != new_side_effects)
{
error ("side effects not recomputed when ADDR_EXPR changed");
return t;
}
for (x = TREE_OPERAND (t, 0);
handled_component_p (x);
x = TREE_OPERAND (x, 0))
;
if (TREE_CODE (x) != VAR_DECL && TREE_CODE (x) != PARM_DECL)
return NULL;
if (!TREE_ADDRESSABLE (x))
{
error ("address taken, but ADDRESSABLE bit not set");
return x;
}
break;
}
case COND_EXPR:
x = COND_EXPR_COND (t);
if (TREE_CODE (TREE_TYPE (x)) != BOOLEAN_TYPE)
{
error ("non-boolean used in condition");
return x;
}
if (!is_gimple_condexpr (x))
{
error ("invalid conditional operand");
return x;
}
break;
case NOP_EXPR:
case CONVERT_EXPR:
case FIX_TRUNC_EXPR:
case FIX_CEIL_EXPR:
case FIX_FLOOR_EXPR:
case FIX_ROUND_EXPR:
case FLOAT_EXPR:
case NEGATE_EXPR:
case ABS_EXPR:
case BIT_NOT_EXPR:
case NON_LVALUE_EXPR:
case TRUTH_NOT_EXPR:
CHECK_OP (0, "invalid operand to unary operator");
break;
case REALPART_EXPR:
case IMAGPART_EXPR:
case COMPONENT_REF:
case ARRAY_REF:
case ARRAY_RANGE_REF:
case BIT_FIELD_REF:
case VIEW_CONVERT_EXPR:
while (handled_component_p (t))
{
if (TREE_CODE (t) == COMPONENT_REF && TREE_OPERAND (t, 2))
CHECK_OP (2, "invalid COMPONENT_REF offset operator");
else if (TREE_CODE (t) == ARRAY_REF
|| TREE_CODE (t) == ARRAY_RANGE_REF)
{
CHECK_OP (1, "invalid array index");
if (TREE_OPERAND (t, 2))
CHECK_OP (2, "invalid array lower bound");
if (TREE_OPERAND (t, 3))
CHECK_OP (3, "invalid array stride");
}
else if (TREE_CODE (t) == BIT_FIELD_REF)
{
CHECK_OP (1, "invalid operand to BIT_FIELD_REF");
CHECK_OP (2, "invalid operand to BIT_FIELD_REF");
}
t = TREE_OPERAND (t, 0);
}
if (!CONSTANT_CLASS_P (t) && !is_gimple_lvalue (t))
{
error ("invalid reference prefix");
return t;
}
*walk_subtrees = 0;
break;
case LT_EXPR:
case LE_EXPR:
case GT_EXPR:
case GE_EXPR:
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:
case LTGT_EXPR:
case PLUS_EXPR:
case MINUS_EXPR:
case MULT_EXPR:
case TRUNC_DIV_EXPR:
case CEIL_DIV_EXPR:
case FLOOR_DIV_EXPR:
case ROUND_DIV_EXPR:
case TRUNC_MOD_EXPR:
case CEIL_MOD_EXPR:
case FLOOR_MOD_EXPR:
case ROUND_MOD_EXPR:
case RDIV_EXPR:
case EXACT_DIV_EXPR:
case MIN_EXPR:
case MAX_EXPR:
case LSHIFT_EXPR:
case RSHIFT_EXPR:
case LROTATE_EXPR:
case RROTATE_EXPR:
case BIT_IOR_EXPR:
case BIT_XOR_EXPR:
case BIT_AND_EXPR:
CHECK_OP (0, "invalid operand to binary operator");
CHECK_OP (1, "invalid operand to binary operator");
break;
case CONSTRUCTOR:
if (TREE_CONSTANT (t) && TREE_CODE (TREE_TYPE (t)) == VECTOR_TYPE)
*walk_subtrees = 0;
break;
default:
break;
}
return NULL;
#undef CHECK_OP
}
static bool
verify_stmt (tree stmt, bool last_in_block)
{
tree addr;
if (OMP_DIRECTIVE_P (stmt))
{
return false;
}
if (!is_gimple_stmt (stmt))
{
error ("is not a valid GIMPLE statement");
goto fail;
}
addr = walk_tree (&stmt, verify_expr, NULL, NULL);
if (addr)
{
debug_generic_stmt (addr);
return true;
}
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;
}
static bool
tree_node_can_be_shared (tree t)
{
if (IS_TYPE_OR_DECL_P (t)
|| is_gimple_min_invariant (t)
|| TREE_CODE (t) == SSA_NAME
|| t == error_mark_node
|| TREE_CODE (t) == IDENTIFIER_NODE)
return true;
if (TREE_CODE (t) == CASE_LABEL_EXPR)
return true;
while (((TREE_CODE (t) == ARRAY_REF || TREE_CODE (t) == ARRAY_RANGE_REF)
&& is_gimple_min_invariant (TREE_OPERAND (t, 1)))
|| TREE_CODE (t) == COMPONENT_REF
|| TREE_CODE (t) == REALPART_EXPR
|| TREE_CODE (t) == IMAGPART_EXPR)
t = TREE_OPERAND (t, 0);
if (DECL_P (t))
return true;
return false;
}
static tree
verify_node_sharing (tree * tp, int *walk_subtrees, void *data)
{
htab_t htab = (htab_t) data;
void **slot;
if (tree_node_can_be_shared (*tp))
{
*walk_subtrees = false;
return NULL;
}
slot = htab_find_slot (htab, *tp, INSERT);
if (*slot)
return (tree) *slot;
*slot = *tp;
return NULL;
}
void
verify_stmts (void)
{
basic_block bb;
block_stmt_iterator bsi;
bool err = false;
htab_t htab;
tree addr;
timevar_push (TV_TREE_STMT_VERIFY);
htab = htab_create (37, htab_hash_pointer, htab_eq_pointer, NULL);
FOR_EACH_BB (bb)
{
tree phi;
int i;
for (phi = phi_nodes (bb); phi; phi = PHI_CHAIN (phi))
{
int phi_num_args = PHI_NUM_ARGS (phi);
if (bb_for_stmt (phi) != bb)
{
error ("bb_for_stmt (phi) is set to a wrong basic block");
err |= true;
}
for (i = 0; i < phi_num_args; i++)
{
tree t = PHI_ARG_DEF (phi, i);
tree addr;
if (TREE_CODE (t) != SSA_NAME
&& TREE_CODE (t) != FUNCTION_DECL
&& !is_gimple_val (t))
{
error ("PHI def is not a GIMPLE value");
debug_generic_stmt (phi);
debug_generic_stmt (t);
err |= true;
}
addr = walk_tree (&t, verify_expr, (void *) 1, NULL);
if (addr)
{
debug_generic_stmt (addr);
err |= true;
}
addr = walk_tree (&t, verify_node_sharing, htab, NULL);
if (addr)
{
error ("incorrect sharing of tree nodes");
debug_generic_stmt (phi);
debug_generic_stmt (addr);
err |= true;
}
}
}
for (bsi = bsi_start (bb); !bsi_end_p (bsi); )
{
tree stmt = bsi_stmt (bsi);
if (bb_for_stmt (stmt) != bb)
{
error ("bb_for_stmt (stmt) is set to a wrong basic block");
err |= true;
}
bsi_next (&bsi);
err |= verify_stmt (stmt, bsi_end_p (bsi));
addr = walk_tree (&stmt, verify_node_sharing, htab, NULL);
if (addr)
{
error ("incorrect sharing of tree nodes");
debug_generic_stmt (stmt);
debug_generic_stmt (addr);
err |= true;
}
}
}
if (err)
internal_error ("verify_stmts failed");
htab_delete (htab);
timevar_pop (TV_TREE_STMT_VERIFY);
}
static int
tree_verify_flow_info (void)
{
int err = 0;
basic_block bb;
block_stmt_iterator bsi;
tree stmt;
edge e;
edge_iterator ei;
if (ENTRY_BLOCK_PTR->stmt_list)
{
error ("ENTRY_BLOCK has a statement list associated with it");
err = 1;
}
if (EXIT_BLOCK_PTR->stmt_list)
{
error ("EXIT_BLOCK has a statement list associated with it");
err = 1;
}
FOR_EACH_EDGE (e, ei, EXIT_BLOCK_PTR->preds)
if (e->flags & EDGE_FALLTHRU)
{
error ("fallthru to exit from bb %d", e->src->index);
err = 1;
}
FOR_EACH_BB (bb)
{
bool found_ctrl_stmt = false;
stmt = NULL_TREE;
for (bsi = bsi_start (bb); !bsi_end_p (bsi); bsi_next (&bsi))
{
tree prev_stmt = stmt;
stmt = bsi_stmt (bsi);
if (TREE_CODE (stmt) != LABEL_EXPR)
break;
if (prev_stmt && DECL_NONLOCAL (LABEL_EXPR_LABEL (stmt)))
{
error ("nonlocal label ");
print_generic_expr (stderr, LABEL_EXPR_LABEL (stmt), 0);
fprintf (stderr, " is not first in a sequence of labels in bb %d",
bb->index);
err = 1;
}
if (label_to_block (LABEL_EXPR_LABEL (stmt)) != bb)
{
error ("label ");
print_generic_expr (stderr, LABEL_EXPR_LABEL (stmt), 0);
fprintf (stderr, " to block does not match in bb %d",
bb->index);
err = 1;
}
if (decl_function_context (LABEL_EXPR_LABEL (stmt))
!= current_function_decl)
{
error ("label ");
print_generic_expr (stderr, LABEL_EXPR_LABEL (stmt), 0);
fprintf (stderr, " has incorrect context in bb %d",
bb->index);
err = 1;
}
}
for (; !bsi_end_p (bsi); bsi_next (&bsi))
{
tree stmt = bsi_stmt (bsi);
if (found_ctrl_stmt)
{
error ("control flow in the middle of basic block %d",
bb->index);
err = 1;
}
if (stmt_ends_bb_p (stmt))
found_ctrl_stmt = true;
if (TREE_CODE (stmt) == LABEL_EXPR)
{
error ("label ");
print_generic_expr (stderr, LABEL_EXPR_LABEL (stmt), 0);
fprintf (stderr, " in the middle of basic block %d", bb->index);
err = 1;
}
}
bsi = bsi_last (bb);
if (bsi_end_p (bsi))
continue;
stmt = bsi_stmt (bsi);
err |= verify_eh_edges (stmt);
if (is_ctrl_stmt (stmt))
{
FOR_EACH_EDGE (e, ei, bb->succs)
if (e->flags & EDGE_FALLTHRU)
{
error ("fallthru edge after a control statement in bb %d",
bb->index);
err = 1;
}
}
if (TREE_CODE (stmt) != COND_EXPR)
{
FOR_EACH_EDGE (e, ei, bb->succs)
if (e->flags & (EDGE_TRUE_VALUE | EDGE_FALSE_VALUE))
{
error ("true/false edge after a non-COND_EXPR in bb %d",
bb->index);
err = 1;
}
}
switch (TREE_CODE (stmt))
{
case COND_EXPR:
{
edge true_edge;
edge false_edge;
if (TREE_CODE (COND_EXPR_THEN (stmt)) != GOTO_EXPR
|| TREE_CODE (COND_EXPR_ELSE (stmt)) != GOTO_EXPR)
{
error ("structured COND_EXPR at the end of bb %d", bb->index);
err = 1;
}
extract_true_false_edges_from_block (bb, &true_edge, &false_edge);
if (!true_edge || !false_edge
|| !(true_edge->flags & EDGE_TRUE_VALUE)
|| !(false_edge->flags & EDGE_FALSE_VALUE)
|| (true_edge->flags & (EDGE_FALLTHRU | EDGE_ABNORMAL))
|| (false_edge->flags & (EDGE_FALLTHRU | EDGE_ABNORMAL))
|| EDGE_COUNT (bb->succs) >= 3)
{
error ("wrong outgoing edge flags at end of bb %d",
bb->index);
err = 1;
}
if (!has_label_p (true_edge->dest,
GOTO_DESTINATION (COND_EXPR_THEN (stmt))))
{
error ("%<then%> label does not match edge at end of bb %d",
bb->index);
err = 1;
}
if (!has_label_p (false_edge->dest,
GOTO_DESTINATION (COND_EXPR_ELSE (stmt))))
{
error ("%<else%> label does not match edge at end of bb %d",
bb->index);
err = 1;
}
}
break;
case GOTO_EXPR:
if (simple_goto_p (stmt))
{
error ("explicit goto at end of bb %d", bb->index);
err = 1;
}
else
{
FOR_EACH_EDGE (e, ei, bb->succs)
if ((e->flags & (EDGE_FALLTHRU | EDGE_TRUE_VALUE
| EDGE_FALSE_VALUE))
|| !(e->flags & EDGE_ABNORMAL))
{
error ("wrong outgoing edge flags at end of bb %d",
bb->index);
err = 1;
}
}
break;
case RETURN_EXPR:
if (!single_succ_p (bb)
|| (single_succ_edge (bb)->flags
& (EDGE_FALLTHRU | EDGE_ABNORMAL
| EDGE_TRUE_VALUE | EDGE_FALSE_VALUE)))
{
error ("wrong outgoing edge flags at end of bb %d", bb->index);
err = 1;
}
if (single_succ (bb) != EXIT_BLOCK_PTR)
{
error ("return edge does not point to exit in bb %d",
bb->index);
err = 1;
}
break;
case SWITCH_EXPR:
{
tree prev;
edge e;
size_t i, n;
tree vec;
vec = SWITCH_LABELS (stmt);
n = TREE_VEC_LENGTH (vec);
for (i = 0; i < n; ++i)
{
tree lab = CASE_LABEL (TREE_VEC_ELT (vec, i));
basic_block label_bb = label_to_block (lab);
gcc_assert (!label_bb->aux || label_bb->aux == (void *)1);
label_bb->aux = (void *)1;
}
prev = TREE_VEC_ELT (vec, 0);
for (i = 1; i < n - 1; ++i)
{
tree c = TREE_VEC_ELT (vec, i);
if (! CASE_LOW (c))
{
error ("found default case not at end of case vector");
err = 1;
continue;
}
if (! tree_int_cst_lt (CASE_LOW (prev), CASE_LOW (c)))
{
error ("case labels not sorted: ");
print_generic_expr (stderr, prev, 0);
fprintf (stderr," is greater than ");
print_generic_expr (stderr, c, 0);
fprintf (stderr," but comes before it.\n");
err = 1;
}
prev = c;
}
if (CASE_LOW (TREE_VEC_ELT (vec, n - 1)))
{
error ("no default case found at end of case vector");
err = 1;
}
FOR_EACH_EDGE (e, ei, bb->succs)
{
if (!e->dest->aux)
{
error ("extra outgoing edge %d->%d",
bb->index, e->dest->index);
err = 1;
}
e->dest->aux = (void *)2;
if ((e->flags & (EDGE_FALLTHRU | EDGE_ABNORMAL
| EDGE_TRUE_VALUE | EDGE_FALSE_VALUE)))
{
error ("wrong outgoing edge flags at end of bb %d",
bb->index);
err = 1;
}
}
for (i = 0; i < n; ++i)
{
tree lab = CASE_LABEL (TREE_VEC_ELT (vec, i));
basic_block label_bb = label_to_block (lab);
if (label_bb->aux != (void *)2)
{
error ("missing edge %i->%i",
bb->index, label_bb->index);
err = 1;
}
}
FOR_EACH_EDGE (e, ei, bb->succs)
e->dest->aux = (void *)0;
}
default: ;
}
}
if (dom_computed[CDI_DOMINATORS] >= DOM_NO_FAST_QUERY)
verify_dominators (CDI_DOMINATORS);
return err;
}
static void
tree_make_forwarder_block (edge fallthru)
{
edge e;
edge_iterator ei;
basic_block dummy, bb;
tree phi, new_phi, var;
dummy = fallthru->src;
bb = fallthru->dest;
if (single_pred_p (bb))
return;
for (phi = phi_nodes (dummy); phi; phi = PHI_CHAIN (phi))
{
var = PHI_RESULT (phi);
new_phi = create_phi_node (var, bb);
SSA_NAME_DEF_STMT (var) = new_phi;
SET_PHI_RESULT (phi, make_ssa_name (SSA_NAME_VAR (var), phi));
add_phi_arg (new_phi, PHI_RESULT (phi), fallthru);
}
set_phi_nodes (bb, phi_reverse (phi_nodes (bb)));
FOR_EACH_EDGE (e, ei, bb->preds)
{
if (e == fallthru)
continue;
flush_pending_stmts (e);
}
}
tree
tree_block_label (basic_block bb)
{
block_stmt_iterator i, s = bsi_start (bb);
bool first = true;
tree label, stmt;
for (i = s; !bsi_end_p (i); first = false, bsi_next (&i))
{
stmt = bsi_stmt (i);
if (TREE_CODE (stmt) != LABEL_EXPR)
break;
label = LABEL_EXPR_LABEL (stmt);
if (!DECL_NONLOCAL (label))
{
if (!first)
bsi_move_before (&i, &s);
return label;
}
}
label = create_artificial_label ();
stmt = build1 (LABEL_EXPR, void_type_node, label);
bsi_insert_before (&s, stmt, BSI_NEW_STMT);
return label;
}
static edge
tree_try_redirect_by_replacing_jump (edge e, basic_block target)
{
basic_block src = e->src;
block_stmt_iterator b;
tree stmt;
if (EDGE_COUNT (src->succs) != 2
|| EDGE_SUCC (src, EDGE_SUCC (src, 0) == e)->dest != target)
return NULL;
b = bsi_last (src);
if (bsi_end_p (b))
return NULL;
stmt = bsi_stmt (b);
if (TREE_CODE (stmt) == COND_EXPR
|| TREE_CODE (stmt) == SWITCH_EXPR)
{
bsi_remove (&b, true);
e = ssa_redirect_edge (e, target);
e->flags = EDGE_FALLTHRU;
return e;
}
return NULL;
}
static edge
tree_redirect_edge_and_branch (edge e, basic_block dest)
{
basic_block bb = e->src;
block_stmt_iterator bsi;
edge ret;
tree label, stmt;
if (e->flags & EDGE_ABNORMAL)
return NULL;
if (e->src != ENTRY_BLOCK_PTR
&& (ret = tree_try_redirect_by_replacing_jump (e, dest)))
return ret;
if (e->dest == dest)
return NULL;
label = tree_block_label (dest);
bsi = bsi_last (bb);
stmt = bsi_end_p (bsi) ? NULL : bsi_stmt (bsi);
switch (stmt ? TREE_CODE (stmt) : ERROR_MARK)
{
case COND_EXPR:
stmt = (e->flags & EDGE_TRUE_VALUE
? COND_EXPR_THEN (stmt)
: COND_EXPR_ELSE (stmt));
GOTO_DESTINATION (stmt) = label;
break;
case GOTO_EXPR:
gcc_unreachable ();
case SWITCH_EXPR:
{
tree cases = get_cases_for_edge (e, stmt);
if (cases)
{
edge e2 = find_edge (e->src, dest);
tree last, first;
first = cases;
while (cases)
{
last = cases;
CASE_LABEL (cases) = label;
cases = TREE_CHAIN (cases);
}
if (e2)
{
tree cases2 = get_cases_for_edge (e2, stmt);
TREE_CHAIN (last) = TREE_CHAIN (cases2);
TREE_CHAIN (cases2) = first;
}
}
else
{
tree vec = SWITCH_LABELS (stmt);
size_t i, n = TREE_VEC_LENGTH (vec);
for (i = 0; i < n; i++)
{
tree elt = TREE_VEC_ELT (vec, i);
if (label_to_block (CASE_LABEL (elt)) == e->dest)
CASE_LABEL (elt) = label;
}
}
break;
}
case RETURN_EXPR:
bsi_remove (&bsi, true);
e->flags |= EDGE_FALLTHRU;
break;
default:
gcc_assert (e->flags & EDGE_FALLTHRU);
break;
}
e = ssa_redirect_edge (e, dest);
return e;
}
static basic_block
tree_redirect_edge_and_branch_force (edge e, basic_block dest)
{
e = tree_redirect_edge_and_branch (e, dest);
gcc_assert (e);
return NULL;
}
static basic_block
tree_split_block (basic_block bb, void *stmt)
{
block_stmt_iterator bsi;
tree_stmt_iterator tsi_tgt;
tree act;
basic_block new_bb;
edge e;
edge_iterator ei;
new_bb = create_empty_bb (bb);
new_bb->succs = bb->succs;
bb->succs = NULL;
FOR_EACH_EDGE (e, ei, new_bb->succs)
e->src = new_bb;
if (stmt && TREE_CODE ((tree) stmt) == LABEL_EXPR)
stmt = NULL;
for (bsi = bsi_start (bb); !bsi_end_p (bsi); bsi_next (&bsi))
{
act = bsi_stmt (bsi);
if (TREE_CODE (act) == LABEL_EXPR)
continue;
if (!stmt)
break;
if (stmt == act)
{
bsi_next (&bsi);
break;
}
}
if (bsi_end_p (bsi))
return new_bb;
new_bb->stmt_list = tsi_split_statement_list_before (&bsi.tsi);
for (tsi_tgt = tsi_start (new_bb->stmt_list);
!tsi_end_p (tsi_tgt); tsi_next (&tsi_tgt))
change_bb_for_stmt (tsi_stmt (tsi_tgt), new_bb);
return new_bb;
}
static bool
tree_move_block_after (basic_block bb, basic_block after)
{
if (bb->prev_bb == after)
return true;
unlink_block (bb);
link_block (bb, after);
return true;
}
static bool
tree_can_duplicate_bb_p (basic_block bb ATTRIBUTE_UNUSED)
{
return true;
}
static basic_block
tree_duplicate_bb (basic_block bb)
{
basic_block new_bb;
block_stmt_iterator bsi, bsi_tgt;
tree phi;
new_bb = create_empty_bb (EXIT_BLOCK_PTR->prev_bb);
for (phi = phi_nodes (bb); phi; phi = PHI_CHAIN (phi))
{
tree copy = create_phi_node (PHI_RESULT (phi), new_bb);
create_new_def_for (PHI_RESULT (copy), copy, PHI_RESULT_PTR (copy));
}
set_phi_nodes (new_bb, phi_reverse (phi_nodes (new_bb)));
bsi_tgt = bsi_start (new_bb);
for (bsi = bsi_start (bb); !bsi_end_p (bsi); bsi_next (&bsi))
{
def_operand_p def_p;
ssa_op_iter op_iter;
tree stmt, copy;
int region;
stmt = bsi_stmt (bsi);
if (TREE_CODE (stmt) == LABEL_EXPR)
continue;
copy = unshare_expr (stmt);
bsi_insert_after (&bsi_tgt, copy, BSI_NEW_STMT);
copy_virtual_operands (copy, stmt);
region = lookup_stmt_eh_region (stmt);
if (region >= 0)
add_stmt_to_eh_region (copy, region);
FOR_EACH_SSA_DEF_OPERAND (def_p, copy, op_iter, SSA_OP_ALL_DEFS)
create_new_def_for (DEF_FROM_PTR (def_p), copy, def_p);
}
return new_bb;
}
void
add_phi_args_after_copy_bb (basic_block bb_copy)
{
basic_block bb, dest;
edge e, e_copy;
edge_iterator ei;
tree phi, phi_copy, phi_next, def;
bb = get_bb_original (bb_copy);
FOR_EACH_EDGE (e_copy, ei, bb_copy->succs)
{
if (!phi_nodes (e_copy->dest))
continue;
if (e_copy->dest->flags & BB_DUPLICATED)
dest = get_bb_original (e_copy->dest);
else
dest = e_copy->dest;
e = find_edge (bb, dest);
if (!e)
{
FOR_EACH_EDGE (e, ei, bb->succs)
if ((e->dest->flags & BB_DUPLICATED)
&& get_bb_original (e->dest) == dest)
break;
gcc_assert (e != NULL);
}
for (phi = phi_nodes (e->dest), phi_copy = phi_nodes (e_copy->dest);
phi;
phi = phi_next, phi_copy = PHI_CHAIN (phi_copy))
{
phi_next = PHI_CHAIN (phi);
def = PHI_ARG_DEF_FROM_EDGE (phi, e);
add_phi_arg (phi_copy, def, e_copy);
}
}
}
void
add_phi_args_after_copy (basic_block *region_copy, unsigned n_region)
{
unsigned i;
for (i = 0; i < n_region; i++)
region_copy[i]->flags |= BB_DUPLICATED;
for (i = 0; i < n_region; i++)
add_phi_args_after_copy_bb (region_copy[i]);
for (i = 0; i < n_region; i++)
region_copy[i]->flags &= ~BB_DUPLICATED;
}
bool
tree_duplicate_sese_region (edge entry, edge exit,
basic_block *region, unsigned n_region,
basic_block *region_copy)
{
unsigned i, n_doms;
bool free_region_copy = false, copying_header = false;
struct loop *loop = entry->dest->loop_father;
edge exit_copy;
basic_block *doms;
edge redirected;
int total_freq = 0, entry_freq = 0;
gcov_type total_count = 0, entry_count = 0;
if (!can_copy_bbs_p (region, n_region))
return false;
for (i = 0; i < n_region; i++)
{
if (region[i]->loop_father != loop)
return false;
if (region[i] != entry->dest
&& region[i] == loop->header)
return false;
}
loop->copy = loop;
if (loop->header == entry->dest)
{
copying_header = true;
loop->copy = loop->outer;
if (!dominated_by_p (CDI_DOMINATORS, loop->latch, exit->src))
return false;
for (i = 0; i < n_region; i++)
if (region[i] != exit->src
&& dominated_by_p (CDI_DOMINATORS, region[i], exit->src))
return false;
}
if (!region_copy)
{
region_copy = XNEWVEC (basic_block, n_region);
free_region_copy = true;
}
gcc_assert (!need_ssa_update_p ());
doms = XNEWVEC (basic_block, n_basic_blocks);
initialize_original_copy_tables ();
n_doms = get_dominated_by_region (CDI_DOMINATORS, region, n_region, doms);
if (entry->dest->count)
{
total_count = entry->dest->count;
entry_count = entry->count;
if (entry_count > total_count)
entry_count = total_count;
}
else
{
total_freq = entry->dest->frequency;
entry_freq = EDGE_FREQUENCY (entry);
if (total_freq == 0)
total_freq = 1;
else if (entry_freq > total_freq)
entry_freq = total_freq;
}
copy_bbs (region, n_region, region_copy, &exit, 1, &exit_copy, loop,
split_edge_bb_loc (entry));
if (total_count)
{
scale_bbs_frequencies_gcov_type (region, n_region,
total_count - entry_count,
total_count);
scale_bbs_frequencies_gcov_type (region_copy, n_region, entry_count,
total_count);
}
else
{
scale_bbs_frequencies_int (region, n_region, total_freq - entry_freq,
total_freq);
scale_bbs_frequencies_int (region_copy, n_region, entry_freq, total_freq);
}
if (copying_header)
{
loop->header = exit->dest;
loop->latch = exit->src;
}
redirected = redirect_edge_and_branch (entry, get_bb_copy (entry->dest));
gcc_assert (redirected != NULL);
flush_pending_stmts (entry);
set_immediate_dominator (CDI_DOMINATORS, entry->dest, entry->src);
doms[n_doms++] = get_bb_original (entry->dest);
iterate_fix_dominators (CDI_DOMINATORS, doms, n_doms);
free (doms);
add_phi_args_after_copy (region_copy, n_region);
update_ssa (TODO_update_ssa);
if (free_region_copy)
free (region_copy);
free_original_copy_tables ();
return true;
}
static void
gather_blocks_in_sese_region (basic_block entry, basic_block exit,
VEC(basic_block,heap) **bbs_p)
{
basic_block son;
for (son = first_dom_son (CDI_DOMINATORS, entry);
son;
son = next_dom_son (CDI_DOMINATORS, son))
{
VEC_safe_push (basic_block, heap, *bbs_p, son);
if (son != exit)
gather_blocks_in_sese_region (son, exit, bbs_p);
}
}
struct move_stmt_d
{
tree block;
tree from_context;
tree to_context;
bitmap vars_to_remove;
htab_t new_label_map;
bool remap_decls_p;
};
static tree
move_stmt_r (tree *tp, int *walk_subtrees, void *data)
{
struct move_stmt_d *p = (struct move_stmt_d *) data;
tree t = *tp;
if (p->block && IS_EXPR_CODE_CLASS (TREE_CODE_CLASS (TREE_CODE (t))))
TREE_BLOCK (t) = p->block;
if (OMP_DIRECTIVE_P (t)
&& TREE_CODE (t) != OMP_RETURN
&& TREE_CODE (t) != OMP_CONTINUE)
{
bool save_remap_decls_p = p->remap_decls_p;
p->remap_decls_p = false;
*walk_subtrees = 0;
walk_tree (&OMP_BODY (t), move_stmt_r, p, NULL);
p->remap_decls_p = save_remap_decls_p;
}
else if (DECL_P (t) && DECL_CONTEXT (t) == p->from_context)
{
if (TREE_CODE (t) == LABEL_DECL)
{
if (p->new_label_map)
{
struct tree_map in, *out;
in.from = t;
out = htab_find_with_hash (p->new_label_map, &in, DECL_UID (t));
if (out)
*tp = t = out->to;
}
DECL_CONTEXT (t) = p->to_context;
}
else if (p->remap_decls_p)
{
DECL_CONTEXT (t) = p->to_context;
if (TREE_CODE (t) == VAR_DECL)
{
struct function *f = DECL_STRUCT_FUNCTION (p->to_context);
f->unexpanded_var_list
= tree_cons (0, t, f->unexpanded_var_list);
bitmap_set_bit (p->vars_to_remove, DECL_UID (t));
}
}
}
else if (TYPE_P (t))
*walk_subtrees = 0;
return NULL_TREE;
}
static void
move_block_to_fn (struct function *dest_cfun, basic_block bb,
basic_block after, bool update_edge_count_p,
bitmap vars_to_remove, htab_t new_label_map, int eh_offset)
{
struct control_flow_graph *cfg;
edge_iterator ei;
edge e;
block_stmt_iterator si;
struct move_stmt_d d;
unsigned old_len, new_len;
basic_block *addr;
move_block_after (bb, after);
if (update_edge_count_p)
FOR_EACH_EDGE (e, ei, bb->succs)
{
cfun->cfg->x_n_edges--;
dest_cfun->cfg->x_n_edges++;
}
VEC_replace (basic_block, cfun->cfg->x_basic_block_info, bb->index, NULL);
cfun->cfg->x_n_basic_blocks--;
cfg = dest_cfun->cfg;
cfg->x_n_basic_blocks++;
if (bb->index > cfg->x_last_basic_block)
cfg->x_last_basic_block = bb->index;
old_len = VEC_length (basic_block, cfg->x_basic_block_info);
if ((unsigned) cfg->x_last_basic_block >= old_len)
{
new_len = cfg->x_last_basic_block + (cfg->x_last_basic_block + 3) / 4;
VEC_safe_grow (basic_block, gc, cfg->x_basic_block_info, new_len);
addr = VEC_address (basic_block, cfg->x_basic_block_info);
memset (&addr[old_len], 0, sizeof (basic_block) * (new_len - old_len));
}
VEC_replace (basic_block, cfg->x_basic_block_info,
cfg->x_last_basic_block, bb);
memset (&d, 0, sizeof (d));
d.vars_to_remove = vars_to_remove;
for (si = bsi_start (bb); !bsi_end_p (si); bsi_next (&si))
{
tree stmt = bsi_stmt (si);
int region;
d.from_context = cfun->decl;
d.to_context = dest_cfun->decl;
d.remap_decls_p = true;
d.new_label_map = new_label_map;
if (TREE_BLOCK (stmt))
d.block = DECL_INITIAL (dest_cfun->decl);
walk_tree (&stmt, move_stmt_r, &d, NULL);
if (TREE_CODE (stmt) == LABEL_EXPR)
{
tree label = LABEL_EXPR_LABEL (stmt);
int uid = LABEL_DECL_UID (label);
gcc_assert (uid > -1);
old_len = VEC_length (basic_block, cfg->x_label_to_block_map);
if (old_len <= (unsigned) uid)
{
new_len = 3 * uid / 2;
VEC_safe_grow (basic_block, gc, cfg->x_label_to_block_map,
new_len);
addr = VEC_address (basic_block, cfg->x_label_to_block_map);
memset (&addr[old_len], 0,
sizeof (basic_block) * (new_len - old_len));
}
VEC_replace (basic_block, cfg->x_label_to_block_map, uid, bb);
VEC_replace (basic_block, cfun->cfg->x_label_to_block_map, uid, NULL);
gcc_assert (DECL_CONTEXT (label) == dest_cfun->decl);
if (uid >= dest_cfun->last_label_uid)
dest_cfun->last_label_uid = uid + 1;
}
else if (TREE_CODE (stmt) == RESX_EXPR && eh_offset != 0)
TREE_OPERAND (stmt, 0) =
build_int_cst (NULL_TREE,
TREE_INT_CST_LOW (TREE_OPERAND (stmt, 0))
+ eh_offset);
region = lookup_stmt_eh_region (stmt);
if (region >= 0)
{
add_stmt_to_eh_region_fn (dest_cfun, stmt, region + eh_offset);
remove_stmt_from_eh_region (stmt);
}
}
}
static int
find_outermost_region_in_block (struct function *src_cfun,
basic_block bb, int region)
{
block_stmt_iterator si;
for (si = bsi_start (bb); !bsi_end_p (si); bsi_next (&si))
{
tree stmt = bsi_stmt (si);
int stmt_region;
if (TREE_CODE (stmt) == RESX_EXPR)
stmt_region = TREE_INT_CST_LOW (TREE_OPERAND (stmt, 0));
else
stmt_region = lookup_stmt_eh_region_fn (src_cfun, stmt);
if (stmt_region > 0)
{
if (region < 0)
region = stmt_region;
else if (stmt_region != region)
{
region = eh_region_outermost (src_cfun, stmt_region, region);
gcc_assert (region != -1);
}
}
}
return region;
}
static tree
new_label_mapper (tree decl, void *data)
{
htab_t hash = (htab_t) data;
struct tree_map *m;
void **slot;
gcc_assert (TREE_CODE (decl) == LABEL_DECL);
m = xmalloc (sizeof (struct tree_map));
m->hash = DECL_UID (decl);
m->from = decl;
m->to = create_artificial_label ();
LABEL_DECL_UID (m->to) = LABEL_DECL_UID (decl);
slot = htab_find_slot_with_hash (hash, m, m->hash, INSERT);
gcc_assert (*slot == NULL);
*slot = m;
return m->to;
}
basic_block
move_sese_region_to_fn (struct function *dest_cfun, basic_block entry_bb,
basic_block exit_bb)
{
VEC(basic_block,heap) *bbs;
basic_block after, bb, *entry_pred, *exit_succ;
struct function *saved_cfun;
int *entry_flag, *exit_flag, eh_offset;
unsigned i, num_entry_edges, num_exit_edges;
edge e;
edge_iterator ei;
bitmap vars_to_remove;
htab_t new_label_map;
saved_cfun = cfun;
calculate_dominance_info (CDI_DOMINATORS);
gcc_assert (entry_bb != exit_bb
&& (!exit_bb
|| dominated_by_p (CDI_DOMINATORS, exit_bb, entry_bb)));
bbs = NULL;
VEC_safe_push (basic_block, heap, bbs, entry_bb);
gather_blocks_in_sese_region (entry_bb, exit_bb, &bbs);
num_entry_edges = EDGE_COUNT (entry_bb->preds);
entry_pred = (basic_block *) xcalloc (num_entry_edges, sizeof (basic_block));
entry_flag = (int *) xcalloc (num_entry_edges, sizeof (int));
i = 0;
for (ei = ei_start (entry_bb->preds); (e = ei_safe_edge (ei)) != NULL;)
{
entry_flag[i] = e->flags;
entry_pred[i++] = e->src;
remove_edge (e);
}
if (exit_bb)
{
num_exit_edges = EDGE_COUNT (exit_bb->succs);
exit_succ = (basic_block *) xcalloc (num_exit_edges,
sizeof (basic_block));
exit_flag = (int *) xcalloc (num_exit_edges, sizeof (int));
i = 0;
for (ei = ei_start (exit_bb->succs); (e = ei_safe_edge (ei)) != NULL;)
{
exit_flag[i] = e->flags;
exit_succ[i++] = e->dest;
remove_edge (e);
}
}
else
{
num_exit_edges = 0;
exit_succ = NULL;
exit_flag = NULL;
}
gcc_assert (dest_cfun->cfg == NULL);
cfun = dest_cfun;
init_empty_tree_cfg ();
eh_offset = 0;
new_label_map = NULL;
if (saved_cfun->eh)
{
int region = -1;
for (i = 0; VEC_iterate (basic_block, bbs, i, bb); i++)
region = find_outermost_region_in_block (saved_cfun, bb, region);
init_eh_for_function ();
if (region != -1)
{
new_label_map = htab_create (17, tree_map_hash, tree_map_eq, free);
eh_offset = duplicate_eh_regions (saved_cfun, new_label_mapper,
new_label_map, region, 0);
}
}
cfun = saved_cfun;
gcc_assert (VEC_length (basic_block, bbs) >= 2);
after = dest_cfun->cfg->x_entry_block_ptr;
vars_to_remove = BITMAP_ALLOC (NULL);
for (i = 0; VEC_iterate (basic_block, bbs, i, bb); i++)
{
move_block_to_fn (dest_cfun, bb, after, bb != exit_bb, vars_to_remove,
new_label_map, eh_offset);
after = bb;
}
if (new_label_map)
htab_delete (new_label_map);
if (!bitmap_empty_p (vars_to_remove))
{
tree *p;
for (p = &cfun->unexpanded_var_list; *p; )
{
tree var = TREE_VALUE (*p);
if (bitmap_bit_p (vars_to_remove, DECL_UID (var)))
{
*p = TREE_CHAIN (*p);
continue;
}
p = &TREE_CHAIN (*p);
}
}
BITMAP_FREE (vars_to_remove);
cfun = dest_cfun;
make_edge (ENTRY_BLOCK_PTR, entry_bb, EDGE_FALLTHRU);
if (exit_bb)
make_edge (exit_bb, EXIT_BLOCK_PTR, 0);
cfun = saved_cfun;
bb = create_empty_bb (entry_pred[0]);
for (i = 0; i < num_entry_edges; i++)
make_edge (entry_pred[i], bb, entry_flag[i]);
for (i = 0; i < num_exit_edges; i++)
make_edge (bb, exit_succ[i], exit_flag[i]);
if (exit_bb)
{
free (exit_flag);
free (exit_succ);
}
free (entry_flag);
free (entry_pred);
free_dominance_info (CDI_DOMINATORS);
free_dominance_info (CDI_POST_DOMINATORS);
VEC_free (basic_block, heap, bbs);
return bb;
}
void
dump_function_to_file (tree fn, FILE *file, int flags)
{
tree arg, vars, var;
bool ignore_topmost_bind = false, any_var = false;
basic_block bb;
tree chain;
struct function *saved_cfun;
fprintf (file, "%s (", lang_hooks.decl_printable_name (fn, 2));
arg = DECL_ARGUMENTS (fn);
while (arg)
{
print_generic_expr (file, arg, dump_flags);
if (TREE_CHAIN (arg))
fprintf (file, ", ");
arg = TREE_CHAIN (arg);
}
fprintf (file, ")\n");
if (flags & TDF_DETAILS)
dump_eh_tree (file, DECL_STRUCT_FUNCTION (fn));
if (flags & TDF_RAW)
{
dump_node (fn, TDF_SLIM | flags, file);
return;
}
saved_cfun = cfun;
cfun = DECL_STRUCT_FUNCTION (fn);
if (cfun && cfun->decl == fn && cfun->unexpanded_var_list)
{
ignore_topmost_bind = true;
fprintf (file, "{\n");
for (vars = cfun->unexpanded_var_list; vars; vars = TREE_CHAIN (vars))
{
var = TREE_VALUE (vars);
print_generic_decl (file, var, flags);
fprintf (file, "\n");
any_var = true;
}
}
if (cfun && cfun->decl == fn && cfun->cfg && basic_block_info)
{
check_bb_profile (ENTRY_BLOCK_PTR, file);
if (!ignore_topmost_bind)
fprintf (file, "{\n");
if (any_var && n_basic_blocks)
fprintf (file, "\n");
FOR_EACH_BB (bb)
dump_generic_bb (file, bb, 2, flags);
fprintf (file, "}\n");
check_bb_profile (EXIT_BLOCK_PTR, file);
}
else
{
int indent;
chain = DECL_SAVED_TREE (fn);
if (chain && TREE_CODE (chain) == BIND_EXPR)
{
if (ignore_topmost_bind)
{
chain = BIND_EXPR_BODY (chain);
indent = 2;
}
else
indent = 0;
}
else
{
if (!ignore_topmost_bind)
fprintf (file, "{\n");
indent = 2;
}
if (any_var)
fprintf (file, "\n");
print_generic_stmt_indented (file, chain, flags, indent);
if (ignore_topmost_bind)
fprintf (file, "}\n");
}
fprintf (file, "\n\n");
cfun = saved_cfun;
}
void
debug_function (tree fn, int flags)
{
dump_function_to_file (fn, stderr, flags);
}
static void print_loop (FILE *, struct loop *, int);
static void print_pred_bbs (FILE *, basic_block bb);
static void print_succ_bbs (FILE *, basic_block bb);
static void
print_pred_bbs (FILE *file, basic_block bb)
{
edge e;
edge_iterator ei;
FOR_EACH_EDGE (e, ei, bb->preds)
fprintf (file, "bb_%d ", e->src->index);
}
static void
print_succ_bbs (FILE *file, basic_block bb)
{
edge e;
edge_iterator ei;
FOR_EACH_EDGE (e, ei, bb->succs)
fprintf (file, "bb_%d ", e->dest->index);
}
static void
print_loop (FILE *file, struct loop *loop, int indent)
{
char *s_indent;
basic_block bb;
if (loop == NULL)
return;
s_indent = (char *) alloca ((size_t) indent + 1);
memset ((void *) s_indent, ' ', (size_t) indent);
s_indent[indent] = '\0';
fprintf (file, "%sloop_%d\n", s_indent, loop->num);
fprintf (file, "%s{\n", s_indent);
FOR_EACH_BB (bb)
if (bb->loop_father == loop)
{
fprintf (file, "%s bb_%d (preds = {", s_indent, bb->index);
print_pred_bbs (file, bb);
fprintf (file, "}, succs = {");
print_succ_bbs (file, bb);
fprintf (file, "})\n");
fprintf (file, "%s {\n", s_indent);
tree_dump_bb (bb, file, indent + 4);
fprintf (file, "%s }\n", s_indent);
}
print_loop (file, loop->inner, indent + 2);
fprintf (file, "%s}\n", s_indent);
print_loop (file, loop->next, indent);
}
void
print_loop_ir (FILE *file)
{
basic_block bb;
bb = BASIC_BLOCK (NUM_FIXED_BLOCKS);
if (bb && bb->loop_father)
print_loop (file, bb->loop_father, 0);
}
void
debug_loop_ir (void)
{
print_loop_ir (stderr);
}
static bool
tree_block_ends_with_call_p (basic_block bb)
{
block_stmt_iterator bsi = bsi_last (bb);
if (bsi_end_p (bsi))
return false;
return get_call_expr_in (bsi_stmt (bsi)) != NULL;
}
static bool
tree_block_ends_with_condjump_p (basic_block bb)
{
tree stmt = last_stmt (bb);
return (stmt && TREE_CODE (stmt) == COND_EXPR);
}
static bool
need_fake_edge_p (tree t)
{
tree call;
call = get_call_expr_in (t);
if (call
&& !(call_expr_flags (call) & ECF_NORETURN))
return true;
if (TREE_CODE (t) == ASM_EXPR
&& (ASM_VOLATILE_P (t) || ASM_INPUT_P (t)))
return true;
return false;
}
static int
tree_flow_call_edges_add (sbitmap blocks)
{
int i;
int blocks_split = 0;
int last_bb = last_basic_block;
bool check_last_block = false;
if (n_basic_blocks == NUM_FIXED_BLOCKS)
return 0;
if (! blocks)
check_last_block = true;
else
check_last_block = TEST_BIT (blocks, EXIT_BLOCK_PTR->prev_bb->index);
if (check_last_block)
{
basic_block bb = EXIT_BLOCK_PTR->prev_bb;
block_stmt_iterator bsi = bsi_last (bb);
tree t = NULL_TREE;
if (!bsi_end_p (bsi))
t = bsi_stmt (bsi);
if (t && need_fake_edge_p (t))
{
edge e;
e = find_edge (bb, EXIT_BLOCK_PTR);
if (e)
{
bsi_insert_on_edge (e, build_empty_stmt ());
bsi_commit_edge_inserts ();
}
}
}
for (i = 0; i < last_bb; i++)
{
basic_block bb = BASIC_BLOCK (i);
block_stmt_iterator bsi;
tree stmt, last_stmt;
if (!bb)
continue;
if (blocks && !TEST_BIT (blocks, i))
continue;
bsi = bsi_last (bb);
if (!bsi_end_p (bsi))
{
last_stmt = bsi_stmt (bsi);
do
{
stmt = bsi_stmt (bsi);
if (need_fake_edge_p (stmt))
{
edge e;
#ifdef ENABLE_CHECKING
if (stmt == last_stmt)
{
e = find_edge (bb, EXIT_BLOCK_PTR);
gcc_assert (e == NULL);
}
#endif
if (stmt != last_stmt)
{
e = split_block (bb, stmt);
if (e)
blocks_split++;
}
make_edge (bb, EXIT_BLOCK_PTR, EDGE_FAKE);
}
bsi_prev (&bsi);
}
while (!bsi_end_p (bsi));
}
}
if (blocks_split)
verify_flow_info ();
return blocks_split;
}
bool
tree_purge_dead_abnormal_call_edges (basic_block bb)
{
bool changed = tree_purge_dead_eh_edges (bb);
if (current_function_has_nonlocal_label)
{
tree stmt = last_stmt (bb);
edge_iterator ei;
edge e;
if (!(stmt && tree_can_make_abnormal_goto (stmt)))
for (ei = ei_start (bb->succs); (e = ei_safe_edge (ei)); )
{
if (e->flags & EDGE_ABNORMAL)
{
remove_edge (e);
changed = true;
}
else
ei_next (&ei);
}
if (changed)
free_dominance_info (CDI_DOMINATORS);
}
return changed;
}
bool
tree_purge_dead_eh_edges (basic_block bb)
{
bool changed = false;
edge e;
edge_iterator ei;
tree stmt = last_stmt (bb);
if (stmt && tree_can_throw_internal (stmt))
return false;
for (ei = ei_start (bb->succs); (e = ei_safe_edge (ei)); )
{
if (e->flags & EDGE_EH)
{
remove_edge (e);
changed = true;
}
else
ei_next (&ei);
}
if (changed)
free_dominance_info (CDI_DOMINATORS);
return changed;
}
bool
tree_purge_all_dead_eh_edges (bitmap blocks)
{
bool changed = false;
unsigned i;
bitmap_iterator bi;
EXECUTE_IF_SET_IN_BITMAP (blocks, 0, i, bi)
{
changed |= tree_purge_dead_eh_edges (BASIC_BLOCK (i));
}
return changed;
}
static void
tree_execute_on_growing_pred (edge e)
{
basic_block bb = e->dest;
if (phi_nodes (bb))
reserve_phi_args_for_new_edge (bb);
}
static void
tree_execute_on_shrinking_pred (edge e)
{
if (phi_nodes (e->dest))
remove_phi_args (e);
}
static void
tree_lv_adjust_loop_header_phi (basic_block first, basic_block second,
basic_block new_head, edge e)
{
tree phi1, phi2;
edge e2 = find_edge (new_head, second);
gcc_assert (e2 != NULL);
for (phi2 = phi_nodes (second), phi1 = phi_nodes (first);
phi2 && phi1;
phi2 = PHI_CHAIN (phi2), phi1 = PHI_CHAIN (phi1))
{
tree def = PHI_ARG_DEF (phi2, e2->dest_idx);
add_phi_arg (phi1, def, e);
}
}
static void
tree_lv_add_condition_to_bb (basic_block first_head, basic_block second_head,
basic_block cond_bb, void *cond_e)
{
block_stmt_iterator bsi;
tree goto1 = NULL_TREE;
tree goto2 = NULL_TREE;
tree new_cond_expr = NULL_TREE;
tree cond_expr = (tree) cond_e;
edge e0;
goto1 = build1 (GOTO_EXPR, void_type_node, tree_block_label (first_head));
goto2 = build1 (GOTO_EXPR, void_type_node, tree_block_label (second_head));
new_cond_expr = build3 (COND_EXPR, void_type_node, cond_expr, goto1, goto2);
bsi = bsi_start (cond_bb);
bsi_insert_after (&bsi, new_cond_expr, BSI_NEW_STMT);
e0 = single_succ_edge (cond_bb);
e0->flags &= ~EDGE_FALLTHRU;
e0->flags |= EDGE_FALSE_VALUE;
}
struct cfg_hooks tree_cfg_hooks = {
"tree",
tree_verify_flow_info,
tree_dump_bb,
create_bb,
tree_redirect_edge_and_branch,
tree_redirect_edge_and_branch_force,
remove_bb,
tree_split_block,
tree_move_block_after,
tree_can_merge_blocks_p,
tree_merge_blocks,
tree_predict_edge,
tree_predicted_by_p,
tree_can_duplicate_bb_p,
tree_duplicate_bb,
tree_split_edge,
tree_make_forwarder_block,
NULL,
tree_block_ends_with_call_p,
tree_block_ends_with_condjump_p,
tree_flow_call_edges_add,
tree_execute_on_growing_pred,
tree_execute_on_shrinking_pred,
tree_duplicate_loop_to_header_edge,
tree_lv_add_condition_to_bb,
tree_lv_adjust_loop_header_phi,
extract_true_false_edges_from_block,
flush_pending_stmts
};
static unsigned int
split_critical_edges (void)
{
basic_block bb;
edge e;
edge_iterator ei;
start_recording_case_labels ();
FOR_ALL_BB (bb)
{
FOR_EACH_EDGE (e, ei, bb->succs)
if (EDGE_CRITICAL_P (e) && !(e->flags & EDGE_ABNORMAL))
{
split_edge (e);
}
}
end_recording_case_labels ();
return 0;
}
struct tree_opt_pass pass_split_crit_edges =
{
"crited",
NULL,
split_critical_edges,
NULL,
NULL,
0,
TV_TREE_SPLIT_EDGES,
PROP_cfg,
PROP_no_crit_edges,
0,
0,
TODO_dump_func,
0
};
tree
gimplify_val (block_stmt_iterator *bsi, tree type, tree exp)
{
tree t, new_stmt, orig_stmt;
if (is_gimple_val (exp))
return exp;
t = make_rename_temp (type, NULL);
new_stmt = build2 (MODIFY_EXPR, type, t, exp);
orig_stmt = bsi_stmt (*bsi);
SET_EXPR_LOCUS (new_stmt, EXPR_LOCUS (orig_stmt));
TREE_BLOCK (new_stmt) = TREE_BLOCK (orig_stmt);
bsi_insert_before (bsi, new_stmt, BSI_SAME_STMT);
if (in_ssa_p)
mark_new_vars_to_rename (new_stmt);
return t;
}
tree
gimplify_build3 (block_stmt_iterator *bsi, enum tree_code code,
tree type, tree a, tree b, tree c)
{
tree ret;
ret = fold_build3 (code, type, a, b, c);
STRIP_NOPS (ret);
return gimplify_val (bsi, type, ret);
}
tree
gimplify_build2 (block_stmt_iterator *bsi, enum tree_code code,
tree type, tree a, tree b)
{
tree ret;
ret = fold_build2 (code, type, a, b);
STRIP_NOPS (ret);
return gimplify_val (bsi, type, ret);
}
tree
gimplify_build1 (block_stmt_iterator *bsi, enum tree_code code, tree type,
tree a)
{
tree ret;
ret = fold_build1 (code, type, a);
STRIP_NOPS (ret);
return gimplify_val (bsi, type, ret);
}
static unsigned int
execute_warn_function_return (void)
{
#ifdef USE_MAPPED_LOCATION
source_location location;
#else
location_t *locus;
#endif
tree last;
edge e;
edge_iterator ei;
if (TREE_THIS_VOLATILE (cfun->decl)
&& EDGE_COUNT (EXIT_BLOCK_PTR->preds) > 0)
{
#ifdef USE_MAPPED_LOCATION
location = UNKNOWN_LOCATION;
#else
locus = NULL;
#endif
FOR_EACH_EDGE (e, ei, EXIT_BLOCK_PTR->preds)
{
last = last_stmt (e->src);
if (TREE_CODE (last) == RETURN_EXPR
#ifdef USE_MAPPED_LOCATION
&& (location = EXPR_LOCATION (last)) != UNKNOWN_LOCATION)
#else
&& (locus = EXPR_LOCUS (last)) != NULL)
#endif
break;
}
#ifdef USE_MAPPED_LOCATION
if (location == UNKNOWN_LOCATION)
location = cfun->function_end_locus;
warning (0, "%H%<noreturn%> function does return", &location);
#else
if (!locus)
locus = &cfun->function_end_locus;
warning (0, "%H%<noreturn%> function does return", locus);
#endif
}
else if (warn_return_type
&& !TREE_NO_WARNING (cfun->decl)
&& EDGE_COUNT (EXIT_BLOCK_PTR->preds) > 0
&& !VOID_TYPE_P (TREE_TYPE (TREE_TYPE (cfun->decl))))
{
FOR_EACH_EDGE (e, ei, EXIT_BLOCK_PTR->preds)
{
tree last = last_stmt (e->src);
if (TREE_CODE (last) == RETURN_EXPR
&& TREE_OPERAND (last, 0) == NULL
&& !TREE_NO_WARNING (last))
{
#ifdef USE_MAPPED_LOCATION
location = EXPR_LOCATION (last);
if (location == UNKNOWN_LOCATION)
location = cfun->function_end_locus;
warning (0, "%Hcontrol reaches end of non-void function", &location);
#else
locus = EXPR_LOCUS (last);
if (!locus)
locus = &cfun->function_end_locus;
warning (0, "%Hcontrol reaches end of non-void function", locus);
#endif
TREE_NO_WARNING (cfun->decl) = 1;
break;
}
}
}
return 0;
}
void
extract_true_false_edges_from_block (basic_block b,
edge *true_edge,
edge *false_edge)
{
edge e = EDGE_SUCC (b, 0);
if (e->flags & EDGE_TRUE_VALUE)
{
*true_edge = e;
*false_edge = EDGE_SUCC (b, 1);
}
else
{
*false_edge = e;
*true_edge = EDGE_SUCC (b, 1);
}
}
struct tree_opt_pass pass_warn_function_return =
{
NULL,
NULL,
execute_warn_function_return,
NULL,
NULL,
0,
0,
PROP_cfg,
0,
0,
0,
0,
0
};
static unsigned int
execute_warn_function_noreturn (void)
{
if (warn_missing_noreturn
&& !TREE_THIS_VOLATILE (cfun->decl)
&& EDGE_COUNT (EXIT_BLOCK_PTR->preds) == 0
&& !lang_hooks.function.missing_noreturn_ok_p (cfun->decl))
warning (OPT_Wmissing_noreturn, "%Jfunction might be possible candidate "
"for attribute %<noreturn%>",
cfun->decl);
return 0;
}
struct tree_opt_pass pass_warn_function_noreturn =
{
NULL,
NULL,
execute_warn_function_noreturn,
NULL,
NULL,
0,
0,
PROP_cfg,
0,
0,
0,
0,
0
};