X-Git-Url: http://git.sourceforge.jp/view?p=pf3gnuchains%2Fgcc-fork.git;a=blobdiff_plain;f=gcc%2Ftree-cfg.c;h=761dce159d1d5be575c509e03288f60473c3a34d;hp=6c5eb87d64443aee8c8737e740aa3ea2ece5699b;hb=f80e77555daaf6c32840c28b9d94f61ba47aa173;hpb=87f9ffa46e585b1dba9fcdfc27ef68177b96a599 diff --git a/gcc/tree-cfg.c b/gcc/tree-cfg.c index 6c5eb87d644..761dce159d1 100644 --- a/gcc/tree-cfg.c +++ b/gcc/tree-cfg.c @@ -1,6 +1,6 @@ /* Control flow functions for trees. - Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 - Free Software Foundation, Inc. + Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, + 2010 Free Software Foundation, Inc. Contributed by Diego Novillo This file is part of GCC. @@ -71,6 +71,12 @@ static const int initial_cfg_capacity = 20; static struct pointer_map_t *edge_to_cases; +/* If we record edge_to_cases, this bitmap will hold indexes + of basic blocks that end in a GIMPLE_SWITCH which we touched + due to edge manipulations. */ + +static bitmap touched_switch_bbs; + /* CFG statistics. */ struct cfg_stats_d { @@ -82,6 +88,14 @@ static struct cfg_stats_d cfg_stats; /* Nonzero if we found a computed goto while building basic blocks. */ static bool found_computed_goto; +/* Hash table to store last discriminator assigned for each locus. */ +struct locus_discrim_map +{ + location_t locus; + int discriminator; +}; +static htab_t discriminator_per_locus; + /* Basic blocks and flowgraphs. */ static void make_blocks (gimple_seq); static void factor_computed_gotos (void); @@ -91,6 +105,10 @@ static void make_edges (void); static void make_cond_expr_edges (basic_block); static void make_gimple_switch_edges (basic_block); static void make_goto_expr_edges (basic_block); +static void make_gimple_asm_edges (basic_block); +static unsigned int locus_map_hash (const void *); +static int locus_map_eq (const void *, const void *); +static void assign_discriminator (location_t, basic_block); static edge gimple_redirect_edge_and_branch (edge, basic_block); static edge gimple_try_redirect_by_replacing_jump (edge, basic_block); static unsigned int split_critical_edges (void); @@ -100,6 +118,7 @@ static inline bool stmt_starts_bb_p (gimple, gimple); static int gimple_verify_flow_info (void); static void gimple_make_forwarder_block (edge); static void gimple_cfg2vcg (FILE *); +static gimple first_non_label_stmt (basic_block); /* Flowgraph optimization and cleanup. */ static void gimple_merge_blocks (basic_block, basic_block); @@ -109,6 +128,7 @@ 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 (gimple, tree); +static void group_case_labels_stmt (gimple); void init_empty_tree_cfg_for_function (struct function *fn) @@ -131,9 +151,9 @@ init_empty_tree_cfg_for_function (struct function *fn) label_to_block_map_for_function (fn), initial_cfg_capacity); - SET_BASIC_BLOCK_FOR_FUNCTION (fn, ENTRY_BLOCK, + SET_BASIC_BLOCK_FOR_FUNCTION (fn, ENTRY_BLOCK, ENTRY_BLOCK_PTR_FOR_FUNCTION (fn)); - SET_BASIC_BLOCK_FOR_FUNCTION (fn, EXIT_BLOCK, + SET_BASIC_BLOCK_FOR_FUNCTION (fn, EXIT_BLOCK, EXIT_BLOCK_PTR_FOR_FUNCTION (fn)); ENTRY_BLOCK_PTR_FOR_FUNCTION (fn)->next_bb @@ -193,8 +213,11 @@ build_gimple_cfg (gimple_seq seq) group_case_labels (); /* Create the edges of the flowgraph. */ + discriminator_per_locus = htab_create (13, locus_map_hash, locus_map_eq, + free); make_edges (); cleanup_dead_labels (); + htab_delete (discriminator_per_locus); /* Debugging dumps. */ @@ -314,7 +337,7 @@ factor_computed_gotos (void) /* Build a label for the new block which will contain the factored computed goto. */ - factored_label_decl = create_artificial_label (); + factored_label_decl = create_artificial_label (UNKNOWN_LOCATION); factored_computed_goto_label = gimple_build_label (factored_label_decl); gsi_insert_after (&new_gsi, factored_computed_goto_label, @@ -375,7 +398,29 @@ make_blocks (gimple_seq seq) /* If STMT is a basic block terminator, set START_NEW_BLOCK for the next iteration. */ if (stmt_ends_bb_p (stmt)) - start_new_block = true; + { + /* If the stmt can make abnormal goto use a new temporary + for the assignment to the LHS. This makes sure the old value + of the LHS is available on the abnormal edge. Otherwise + we will end up with overlapping life-ranges for abnormal + SSA names. */ + if (gimple_has_lhs (stmt) + && stmt_can_make_abnormal_goto (stmt) + && is_gimple_reg_type (TREE_TYPE (gimple_get_lhs (stmt)))) + { + tree lhs = gimple_get_lhs (stmt); + tree tmp = create_tmp_var (TREE_TYPE (lhs), NULL); + gimple s = gimple_build_assign (lhs, tmp); + gimple_set_location (s, gimple_location (stmt)); + gimple_set_block (s, gimple_block (stmt)); + gimple_set_lhs (stmt, tmp); + if (TREE_CODE (TREE_TYPE (tmp)) == COMPLEX_TYPE + || TREE_CODE (TREE_TYPE (tmp)) == VECTOR_TYPE) + DECL_GIMPLE_REG_P (tmp) = 1; + gsi_insert_after (&i, s, GSI_SAME_STMT); + } + start_new_block = true; + } gsi_next (&i); first_stmt_of_seq = false; @@ -439,11 +484,12 @@ fold_cond_expr_cond (void) if (stmt && gimple_code (stmt) == GIMPLE_COND) { + location_t loc = gimple_location (stmt); tree cond; bool zerop, onep; fold_defer_overflow_warnings (); - cond = fold_binary (gimple_cond_code (stmt), boolean_type_node, + cond = fold_binary_loc (loc, gimple_cond_code (stmt), boolean_type_node, gimple_cond_lhs (stmt), gimple_cond_rhs (stmt)); if (cond) { @@ -507,6 +553,9 @@ make_edges (void) make_eh_edges (last); fallthru = false; break; + case GIMPLE_EH_DISPATCH: + fallthru = make_eh_dispatch_edges (last); + break; case GIMPLE_CALL: /* If this function receives a nonlocal goto, then we need to @@ -527,9 +576,12 @@ make_edges (void) /* A GIMPLE_ASSIGN may throw internally and thus be considered control-altering. */ if (is_ctrl_altering_stmt (last)) - { - make_eh_edges (last); - } + make_eh_edges (last); + fallthru = true; + break; + + case GIMPLE_ASM: + make_gimple_asm_edges (bb); fallthru = true; break; @@ -554,13 +606,11 @@ make_edges (void) fallthru = false; break; - case GIMPLE_OMP_ATOMIC_LOAD: case GIMPLE_OMP_ATOMIC_STORE: fallthru = true; break; - case GIMPLE_OMP_RETURN: /* In the case of a GIMPLE_OMP_SECTION, the edge will go somewhere other than the next block. This will be @@ -628,7 +678,11 @@ make_edges (void) fallthru = true; if (fallthru) - make_edge (bb, bb->next_bb, EDGE_FALLTHRU); + { + make_edge (bb, bb->next_bb, EDGE_FALLTHRU); + if (last) + assign_discriminator (gimple_location (last), bb->next_bb); + } } if (root_omp_region) @@ -638,6 +692,93 @@ make_edges (void) fold_cond_expr_cond (); } +/* Trivial hash function for a location_t. ITEM is a pointer to + a hash table entry that maps a location_t to a discriminator. */ + +static unsigned int +locus_map_hash (const void *item) +{ + return ((const struct locus_discrim_map *) item)->locus; +} + +/* Equality function for the locus-to-discriminator map. VA and VB + point to the two hash table entries to compare. */ + +static int +locus_map_eq (const void *va, const void *vb) +{ + const struct locus_discrim_map *a = (const struct locus_discrim_map *) va; + const struct locus_discrim_map *b = (const struct locus_discrim_map *) vb; + return a->locus == b->locus; +} + +/* Find the next available discriminator value for LOCUS. The + discriminator distinguishes among several basic blocks that + share a common locus, allowing for more accurate sample-based + profiling. */ + +static int +next_discriminator_for_locus (location_t locus) +{ + struct locus_discrim_map item; + struct locus_discrim_map **slot; + + item.locus = locus; + item.discriminator = 0; + slot = (struct locus_discrim_map **) + htab_find_slot_with_hash (discriminator_per_locus, (void *) &item, + (hashval_t) locus, INSERT); + gcc_assert (slot); + if (*slot == HTAB_EMPTY_ENTRY) + { + *slot = XNEW (struct locus_discrim_map); + gcc_assert (*slot); + (*slot)->locus = locus; + (*slot)->discriminator = 0; + } + (*slot)->discriminator++; + return (*slot)->discriminator; +} + +/* Return TRUE if LOCUS1 and LOCUS2 refer to the same source line. */ + +static bool +same_line_p (location_t locus1, location_t locus2) +{ + expanded_location from, to; + + if (locus1 == locus2) + return true; + + from = expand_location (locus1); + to = expand_location (locus2); + + if (from.line != to.line) + return false; + if (from.file == to.file) + return true; + return (from.file != NULL + && to.file != NULL + && strcmp (from.file, to.file) == 0); +} + +/* Assign a unique discriminator value to block BB if it begins at the same + LOCUS as its predecessor block. */ + +static void +assign_discriminator (location_t locus, basic_block bb) +{ + gimple first_in_to_bb, last_in_to_bb; + + if (locus == 0 || bb->discriminator != 0) + return; + + first_in_to_bb = first_non_label_stmt (bb); + last_in_to_bb = last_stmt (bb); + if ((first_in_to_bb && same_line_p (locus, gimple_location (first_in_to_bb))) + || (last_in_to_bb && same_line_p (locus, gimple_location (last_in_to_bb)))) + bb->discriminator = next_discriminator_for_locus (locus); +} /* Create the edges for a GIMPLE_COND starting at block BB. */ @@ -649,10 +790,13 @@ make_cond_expr_edges (basic_block bb) basic_block then_bb, else_bb; tree then_label, else_label; edge e; + location_t entry_locus; gcc_assert (entry); gcc_assert (gimple_code (entry) == GIMPLE_COND); + entry_locus = gimple_location (entry); + /* Entry basic blocks for each component. */ then_label = gimple_cond_true_label (entry); else_label = gimple_cond_false_label (entry); @@ -662,12 +806,14 @@ make_cond_expr_edges (basic_block bb) else_stmt = first_stmt (else_bb); e = make_edge (bb, then_bb, EDGE_TRUE_VALUE); + assign_discriminator (entry_locus, then_bb); e->goto_locus = gimple_location (then_stmt); if (e->goto_locus) e->goto_block = gimple_block (then_stmt); e = make_edge (bb, else_bb, EDGE_FALSE_VALUE); if (e) { + assign_discriminator (entry_locus, else_bb); e->goto_locus = gimple_location (else_stmt); if (e->goto_locus) e->goto_block = gimple_block (else_stmt); @@ -709,6 +855,7 @@ start_recording_case_labels (void) { gcc_assert (edge_to_cases == NULL); edge_to_cases = pointer_map_create (); + touched_switch_bbs = BITMAP_ALLOC (NULL); } /* Return nonzero if we are recording information for case labels. */ @@ -724,9 +871,22 @@ recording_case_labels_p (void) void end_recording_case_labels (void) { + bitmap_iterator bi; + unsigned i; pointer_map_traverse (edge_to_cases, edge_to_cases_cleanup, NULL); pointer_map_destroy (edge_to_cases); edge_to_cases = NULL; + EXECUTE_IF_SET_IN_BITMAP (touched_switch_bbs, 0, i, bi) + { + basic_block bb = BASIC_BLOCK (i); + if (bb) + { + gimple stmt = last_stmt (bb); + if (stmt && gimple_code (stmt) == GIMPLE_SWITCH) + group_case_labels_stmt (stmt); + } + } + BITMAP_FREE (touched_switch_bbs); } /* If we are inside a {start,end}_recording_cases block, then return @@ -777,8 +937,11 @@ static void make_gimple_switch_edges (basic_block bb) { gimple entry = last_stmt (bb); + location_t entry_locus; size_t i, n; + entry_locus = gimple_location (entry); + n = gimple_switch_num_labels (entry); for (i = 0; i < n; ++i) @@ -786,6 +949,7 @@ make_gimple_switch_edges (basic_block bb) tree lab = CASE_LABEL (gimple_switch_label (entry, i)); basic_block label_bb = label_to_block (lab); make_edge (bb, label_bb, 0); + assign_discriminator (entry_locus, label_bb); } } @@ -858,8 +1022,10 @@ make_goto_expr_edges (basic_block bb) if (simple_goto_p (goto_t)) { tree dest = gimple_goto_dest (goto_t); - edge e = make_edge (bb, label_to_block (dest), EDGE_FALLTHRU); + basic_block label_bb = label_to_block (dest); + edge e = make_edge (bb, label_bb, EDGE_FALLTHRU); e->goto_locus = gimple_location (goto_t); + assign_discriminator (e->goto_locus, label_bb); if (e->goto_locus) e->goto_block = gimple_block (goto_t); gsi_remove (&last, true); @@ -870,6 +1036,23 @@ make_goto_expr_edges (basic_block bb) make_abnormal_goto_edges (bb, false); } +/* Create edges for an asm statement with labels at block BB. */ + +static void +make_gimple_asm_edges (basic_block bb) +{ + gimple stmt = last_stmt (bb); + location_t stmt_loc = gimple_location (stmt); + int i, n = gimple_asm_nlabels (stmt); + + for (i = 0; i < n; ++i) + { + tree label = TREE_VALUE (gimple_asm_label_op (stmt, i)); + basic_block label_bb = label_to_block (label); + make_edge (bb, label_bb, 0); + assign_discriminator (stmt_loc, label_bb); + } +} /*--------------------------------------------------------------------------- Flowgraph analysis @@ -893,29 +1076,6 @@ static struct label_record bool used; } *label_for_bb; -/* Callback for for_each_eh_region. Helper for cleanup_dead_labels. */ -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); - - /* ??? After optimizing, there may be EH regions with labels - that have already been removed from the function body, so - there is no basic block for them. */ - if (! bb) - return; - - new_label = label_for_bb[bb->index].label; - label_for_bb[bb->index].used = true; - set_eh_region_tree_label (region, new_label); - } -} - - /* Given LABEL return the first label in the same basic block. */ static tree @@ -935,6 +1095,58 @@ main_block_label (tree label) return main_label; } +/* Clean up redundant labels within the exception tree. */ + +static void +cleanup_dead_labels_eh (void) +{ + eh_landing_pad lp; + eh_region r; + tree lab; + int i; + + if (cfun->eh == NULL) + return; + + for (i = 1; VEC_iterate (eh_landing_pad, cfun->eh->lp_array, i, lp); ++i) + if (lp && lp->post_landing_pad) + { + lab = main_block_label (lp->post_landing_pad); + if (lab != lp->post_landing_pad) + { + EH_LANDING_PAD_NR (lp->post_landing_pad) = 0; + EH_LANDING_PAD_NR (lab) = lp->index; + } + } + + FOR_ALL_EH_REGION (r) + switch (r->type) + { + case ERT_CLEANUP: + case ERT_MUST_NOT_THROW: + break; + + case ERT_TRY: + { + eh_catch c; + for (c = r->u.eh_try.first_catch; c ; c = c->next_catch) + { + lab = c->label; + if (lab) + c->label = main_block_label (lab); + } + } + break; + + case ERT_ALLOWED_EXCEPTIONS: + lab = r->u.allowed.label; + if (lab) + r->u.allowed.label = main_block_label (lab); + break; + } +} + + /* Cleanup redundant labels. This is a three-step process: 1) Find the leading label for each block. 2) Redirect all references to labels to the leading labels. @@ -1018,6 +1230,19 @@ cleanup_dead_labels (void) break; } + case GIMPLE_ASM: + { + int i, n = gimple_asm_nlabels (stmt); + + for (i = 0; i < n; ++i) + { + tree cons = gimple_asm_label_op (stmt, i); + tree label = main_block_label (TREE_VALUE (cons)); + TREE_VALUE (cons) = label; + } + break; + } + /* We have to handle gotos until they're removed, and we don't remove them until after we've created the CFG edges. */ case GIMPLE_GOTO: @@ -1025,15 +1250,16 @@ cleanup_dead_labels (void) { tree new_dest = main_block_label (gimple_goto_dest (stmt)); gimple_goto_set_dest (stmt, new_dest); - break; } + break; default: break; } } - for_each_eh_region (update_eh_label); + /* Do the same for the exception region tree labels. */ + cleanup_dead_labels_eh (); /* Finally, purge dead labels. All user-defined labels and labels that can be the target of non-local gotos and labels which have their @@ -1073,108 +1299,115 @@ cleanup_dead_labels (void) free (label_for_bb); } -/* Look for blocks ending in a multiway branch (a SWITCH_EXPR in GIMPLE), - and scan the sorted vector of cases. Combine the ones jumping to the - same label. +/* Scan the sorted vector of cases in STMT (a GIMPLE_SWITCH) and combine + the ones jumping to the same label. Eg. three separate entries 1: 2: 3: become one entry 1..3: */ -void -group_case_labels (void) +static void +group_case_labels_stmt (gimple stmt) { - basic_block bb; + int old_size = gimple_switch_num_labels (stmt); + int i, j, new_size = old_size; + tree default_case = NULL_TREE; + tree default_label = NULL_TREE; + bool has_default; - FOR_EACH_BB (bb) + /* The default label is always the first case in a switch + statement after gimplification if it was not optimized + away */ + if (!CASE_LOW (gimple_switch_default_label (stmt)) + && !CASE_HIGH (gimple_switch_default_label (stmt))) { - gimple stmt = last_stmt (bb); - if (stmt && gimple_code (stmt) == GIMPLE_SWITCH) + default_case = gimple_switch_default_label (stmt); + default_label = CASE_LABEL (default_case); + has_default = true; + } + else + has_default = false; + + /* Look for possible opportunities to merge cases. */ + if (has_default) + i = 1; + else + i = 0; + while (i < old_size) + { + tree base_case, base_label, base_high; + base_case = gimple_switch_label (stmt, i); + + gcc_assert (base_case); + base_label = CASE_LABEL (base_case); + + /* Discard cases that have the same destination as the + default case. */ + if (base_label == default_label) + { + gimple_switch_set_label (stmt, i, NULL_TREE); + i++; + new_size--; + continue; + } + + base_high = CASE_HIGH (base_case) + ? CASE_HIGH (base_case) + : CASE_LOW (base_case); + i++; + + /* Try to merge case labels. Break out when we reach the end + of the label vector or when we cannot merge the next case + label with the current one. */ + while (i < old_size) { - int old_size = gimple_switch_num_labels (stmt); - int i, j, new_size = old_size; - tree default_case = NULL_TREE; - tree default_label = NULL_TREE; - bool has_default; - - /* The default label is always the first case in a switch - statement after gimplification if it was not optimized - away */ - if (!CASE_LOW (gimple_switch_default_label (stmt)) - && !CASE_HIGH (gimple_switch_default_label (stmt))) + tree merge_case = gimple_switch_label (stmt, i); + tree merge_label = CASE_LABEL (merge_case); + tree t = int_const_binop (PLUS_EXPR, base_high, + integer_one_node, 1); + + /* Merge the cases if they jump to the same place, + and their ranges are consecutive. */ + if (merge_label == base_label + && tree_int_cst_equal (CASE_LOW (merge_case), t)) { - default_case = gimple_switch_default_label (stmt); - default_label = CASE_LABEL (default_case); - has_default = true; + base_high = CASE_HIGH (merge_case) ? + CASE_HIGH (merge_case) : CASE_LOW (merge_case); + CASE_HIGH (base_case) = base_high; + gimple_switch_set_label (stmt, i, NULL_TREE); + new_size--; + i++; } else - has_default = false; - - /* Look for possible opportunities to merge cases. */ - if (has_default) - i = 1; - else - i = 0; - while (i < old_size) - { - tree base_case, base_label, base_high; - base_case = gimple_switch_label (stmt, i); - - gcc_assert (base_case); - base_label = CASE_LABEL (base_case); + break; + } + } - /* Discard cases that have the same destination as the - default case. */ - if (base_label == default_label) - { - gimple_switch_set_label (stmt, i, NULL_TREE); - i++; - new_size--; - continue; - } + /* Compress the case labels in the label vector, and adjust the + length of the vector. */ + for (i = 0, j = 0; i < new_size; i++) + { + while (! gimple_switch_label (stmt, j)) + j++; + gimple_switch_set_label (stmt, i, + gimple_switch_label (stmt, j++)); + } - base_high = CASE_HIGH (base_case) - ? CASE_HIGH (base_case) - : CASE_LOW (base_case); - i++; + gcc_assert (new_size <= old_size); + gimple_switch_set_num_labels (stmt, new_size); +} - /* Try to merge case labels. Break out when we reach the end - of the label vector or when we cannot merge the next case - label with the current one. */ - while (i < old_size) - { - tree merge_case = gimple_switch_label (stmt, i); - tree merge_label = CASE_LABEL (merge_case); - tree t = int_const_binop (PLUS_EXPR, base_high, - integer_one_node, 1); - - /* Merge the cases if they jump to the same place, - and their ranges are consecutive. */ - 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; - gimple_switch_set_label (stmt, i, NULL_TREE); - new_size--; - i++; - } - else - break; - } - } +/* Look for blocks ending in a multiway branch (a GIMPLE_SWITCH), + and scan the sorted vector of cases. Combine the ones jumping to the + same label. */ - /* Compress the case labels in the label vector, and adjust the - length of the vector. */ - for (i = 0, j = 0; i < new_size; i++) - { - while (! gimple_switch_label (stmt, j)) - j++; - gimple_switch_set_label (stmt, i, - gimple_switch_label (stmt, j++)); - } +void +group_case_labels (void) +{ + basic_block bb; - gcc_assert (new_size <= old_size); - gimple_switch_set_num_labels (stmt, new_size); - } + FOR_EACH_BB (bb) + { + gimple stmt = last_stmt (bb); + if (stmt && gimple_code (stmt) == GIMPLE_SWITCH) + group_case_labels_stmt (stmt); } } @@ -1190,7 +1423,7 @@ gimple_can_merge_blocks_p (basic_block a, basic_block b) if (!single_succ_p (a)) return false; - if (single_succ_edge (a)->flags & EDGE_ABNORMAL) + if (single_succ_edge (a)->flags & (EDGE_ABNORMAL | EDGE_EH)) return false; if (single_succ (a) != b) @@ -1214,47 +1447,78 @@ gimple_can_merge_blocks_p (basic_block a, basic_block b) && DECL_NONLOCAL (gimple_label_label (stmt))) return false; - /* It must be possible to eliminate all phi nodes in B. If ssa form - is not up-to-date, we cannot eliminate any phis; however, if only - some symbols as whole are marked for renaming, this is not a problem, - as phi nodes for those symbols are irrelevant in updating anyway. */ - phis = phi_nodes (b); - if (!gimple_seq_empty_p (phis)) - { - gimple_stmt_iterator i; - - if (name_mappings_registered_p ()) - return false; - - for (i = gsi_start (phis); !gsi_end_p (i); gsi_next (&i)) - { - gimple phi = gsi_stmt (i); - - if (!is_gimple_reg (gimple_phi_result (phi)) - && !may_propagate_copy (gimple_phi_result (phi), - gimple_phi_arg_def (phi, 0))) - return false; - } - } - - /* Do not remove user labels. */ + /* Examine the labels at the beginning of B. */ for (gsi = gsi_start_bb (b); !gsi_end_p (gsi); gsi_next (&gsi)) { + tree lab; stmt = gsi_stmt (gsi); if (gimple_code (stmt) != GIMPLE_LABEL) break; - if (!DECL_ARTIFICIAL (gimple_label_label (stmt))) + lab = gimple_label_label (stmt); + + /* Do not remove user labels. */ + if (!DECL_ARTIFICIAL (lab)) return false; } /* Protect the loop latches. */ - if (current_loops - && b->loop_father->latch == b) + if (current_loops && b->loop_father->latch == b) + return false; + + /* It must be possible to eliminate all phi nodes in B. If ssa form + is not up-to-date and a name-mapping is registered, we cannot eliminate + any phis. Symbols marked for renaming are never a problem though. */ + phis = phi_nodes (b); + if (!gimple_seq_empty_p (phis) + && name_mappings_registered_p ()) return false; return true; } +/* Return true if the var whose chain of uses starts at PTR has no + nondebug uses. */ +bool +has_zero_uses_1 (const ssa_use_operand_t *head) +{ + const ssa_use_operand_t *ptr; + + for (ptr = head->next; ptr != head; ptr = ptr->next) + if (!is_gimple_debug (USE_STMT (ptr))) + return false; + + return true; +} + +/* Return true if the var whose chain of uses starts at PTR has a + single nondebug use. Set USE_P and STMT to that single nondebug + use, if so, or to NULL otherwise. */ +bool +single_imm_use_1 (const ssa_use_operand_t *head, + use_operand_p *use_p, gimple *stmt) +{ + ssa_use_operand_t *ptr, *single_use = 0; + + for (ptr = head->next; ptr != head; ptr = ptr->next) + if (!is_gimple_debug (USE_STMT (ptr))) + { + if (single_use) + { + single_use = NULL; + break; + } + single_use = ptr; + } + + if (use_p) + *use_p = single_use; + + if (stmt) + *stmt = single_use ? single_use->loc.stmt : NULL; + + return !!single_use; +} + /* Replaces all uses of NAME by VAL. */ void @@ -1267,9 +1531,6 @@ replace_uses_by (tree name, tree val) FOR_EACH_IMM_USE_STMT (stmt, imm_iter, name) { - if (gimple_code (stmt) != GIMPLE_PHI) - push_stmt_changes (&stmt); - FOR_EACH_IMM_USE_ON_STMT (use, imm_iter) { replace_exp (use, val); @@ -1296,7 +1557,7 @@ replace_uses_by (tree name, tree val) if (cfgcleanup_altered_bbs) bitmap_set_bit (cfgcleanup_altered_bbs, gimple_bb (stmt)->index); - /* FIXME. This should go in pop_stmt_changes. */ + /* FIXME. This should go in update_stmt. */ for (i = 0; i < gimple_num_ops (stmt); i++) { tree op = gimple_op (stmt, i); @@ -1308,8 +1569,7 @@ replace_uses_by (tree name, tree val) } maybe_clean_or_replace_eh_stmt (stmt, stmt); - - pop_stmt_changes (&stmt); + update_stmt (stmt); } } @@ -1385,6 +1645,9 @@ gimple_merge_blocks (basic_block a, basic_block b) FOR_EACH_IMM_USE_STMT (stmt, iter, def) FOR_EACH_IMM_USE_ON_STMT (use_p, iter) SET_USE (use_p, use); + + if (SSA_NAME_OCCURS_IN_ABNORMAL_PHI (def)) + SSA_NAME_OCCURS_IN_ABNORMAL_PHI (use) = 1; } else replace_uses_by (def, use); @@ -1402,475 +1665,72 @@ gimple_merge_blocks (basic_block a, basic_block b) /* Remove labels from B and set gimple_bb to A for other statements. */ for (gsi = gsi_start_bb (b); !gsi_end_p (gsi);) { - if (gimple_code (gsi_stmt (gsi)) == GIMPLE_LABEL) - { - gimple label = gsi_stmt (gsi); - - gsi_remove (&gsi, false); - - /* Now that we can thread computed gotos, we might have - a situation where we have a forced label in block B - However, the label at the start of block B might still be - used in other ways (think about the runtime checking for - Fortran assigned gotos). So we can not just delete the - label. Instead we move the label to the start of block A. */ - if (FORCED_LABEL (gimple_label_label (label))) - { - gimple_stmt_iterator dest_gsi = gsi_start_bb (a); - gsi_insert_before (&dest_gsi, label, GSI_NEW_STMT); - } - } - else - { - gimple_set_bb (gsi_stmt (gsi), a); - gsi_next (&gsi); - } - } - - /* Merge the sequences. */ - last = gsi_last_bb (a); - gsi_insert_seq_after (&last, bb_seq (b), GSI_NEW_STMT); - set_bb_seq (b, NULL); - - if (cfgcleanup_altered_bbs) - bitmap_set_bit (cfgcleanup_altered_bbs, a->index); -} - - -/* Return the one of two successors of BB that is not reachable by a - reached by a complex edge, if there is one. Else, return BB. We use - this in optimizations that use post-dominators for their heuristics, - to catch the cases in C++ where function calls are involved. */ - -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; -} - - -/* Walk the function tree removing unnecessary statements. - - * Empty statement nodes are removed - - * Unnecessary TRY_FINALLY and TRY_CATCH blocks are removed - - * Unnecessary COND_EXPRs are removed - - * Some unnecessary BIND_EXPRs are removed - - * GOTO_EXPRs immediately preceding destination are removed. - - Clearly more work could be done. The trick is doing the analysis - and removal fast enough to be a net improvement in compile times. - - Note that when we remove a control structure such as a COND_EXPR - BIND_EXPR, or TRY block, we will need to repeat this optimization pass - to ensure we eliminate all the useless code. */ - -struct rus_data -{ - bool repeat; - bool may_throw; - bool may_branch; - bool has_label; - bool last_was_goto; - gimple_stmt_iterator last_goto_gsi; -}; - - -static void remove_useless_stmts_1 (gimple_stmt_iterator *gsi, struct rus_data *); - -/* Given a statement sequence, find the first executable statement with - location information, and warn that it is unreachable. When searching, - descend into containers in execution order. */ - -static bool -remove_useless_stmts_warn_notreached (gimple_seq stmts) -{ - gimple_stmt_iterator gsi; - - for (gsi = gsi_start (stmts); !gsi_end_p (gsi); gsi_next (&gsi)) - { gimple stmt = gsi_stmt (gsi); + if (gimple_code (stmt) == GIMPLE_LABEL) + { + tree label = gimple_label_label (stmt); + int lp_nr; - if (gimple_has_location (stmt)) - { - location_t loc = gimple_location (stmt); - if (LOCATION_LINE (loc) > 0) - { - warning (OPT_Wunreachable_code, "%Hwill never be executed", &loc); - return true; - } - } - - switch (gimple_code (stmt)) - { - /* Unfortunately, we need the CFG now to detect unreachable - branches in a conditional, so conditionals are not handled here. */ - - case GIMPLE_TRY: - if (remove_useless_stmts_warn_notreached (gimple_try_eval (stmt))) - return true; - if (remove_useless_stmts_warn_notreached (gimple_try_cleanup (stmt))) - return true; - break; - - case GIMPLE_CATCH: - return remove_useless_stmts_warn_notreached (gimple_catch_handler (stmt)); - - case GIMPLE_EH_FILTER: - return remove_useless_stmts_warn_notreached (gimple_eh_filter_failure (stmt)); - - case GIMPLE_BIND: - return remove_useless_stmts_warn_notreached (gimple_bind_body (stmt)); - - default: - break; - } - } - - return false; -} - -/* Helper for remove_useless_stmts_1. Handle GIMPLE_COND statements. */ - -static void -remove_useless_stmts_cond (gimple_stmt_iterator *gsi, struct rus_data *data) -{ - gimple stmt = gsi_stmt (*gsi); - - /* The folded result must still be a conditional statement. */ - fold_stmt_inplace (stmt); - - data->may_branch = true; - - /* Replace trivial conditionals with gotos. */ - if (gimple_cond_true_p (stmt)) - { - /* Goto THEN label. */ - tree then_label = gimple_cond_true_label (stmt); - - gsi_replace (gsi, gimple_build_goto (then_label), false); - data->last_goto_gsi = *gsi; - data->last_was_goto = true; - data->repeat = true; - } - else if (gimple_cond_false_p (stmt)) - { - /* Goto ELSE label. */ - tree else_label = gimple_cond_false_label (stmt); - - gsi_replace (gsi, gimple_build_goto (else_label), false); - data->last_goto_gsi = *gsi; - data->last_was_goto = true; - data->repeat = true; - } - else - { - tree then_label = gimple_cond_true_label (stmt); - tree else_label = gimple_cond_false_label (stmt); - - if (then_label == else_label) - { - /* Goto common destination. */ - gsi_replace (gsi, gimple_build_goto (then_label), false); - data->last_goto_gsi = *gsi; - data->last_was_goto = true; - data->repeat = true; - } - } - - gsi_next (gsi); - - data->last_was_goto = false; -} - -/* Helper for remove_useless_stmts_1. - Handle the try-finally case for GIMPLE_TRY statements. */ - -static void -remove_useless_stmts_tf (gimple_stmt_iterator *gsi, struct rus_data *data) -{ - bool save_may_branch, save_may_throw; - bool this_may_branch, this_may_throw; - - gimple_seq eval_seq, cleanup_seq; - gimple_stmt_iterator eval_gsi, cleanup_gsi; - - gimple stmt = gsi_stmt (*gsi); - - /* Collect may_branch and may_throw information for the body only. */ - save_may_branch = data->may_branch; - save_may_throw = data->may_throw; - data->may_branch = false; - data->may_throw = false; - data->last_was_goto = false; - - eval_seq = gimple_try_eval (stmt); - eval_gsi = gsi_start (eval_seq); - remove_useless_stmts_1 (&eval_gsi, 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_was_goto = false; - - cleanup_seq = gimple_try_cleanup (stmt); - cleanup_gsi = gsi_start (cleanup_seq); - remove_useless_stmts_1 (&cleanup_gsi, data); - - /* If the body is empty, then we can emit the FINALLY block without - the enclosing TRY_FINALLY_EXPR. */ - if (gimple_seq_empty_p (eval_seq)) - { - gsi_insert_seq_before (gsi, cleanup_seq, GSI_SAME_STMT); - gsi_remove (gsi, false); - data->repeat = true; - } - - /* If the handler is empty, then we can emit the TRY block without - the enclosing TRY_FINALLY_EXPR. */ - else if (gimple_seq_empty_p (cleanup_seq)) - { - gsi_insert_seq_before (gsi, eval_seq, GSI_SAME_STMT); - gsi_remove (gsi, false); - data->repeat = true; - } - - /* If the body neither throws, nor branches, then we can safely - string the TRY and FINALLY blocks together. */ - else if (!this_may_branch && !this_may_throw) - { - gsi_insert_seq_before (gsi, eval_seq, GSI_SAME_STMT); - gsi_insert_seq_before (gsi, cleanup_seq, GSI_SAME_STMT); - gsi_remove (gsi, false); - data->repeat = true; - } - else - gsi_next (gsi); -} - -/* Helper for remove_useless_stmts_1. - Handle the try-catch case for GIMPLE_TRY statements. */ - -static void -remove_useless_stmts_tc (gimple_stmt_iterator *gsi, struct rus_data *data) -{ - bool save_may_throw, this_may_throw; - - gimple_seq eval_seq, cleanup_seq, handler_seq, failure_seq; - gimple_stmt_iterator eval_gsi, cleanup_gsi, handler_gsi, failure_gsi; - - gimple stmt = gsi_stmt (*gsi); - - /* Collect may_throw information for the body only. */ - save_may_throw = data->may_throw; - data->may_throw = false; - data->last_was_goto = false; - - eval_seq = gimple_try_eval (stmt); - eval_gsi = gsi_start (eval_seq); - remove_useless_stmts_1 (&eval_gsi, data); - - this_may_throw = data->may_throw; - data->may_throw = save_may_throw; - - cleanup_seq = gimple_try_cleanup (stmt); - - /* If the body cannot throw, then we can drop the entire TRY_CATCH_EXPR. */ - if (!this_may_throw) - { - if (warn_notreached) - { - remove_useless_stmts_warn_notreached (cleanup_seq); - } - gsi_insert_seq_before (gsi, eval_seq, GSI_SAME_STMT); - gsi_remove (gsi, false); - data->repeat = true; - return; - } - - /* Process the catch clause specially. We may be able to tell that - no exceptions propagate past this point. */ - - this_may_throw = true; - cleanup_gsi = gsi_start (cleanup_seq); - stmt = gsi_stmt (cleanup_gsi); - data->last_was_goto = false; - - switch (gimple_code (stmt)) - { - case GIMPLE_CATCH: - /* If the first element is a catch, they all must be. */ - while (!gsi_end_p (cleanup_gsi)) - { - stmt = gsi_stmt (cleanup_gsi); - /* If we catch all exceptions, then the body does not - propagate exceptions past this point. */ - if (gimple_catch_types (stmt) == NULL) - this_may_throw = false; - data->last_was_goto = false; - handler_seq = gimple_catch_handler (stmt); - handler_gsi = gsi_start (handler_seq); - remove_useless_stmts_1 (&handler_gsi, data); - gsi_next (&cleanup_gsi); - } - gsi_next (gsi); - break; - - case GIMPLE_EH_FILTER: - /* If the first element is an eh_filter, it should stand alone. */ - if (gimple_eh_filter_must_not_throw (stmt)) - this_may_throw = false; - else if (gimple_eh_filter_types (stmt) == NULL) - this_may_throw = false; - failure_seq = gimple_eh_filter_failure (stmt); - failure_gsi = gsi_start (failure_seq); - remove_useless_stmts_1 (&failure_gsi, data); - gsi_next (gsi); - break; - - default: - /* Otherwise this is a list of cleanup statements. */ - remove_useless_stmts_1 (&cleanup_gsi, data); - - /* If the cleanup is empty, then we can emit the TRY block without - the enclosing TRY_CATCH_EXPR. */ - if (gimple_seq_empty_p (cleanup_seq)) - { - gsi_insert_seq_before (gsi, eval_seq, GSI_SAME_STMT); - gsi_remove(gsi, false); - data->repeat = true; - } - else - gsi_next (gsi); - break; - } - - data->may_throw |= this_may_throw; -} - -/* Helper for remove_useless_stmts_1. Handle GIMPLE_BIND statements. */ - -static void -remove_useless_stmts_bind (gimple_stmt_iterator *gsi, struct rus_data *data ATTRIBUTE_UNUSED) -{ - tree block; - gimple_seq body_seq, fn_body_seq; - gimple_stmt_iterator body_gsi; + gsi_remove (&gsi, false); - gimple stmt = gsi_stmt (*gsi); + /* Now that we can thread computed gotos, we might have + a situation where we have a forced label in block B + However, the label at the start of block B might still be + used in other ways (think about the runtime checking for + Fortran assigned gotos). So we can not just delete the + label. Instead we move the label to the start of block A. */ + if (FORCED_LABEL (label)) + { + gimple_stmt_iterator dest_gsi = gsi_start_bb (a); + gsi_insert_before (&dest_gsi, stmt, GSI_NEW_STMT); + } - /* First remove anything underneath the BIND_EXPR. */ - - body_seq = gimple_bind_body (stmt); - body_gsi = gsi_start (body_seq); - remove_useless_stmts_1 (&body_gsi, data); - - /* If the GIMPLE_BIND has no variables, then we can pull everything - up one level and remove the GIMPLE_BIND, unless this is the toplevel - GIMPLE_BIND for the current function or an inlined function. - - When this situation occurs we will want to apply this - optimization again. */ - block = gimple_bind_block (stmt); - fn_body_seq = gimple_body (current_function_decl); - if (gimple_bind_vars (stmt) == NULL_TREE - && (gimple_seq_empty_p (fn_body_seq) - || stmt != gimple_seq_first_stmt (fn_body_seq)) - && (! block - || ! BLOCK_ABSTRACT_ORIGIN (block) - || (TREE_CODE (BLOCK_ABSTRACT_ORIGIN (block)) - != FUNCTION_DECL))) - { - tree var = NULL_TREE; - /* Even if there are no gimple_bind_vars, there might be other - decls in BLOCK_VARS rendering the GIMPLE_BIND not useless. */ - if (block && !BLOCK_NUM_NONLOCALIZED_VARS (block)) - for (var = BLOCK_VARS (block); var; var = TREE_CHAIN (var)) - if (TREE_CODE (var) == IMPORTED_DECL) - break; - if (var || (block && BLOCK_NUM_NONLOCALIZED_VARS (block))) - gsi_next (gsi); + lp_nr = EH_LANDING_PAD_NR (label); + if (lp_nr) + { + eh_landing_pad lp = get_eh_landing_pad_from_number (lp_nr); + lp->post_landing_pad = NULL; + } + } else { - gsi_insert_seq_before (gsi, body_seq, GSI_SAME_STMT); - gsi_remove (gsi, false); - data->repeat = true; + gimple_set_bb (stmt, a); + gsi_next (&gsi); } } - else - gsi_next (gsi); -} - -/* Helper for remove_useless_stmts_1. Handle GIMPLE_GOTO statements. */ - -static void -remove_useless_stmts_goto (gimple_stmt_iterator *gsi, struct rus_data *data) -{ - gimple stmt = gsi_stmt (*gsi); - - tree dest = gimple_goto_dest (stmt); - - data->may_branch = true; - data->last_was_goto = false; - /* Record iterator for last goto expr, so that we can delete it if unnecessary. */ - if (TREE_CODE (dest) == LABEL_DECL) - { - data->last_goto_gsi = *gsi; - data->last_was_goto = true; - } + /* Merge the sequences. */ + last = gsi_last_bb (a); + gsi_insert_seq_after (&last, bb_seq (b), GSI_NEW_STMT); + set_bb_seq (b, NULL); - gsi_next(gsi); + if (cfgcleanup_altered_bbs) + bitmap_set_bit (cfgcleanup_altered_bbs, a->index); } -/* Helper for remove_useless_stmts_1. Handle GIMPLE_LABEL statements. */ -static void -remove_useless_stmts_label (gimple_stmt_iterator *gsi, struct rus_data *data) -{ - gimple stmt = gsi_stmt (*gsi); - - tree label = gimple_label_label (stmt); - - data->has_label = true; - - /* We do want to jump across non-local label receiver code. */ - if (DECL_NONLOCAL (label)) - data->last_was_goto = false; +/* Return the one of two successors of BB that is not reachable by a + complex edge, if there is one. Else, return BB. We use + this in optimizations that use post-dominators for their heuristics, + to catch the cases in C++ where function calls are involved. */ - else if (data->last_was_goto - && gimple_goto_dest (gsi_stmt (data->last_goto_gsi)) == label) - { - /* Replace the preceding GIMPLE_GOTO statement with - a GIMPLE_NOP, which will be subsequently removed. - In this way, we avoid invalidating other iterators - active on the statement sequence. */ - gsi_replace(&data->last_goto_gsi, gimple_build_nop(), false); - data->last_was_goto = false; - data->repeat = true; - } +basic_block +single_noncomplex_succ (basic_block bb) +{ + edge e0, e1; + if (EDGE_COUNT (bb->succs) != 2) + return bb; - /* ??? Add something here to delete unused labels. */ + 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; - gsi_next (gsi); + return bb; } - /* T is CALL_EXPR. Set current_function_calls_* flags. */ void @@ -1895,195 +1755,6 @@ clear_special_calls (void) cfun->calls_setjmp = false; } -/* Remove useless statements from a statement sequence, and perform - some preliminary simplifications. */ - -static void -remove_useless_stmts_1 (gimple_stmt_iterator *gsi, struct rus_data *data) -{ - while (!gsi_end_p (*gsi)) - { - gimple stmt = gsi_stmt (*gsi); - - switch (gimple_code (stmt)) - { - case GIMPLE_COND: - remove_useless_stmts_cond (gsi, data); - break; - - case GIMPLE_GOTO: - remove_useless_stmts_goto (gsi, data); - break; - - case GIMPLE_LABEL: - remove_useless_stmts_label (gsi, data); - break; - - case GIMPLE_ASSIGN: - fold_stmt (gsi); - stmt = gsi_stmt (*gsi); - data->last_was_goto = false; - if (stmt_could_throw_p (stmt)) - data->may_throw = true; - gsi_next (gsi); - break; - - case GIMPLE_ASM: - fold_stmt (gsi); - data->last_was_goto = false; - gsi_next (gsi); - break; - - case GIMPLE_CALL: - fold_stmt (gsi); - stmt = gsi_stmt (*gsi); - data->last_was_goto = false; - if (is_gimple_call (stmt)) - notice_special_calls (stmt); - - /* We used to call update_gimple_call_flags here, - which copied side-effects and nothrows status - from the function decl to the call. In the new - tuplified GIMPLE, the accessors for this information - always consult the function decl, so this copying - is no longer necessary. */ - if (stmt_could_throw_p (stmt)) - data->may_throw = true; - gsi_next (gsi); - break; - - case GIMPLE_RETURN: - fold_stmt (gsi); - data->last_was_goto = false; - data->may_branch = true; - gsi_next (gsi); - break; - - case GIMPLE_BIND: - remove_useless_stmts_bind (gsi, data); - break; - - case GIMPLE_TRY: - if (gimple_try_kind (stmt) == GIMPLE_TRY_CATCH) - remove_useless_stmts_tc (gsi, data); - else if (gimple_try_kind (stmt) == GIMPLE_TRY_FINALLY) - remove_useless_stmts_tf (gsi, data); - else - gcc_unreachable (); - break; - - case GIMPLE_CATCH: - gcc_unreachable (); - break; - - case GIMPLE_NOP: - gsi_remove (gsi, false); - break; - - case GIMPLE_OMP_FOR: - { - gimple_seq pre_body_seq = gimple_omp_for_pre_body (stmt); - gimple_stmt_iterator pre_body_gsi = gsi_start (pre_body_seq); - - remove_useless_stmts_1 (&pre_body_gsi, data); - data->last_was_goto = false; - } - /* FALLTHROUGH */ - case GIMPLE_OMP_CRITICAL: - case GIMPLE_OMP_CONTINUE: - case GIMPLE_OMP_MASTER: - case GIMPLE_OMP_ORDERED: - case GIMPLE_OMP_SECTION: - case GIMPLE_OMP_SECTIONS: - case GIMPLE_OMP_SINGLE: - { - gimple_seq body_seq = gimple_omp_body (stmt); - gimple_stmt_iterator body_gsi = gsi_start (body_seq); - - remove_useless_stmts_1 (&body_gsi, data); - data->last_was_goto = false; - gsi_next (gsi); - } - break; - - case GIMPLE_OMP_PARALLEL: - case GIMPLE_OMP_TASK: - { - /* Make sure the outermost GIMPLE_BIND isn't removed - as useless. */ - gimple_seq body_seq = gimple_omp_body (stmt); - gimple bind = gimple_seq_first_stmt (body_seq); - gimple_seq bind_seq = gimple_bind_body (bind); - gimple_stmt_iterator bind_gsi = gsi_start (bind_seq); - - remove_useless_stmts_1 (&bind_gsi, data); - data->last_was_goto = false; - gsi_next (gsi); - } - break; - - case GIMPLE_CHANGE_DYNAMIC_TYPE: - /* If we do not optimize remove GIMPLE_CHANGE_DYNAMIC_TYPE as - expansion is confused about them and we only remove them - during alias computation otherwise. */ - if (!optimize) - { - data->last_was_goto = false; - gsi_remove (gsi, false); - break; - } - /* Fallthru. */ - - default: - data->last_was_goto = false; - gsi_next (gsi); - break; - } - } -} - -/* Walk the function tree, removing useless statements and performing - some preliminary simplifications. */ - -static unsigned int -remove_useless_stmts (void) -{ - struct rus_data data; - - clear_special_calls (); - - do - { - gimple_stmt_iterator gsi; - - gsi = gsi_start (gimple_body (current_function_decl)); - memset (&data, 0, sizeof (data)); - remove_useless_stmts_1 (&gsi, &data); - } - while (data.repeat); - return 0; -} - - -struct gimple_opt_pass pass_remove_useless_stmts = -{ - { - GIMPLE_PASS, - "useless", /* name */ - NULL, /* gate */ - remove_useless_stmts, /* execute */ - NULL, /* sub */ - NULL, /* next */ - 0, /* static_pass_number */ - 0, /* tv_id */ - PROP_gimple_any, /* properties_required */ - 0, /* properties_provided */ - 0, /* properties_destroyed */ - 0, /* todo_flags_start */ - TODO_dump_func /* todo_flags_finish */ - } -}; - /* Remove PHI nodes associated with basic block BB and all edges out of BB. */ static void @@ -2105,7 +1776,6 @@ static void remove_bb (basic_block bb) { gimple_stmt_iterator i; - source_location loc = UNKNOWN_LOCATION; if (dump_file) { @@ -2131,7 +1801,11 @@ remove_bb (basic_block bb) /* Remove all the instructions in the block. */ if (bb_seq (bb) != NULL) { - for (i = gsi_start_bb (bb); !gsi_end_p (i);) + /* Walk backwards so as to get a chance to substitute all + released DEFs into debug stmts. See + eliminate_unnecessary_stmts() in tree-ssa-dce.c for more + details. */ + for (i = gsi_last_bb (bb); !gsi_end_p (i);) { gimple stmt = gsi_stmt (i); if (gimple_code (stmt) == GIMPLE_LABEL @@ -2167,24 +1841,13 @@ remove_bb (basic_block bb) gsi_remove (&i, true); } - /* Don't warn for removed gotos. Gotos are often removed due to - jump threading, thus resulting in bogus warnings. Not great, - since this way we lose warnings for gotos in the original - program that are indeed unreachable. */ - if (gimple_code (stmt) != GIMPLE_GOTO - && gimple_has_location (stmt) - && !loc) - loc = gimple_location (stmt); + if (gsi_end_p (i)) + i = gsi_last_bb (bb); + else + gsi_prev (&i); } } - /* If requested, give a warning that the first statement in the - block is unreachable. We walk statements backwards in the - loop above, so the last statement we process is the first statement - in the block. */ - if (loc > BUILTINS_LOCATION && LOCATION_LINE (loc) > 0) - warning (OPT_Wunreachable_code, "%Hwill never be executed", &loc); - remove_phi_nodes_and_edges_for_unreachable_block (bb); bb->il.gimple = NULL; } @@ -2549,11 +2212,17 @@ gimple_cfg2vcg (FILE *file) bool is_ctrl_stmt (gimple t) { - return gimple_code (t) == GIMPLE_COND - || gimple_code (t) == GIMPLE_SWITCH - || gimple_code (t) == GIMPLE_GOTO - || gimple_code (t) == GIMPLE_RETURN - || gimple_code (t) == GIMPLE_RESX; + switch (gimple_code (t)) + { + case GIMPLE_COND: + case GIMPLE_SWITCH: + case GIMPLE_GOTO: + case GIMPLE_RETURN: + case GIMPLE_RESX: + return true; + default: + return false; + } } @@ -2565,24 +2234,41 @@ is_ctrl_altering_stmt (gimple t) { gcc_assert (t); - if (is_gimple_call (t)) + switch (gimple_code (t)) { - int flags = gimple_call_flags (t); + case GIMPLE_CALL: + { + int flags = gimple_call_flags (t); - /* A non-pure/const call alters flow control if the current - function has nonlocal labels. */ - if (!(flags & (ECF_CONST | ECF_PURE)) - && cfun->has_nonlocal_label) - return true; + /* A non-pure/const call alters flow control if the current + function has nonlocal labels. */ + if (!(flags & (ECF_CONST | ECF_PURE)) && cfun->has_nonlocal_label) + return true; + + /* A call also alters control flow if it does not return. */ + if (flags & ECF_NORETURN) + return true; + } + break; + + case GIMPLE_EH_DISPATCH: + /* EH_DISPATCH branches to the individual catch handlers at + this level of a try or allowed-exceptions region. It can + fallthru to the next statement as well. */ + return true; - /* A call also alters control flow if it does not return. */ - if (gimple_call_flags (t) & ECF_NORETURN) + case GIMPLE_ASM: + if (gimple_asm_nlabels (t) > 0) return true; - } + break; - /* OpenMP directives alter control flow. */ - if (is_gimple_omp (t)) - return true; + CASE_GIMPLE_OMP: + /* OpenMP directives alter control flow. */ + return true; + + default: + break; + } /* If a statement can throw, it alters control flow. */ return stmt_can_throw_internal (t); @@ -2675,6 +2361,24 @@ gimple first_stmt (basic_block bb) { gimple_stmt_iterator i = gsi_start_bb (bb); + gimple stmt = NULL; + + while (!gsi_end_p (i) && is_gimple_debug ((stmt = gsi_stmt (i)))) + { + gsi_next (&i); + stmt = NULL; + } + return stmt; +} + +/* Return the first non-label statement in basic block BB. */ + +static gimple +first_non_label_stmt (basic_block bb) +{ + gimple_stmt_iterator i = gsi_start_bb (bb); + while (!gsi_end_p (i) && gimple_code (gsi_stmt (i)) == GIMPLE_LABEL) + gsi_next (&i); return !gsi_end_p (i) ? gsi_stmt (i) : NULL; } @@ -2683,8 +2387,15 @@ first_stmt (basic_block bb) gimple last_stmt (basic_block bb) { - gimple_stmt_iterator b = gsi_last_bb (bb); - return !gsi_end_p (b) ? gsi_stmt (b) : NULL; + gimple_stmt_iterator i = gsi_last_bb (bb); + gimple stmt = NULL; + + while (!gsi_end_p (i) && is_gimple_debug ((stmt = gsi_stmt (i)))) + { + gsi_prev (&i); + stmt = NULL; + } + return stmt; } /* Return the last statement of an otherwise empty block. Return NULL @@ -2694,14 +2405,14 @@ last_stmt (basic_block bb) gimple last_and_only_stmt (basic_block bb) { - gimple_stmt_iterator i = gsi_last_bb (bb); + gimple_stmt_iterator i = gsi_last_nondebug_bb (bb); gimple last, prev; if (gsi_end_p (i)) return NULL; last = gsi_stmt (i); - gsi_prev (&i); + gsi_prev_nondebug (&i); if (gsi_end_p (i)) return last; @@ -2728,11 +2439,11 @@ reinstall_phi_args (edge new_edge, edge old_edge) edge_var_map *vm; int i; gimple_stmt_iterator phis; - + v = redirect_edge_var_map_vector (old_edge); if (!v) return; - + for (i = 0, phis = gsi_start_phis (new_edge->dest); VEC_iterate (edge_var_map, v, i, vm) && !gsi_end_p (phis); i++, gsi_next (&phis)) @@ -2740,12 +2451,12 @@ reinstall_phi_args (edge new_edge, edge old_edge) gimple phi = gsi_stmt (phis); tree result = redirect_edge_var_map_result (vm); tree arg = redirect_edge_var_map_def (vm); - + gcc_assert (result == gimple_phi_result (phi)); - - add_phi_arg (phi, arg, new_edge); + + add_phi_arg (phi, arg, new_edge, redirect_edge_var_map_location (vm)); } - + redirect_edge_var_map_clear (old_edge); } @@ -2758,11 +2469,15 @@ static basic_block split_edge_bb_loc (edge edge_in) { basic_block dest = edge_in->dest; + basic_block dest_prev = dest->prev_bb; - if (dest->prev_bb && find_edge (dest->prev_bb, dest)) - return edge_in->src; - else - return dest->prev_bb; + if (dest_prev) + { + edge e = find_edge (dest_prev, dest); + if (e && !(e->flags & EDGE_COMPLEX)) + return edge_in->src; + } + return dest_prev; } /* Split a (typically critical) edge EDGE_IN. Return the new block. @@ -3107,11 +2822,12 @@ verify_types_in_gimple_min_lval (tree expr) return false; } -/* Verify if EXPR is a valid GIMPLE reference expression. Returns true +/* Verify if EXPR is a valid GIMPLE reference expression. If + REQUIRE_LVALUE is true verifies it is an lvalue. Returns true if there is an error, otherwise false. */ static bool -verify_types_in_gimple_reference (tree expr) +verify_types_in_gimple_reference (tree expr, bool require_lvalue) { while (handled_component_p (expr)) { @@ -3173,17 +2889,30 @@ verify_types_in_gimple_reference (tree expr) return true; } - /* For VIEW_CONVERT_EXPRs which are allowed here, too, there - is nothing to verify. Gross mismatches at most invoke - undefined behavior. */ - if (TREE_CODE (expr) == VIEW_CONVERT_EXPR - && !handled_component_p (op)) - return false; + if (TREE_CODE (expr) == VIEW_CONVERT_EXPR) + { + /* For VIEW_CONVERT_EXPRs which are allowed here too, we only check + that their operand is not an SSA name or an invariant when + requiring an lvalue (this usually means there is a SRA or IPA-SRA + bug). Otherwise there is nothing to verify, gross mismatches at + most invoke undefined behavior. */ + if (require_lvalue + && (TREE_CODE (op) == SSA_NAME + || is_gimple_min_invariant (op))) + { + error ("Conversion of an SSA_NAME on the left hand side."); + debug_generic_stmt (expr); + return true; + } + else if (!handled_component_p (op)) + return false; + } expr = op; } - return verify_types_in_gimple_min_lval (expr); + return ((require_lvalue || !is_gimple_min_invariant (expr)) + && verify_types_in_gimple_min_lval (expr)); } /* Returns true if there is one pointer type in TYPE_POINTER_TO (SRC_OBJ) @@ -3224,6 +2953,15 @@ verify_gimple_call (gimple stmt) { tree fn = gimple_call_fn (stmt); tree fntype; + unsigned i; + + if (TREE_CODE (fn) != OBJ_TYPE_REF + && !is_gimple_val (fn)) + { + error ("invalid function in gimple call"); + debug_generic_stmt (fn); + return true; + } if (!POINTER_TYPE_P (TREE_TYPE (fn)) || (TREE_CODE (TREE_TYPE (TREE_TYPE (fn))) != FUNCTION_TYPE @@ -3234,12 +2972,19 @@ verify_gimple_call (gimple stmt) } if (gimple_call_lhs (stmt) - && !is_gimple_lvalue (gimple_call_lhs (stmt))) + && (!is_gimple_lvalue (gimple_call_lhs (stmt)) + || verify_types_in_gimple_reference (gimple_call_lhs (stmt), true))) { error ("invalid LHS in gimple call"); return true; } + if (gimple_call_lhs (stmt) && gimple_call_noreturn_p (stmt)) + { + error ("LHS in noreturn call"); + return true; + } + fntype = TREE_TYPE (TREE_TYPE (fn)); if (gimple_call_lhs (stmt) && !useless_type_conversion_p (TREE_TYPE (gimple_call_lhs (stmt)), @@ -3258,11 +3003,48 @@ verify_gimple_call (gimple stmt) return true; } + if (gimple_call_chain (stmt) + && !is_gimple_val (gimple_call_chain (stmt))) + { + error ("invalid static chain in gimple call"); + debug_generic_stmt (gimple_call_chain (stmt)); + return true; + } + + /* If there is a static chain argument, this should not be an indirect + call, and the decl should have DECL_STATIC_CHAIN set. */ + if (gimple_call_chain (stmt)) + { + if (TREE_CODE (fn) != ADDR_EXPR + || TREE_CODE (TREE_OPERAND (fn, 0)) != FUNCTION_DECL) + { + error ("static chain in indirect gimple call"); + return true; + } + fn = TREE_OPERAND (fn, 0); + + if (!DECL_STATIC_CHAIN (fn)) + { + error ("static chain with function that doesn't use one"); + return true; + } + } + /* ??? The C frontend passes unpromoted arguments in case it didn't see a function declaration before the call. So for now - leave the call arguments unverified. Once we gimplify + leave the call arguments mostly unverified. Once we gimplify unit-at-a-time we have a chance to fix this. */ + for (i = 0; i < gimple_call_num_args (stmt); ++i) + { + tree arg = gimple_call_arg (stmt, i); + if (!is_gimple_operand (arg)) + { + error ("invalid argument to gimple call"); + debug_generic_expr (arg); + } + } + return false; } @@ -3371,6 +3153,21 @@ verify_gimple_assign_unary (gimple stmt) return false; } + case ADDR_SPACE_CONVERT_EXPR: + { + if (!POINTER_TYPE_P (rhs1_type) || !POINTER_TYPE_P (lhs_type) + || (TYPE_ADDR_SPACE (TREE_TYPE (rhs1_type)) + == TYPE_ADDR_SPACE (TREE_TYPE (lhs_type)))) + { + error ("invalid types in address space conversion"); + debug_generic_expr (lhs_type); + debug_generic_expr (rhs1_type); + return true; + } + + return false; + } + case FIXED_CONVERT_EXPR: { if (!valid_fixed_convert_types_p (lhs_type, rhs1_type) @@ -3506,13 +3303,13 @@ verify_gimple_assign_binary (gimple stmt) if ((!INTEGRAL_TYPE_P (rhs1_type) && !FIXED_POINT_TYPE_P (rhs1_type) && !(TREE_CODE (rhs1_type) == VECTOR_TYPE - && TREE_CODE (TREE_TYPE (rhs1_type)) == INTEGER_TYPE)) + && INTEGRAL_TYPE_P (TREE_TYPE (rhs1_type)))) || (!INTEGRAL_TYPE_P (rhs2_type) /* Vector shifts of vectors are also ok. */ && !(TREE_CODE (rhs1_type) == VECTOR_TYPE - && TREE_CODE (TREE_TYPE (rhs1_type)) == INTEGER_TYPE + && INTEGRAL_TYPE_P (TREE_TYPE (rhs1_type)) && TREE_CODE (rhs2_type) == VECTOR_TYPE - && TREE_CODE (TREE_TYPE (rhs2_type)) == INTEGER_TYPE)) + && INTEGRAL_TYPE_P (TREE_TYPE (rhs2_type)))) || !useless_type_conversion_p (lhs_type, rhs1_type)) { error ("type mismatch in shift expression"); @@ -3530,7 +3327,8 @@ verify_gimple_assign_binary (gimple stmt) { if (TREE_CODE (rhs1_type) != VECTOR_TYPE || !(INTEGRAL_TYPE_P (TREE_TYPE (rhs1_type)) - || FIXED_POINT_TYPE_P (TREE_TYPE (rhs1_type))) + || FIXED_POINT_TYPE_P (TREE_TYPE (rhs1_type)) + || SCALAR_FLOAT_TYPE_P (TREE_TYPE (rhs1_type))) || (!INTEGRAL_TYPE_P (rhs2_type) && (TREE_CODE (rhs2_type) != VECTOR_TYPE || !INTEGRAL_TYPE_P (TREE_TYPE (rhs2_type)))) @@ -3542,12 +3340,66 @@ verify_gimple_assign_binary (gimple stmt) debug_generic_expr (rhs2_type); return true; } + /* For shifting a vector of floating point components we + only allow shifting by a constant multiple of the element size. */ + if (SCALAR_FLOAT_TYPE_P (TREE_TYPE (rhs1_type)) + && (TREE_CODE (rhs2) != INTEGER_CST + || !div_if_zero_remainder (EXACT_DIV_EXPR, rhs2, + TYPE_SIZE (TREE_TYPE (rhs1_type))))) + { + error ("non-element sized vector shift of floating point vector"); + return true; + } return false; } + case PLUS_EXPR: + { + /* We use regular PLUS_EXPR for vectors. + ??? This just makes the checker happy and may not be what is + intended. */ + if (TREE_CODE (lhs_type) == VECTOR_TYPE + && POINTER_TYPE_P (TREE_TYPE (lhs_type))) + { + if (TREE_CODE (rhs1_type) != VECTOR_TYPE + || TREE_CODE (rhs2_type) != VECTOR_TYPE) + { + error ("invalid non-vector operands to vector valued plus"); + return true; + } + lhs_type = TREE_TYPE (lhs_type); + rhs1_type = TREE_TYPE (rhs1_type); + rhs2_type = TREE_TYPE (rhs2_type); + /* PLUS_EXPR is commutative, so we might end up canonicalizing + the pointer to 2nd place. */ + if (POINTER_TYPE_P (rhs2_type)) + { + tree tem = rhs1_type; + rhs1_type = rhs2_type; + rhs2_type = tem; + } + goto do_pointer_plus_expr_check; + } + } + /* Fallthru. */ + case MINUS_EXPR: + { + if (POINTER_TYPE_P (lhs_type) + || POINTER_TYPE_P (rhs1_type) + || POINTER_TYPE_P (rhs2_type)) + { + error ("invalid (pointer) operands to plus/minus"); + return true; + } + + /* Continue with generic binary expression handling. */ + break; + } + case POINTER_PLUS_EXPR: { +do_pointer_plus_expr_check: if (!POINTER_TYPE_P (rhs1_type) || !useless_type_conversion_p (lhs_type, rhs1_type) || !useless_type_conversion_p (sizetype, rhs2_type)) @@ -3560,7 +3412,7 @@ verify_gimple_assign_binary (gimple stmt) } return false; - } + } case TRUTH_ANDIF_EXPR: case TRUTH_ORIF_EXPR: @@ -3603,23 +3455,13 @@ verify_gimple_assign_binary (gimple stmt) connected to the operand types. */ return verify_gimple_comparison (lhs_type, rhs1, rhs2); - case PLUS_EXPR: - case MINUS_EXPR: - { - if (POINTER_TYPE_P (lhs_type) - || POINTER_TYPE_P (rhs1_type) - || POINTER_TYPE_P (rhs2_type)) - { - error ("invalid (pointer) operands to plus/minus"); - return true; - } - - /* Continue with generic binary expression handling. */ - break; - } + case WIDEN_MULT_EXPR: + if (TREE_CODE (lhs_type) != INTEGER_TYPE) + return true; + return ((2 * TYPE_PRECISION (rhs1_type) != TYPE_PRECISION (lhs_type)) + || (TYPE_PRECISION (rhs1_type) != TYPE_PRECISION (rhs2_type))); case WIDEN_SUM_EXPR: - case WIDEN_MULT_EXPR: case VEC_WIDEN_MULT_HI_EXPR: case VEC_WIDEN_MULT_LO_EXPR: case VEC_PACK_TRUNC_EXPR: @@ -3690,7 +3532,7 @@ verify_gimple_assign_single (gimple stmt) } if (handled_component_p (lhs)) - res |= verify_types_in_gimple_reference (lhs); + res |= verify_types_in_gimple_reference (lhs, true); /* Special codes we cannot handle via their class. */ switch (rhs_code) @@ -3704,16 +3546,17 @@ verify_gimple_assign_single (gimple stmt) return true; } - if (!one_pointer_to_useless_type_conversion_p (lhs_type, - TREE_TYPE (op))) + if (!types_compatible_p (TREE_TYPE (op), TREE_TYPE (TREE_TYPE (rhs1))) + && !one_pointer_to_useless_type_conversion_p (TREE_TYPE (rhs1), + TREE_TYPE (op))) { error ("type mismatch in address expression"); - debug_generic_stmt (lhs_type); - debug_generic_stmt (TYPE_POINTER_TO (TREE_TYPE (op))); + debug_generic_stmt (TREE_TYPE (rhs1)); + debug_generic_stmt (TREE_TYPE (op)); return true; } - return verify_types_in_gimple_reference (op); + return verify_types_in_gimple_reference (op, true); } /* tcc_reference */ @@ -3736,7 +3579,7 @@ verify_gimple_assign_single (gimple stmt) debug_generic_stmt (rhs1); return true; } - return res || verify_types_in_gimple_reference (rhs1); + return res || verify_types_in_gimple_reference (rhs1, false); /* tcc_constant */ case SSA_NAME: @@ -3769,8 +3612,6 @@ verify_gimple_assign_single (gimple stmt) case OBJ_TYPE_REF: case ASSERT_EXPR: case WITH_SIZE_EXPR: - case EXC_PTR_EXPR: - case FILTER_EXPR: case POLYNOMIAL_CHREC: case DOT_PROD_EXPR: case VEC_COND_EXPR: @@ -3819,7 +3660,7 @@ verify_gimple_return (gimple stmt) return values from the original source. */ if (op == NULL) return false; - + if (!is_gimple_val (op) && TREE_CODE (op) != RESULT_DECL) { @@ -3892,7 +3733,7 @@ verify_gimple_phi (gimple stmt) tree type = TREE_TYPE (gimple_phi_result (stmt)); unsigned i; - if (!is_gimple_variable (gimple_phi_result (stmt))) + if (TREE_CODE (gimple_phi_result (stmt)) != SSA_NAME) { error ("Invalid PHI result"); return true; @@ -3923,23 +3764,28 @@ verify_gimple_phi (gimple stmt) } +/* Verify a gimple debug statement STMT. + Returns true if anything is wrong. */ + +static bool +verify_gimple_debug (gimple stmt ATTRIBUTE_UNUSED) +{ + /* There isn't much that could be wrong in a gimple debug stmt. A + gimple debug bind stmt, for example, maps a tree, that's usually + a VAR_DECL or a PARM_DECL, but that could also be some scalarized + component or member of an aggregate type, to another tree, that + can be an arbitrary expression. These stmts expand into debug + insns, and are converted to debug notes by var-tracking.c. */ + return false; +} + + /* Verify the GIMPLE statement STMT. Returns true if there is an error, otherwise false. */ static bool verify_types_in_gimple_stmt (gimple stmt) { - if (is_gimple_omp (stmt)) - { - /* OpenMP directives are validated by the FE and never operated - on by the optimizers. Furthermore, GIMPLE_OMP_FOR may contain - non-gimple expressions when the main index variable has had - its address taken. This does not affect the loop itself - because the header of an GIMPLE_OMP_FOR is merely used to determine - how to setup the parallel iteration. */ - return false; - } - switch (gimple_code (stmt)) { case GIMPLE_ASSIGN: @@ -3952,6 +3798,20 @@ verify_types_in_gimple_stmt (gimple stmt) return verify_gimple_call (stmt); case GIMPLE_COND: + if (TREE_CODE_CLASS (gimple_cond_code (stmt)) != tcc_comparison) + { + error ("invalid comparison code in gimple cond"); + return true; + } + if (!(!gimple_cond_true_label (stmt) + || TREE_CODE (gimple_cond_true_label (stmt)) == LABEL_DECL) + || !(!gimple_cond_false_label (stmt) + || TREE_CODE (gimple_cond_false_label (stmt)) == LABEL_DECL)) + { + error ("invalid labels in gimple cond"); + return true; + } + return verify_gimple_comparison (boolean_type_node, gimple_cond_lhs (stmt), gimple_cond_rhs (stmt)); @@ -3968,19 +3828,29 @@ verify_types_in_gimple_stmt (gimple stmt) case GIMPLE_ASM: return false; - case GIMPLE_CHANGE_DYNAMIC_TYPE: - return (!is_gimple_val (gimple_cdt_location (stmt)) - || !POINTER_TYPE_P (TREE_TYPE (gimple_cdt_location (stmt)))); - case GIMPLE_PHI: return verify_gimple_phi (stmt); /* Tuples that do not have tree operands. */ case GIMPLE_NOP: - case GIMPLE_RESX: case GIMPLE_PREDICT: + case GIMPLE_RESX: + case GIMPLE_EH_DISPATCH: + case GIMPLE_EH_MUST_NOT_THROW: return false; + CASE_GIMPLE_OMP: + /* OpenMP directives are validated by the FE and never operated + on by the optimizers. Furthermore, GIMPLE_OMP_FOR may contain + non-gimple expressions when the main index variable has had + its address taken. This does not affect the loop itself + because the header of an GIMPLE_OMP_FOR is merely used to determine + how to setup the parallel iteration. */ + return false; + + case GIMPLE_DEBUG: + return verify_gimple_debug (stmt); + default: gcc_unreachable (); } @@ -4051,6 +3921,7 @@ verify_stmt (gimple_stmt_iterator *gsi) struct walk_stmt_info wi; bool last_in_block = gsi_one_before_end_p (*gsi); gimple stmt = gsi_stmt (*gsi); + int lp_nr; if (is_gimple_omp (stmt)) { @@ -4087,12 +3958,15 @@ verify_stmt (gimple_stmt_iterator *gsi) } } + if (is_gimple_debug (stmt)) + return false; + memset (&wi, 0, sizeof (wi)); addr = walk_gimple_op (gsi_stmt (*gsi), verify_expr, &wi); if (addr) { debug_generic_expr (addr); - inform (input_location, "in statement"); + inform (gimple_location (gsi_stmt (*gsi)), "in statement"); debug_gimple_stmt (stmt); return true; } @@ -4102,14 +3976,21 @@ verify_stmt (gimple_stmt_iterator *gsi) have optimizations that simplify statements such that we prove that they cannot throw, that we update other data structures to match. */ - if (lookup_stmt_eh_region (stmt) >= 0) + lp_nr = lookup_stmt_eh_lp (stmt); + if (lp_nr != 0) { if (!stmt_could_throw_p (stmt)) { - error ("statement marked for throw, but doesn%'t"); - goto fail; + /* During IPA passes, ipa-pure-const sets nothrow flags on calls + and they are updated on statements only after fixup_cfg + is executed at beggining of expansion stage. */ + if (cgraph_state != CGRAPH_STATE_IPA_SSA) + { + error ("statement marked for throw, but doesn%'t"); + goto fail; + } } - if (!last_in_block && stmt_can_throw_internal (stmt)) + else if (lp_nr > 0 && !last_in_block && stmt_can_throw_internal (stmt)) { error ("statement marked for throw in middle of block"); goto fail; @@ -4126,7 +4007,7 @@ verify_stmt (gimple_stmt_iterator *gsi) /* Return true when the T can be shared. */ -static bool +bool tree_node_can_be_shared (tree t) { if (IS_TYPE_OR_DECL_P (t) @@ -4258,6 +4139,14 @@ verify_stmts (void) err |= true; } } + +#ifdef ENABLE_TYPES_CHECKING + if (verify_gimple_phi (phi)) + { + debug_gimple_stmt (phi); + err |= true; + } +#endif } for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); ) @@ -4277,6 +4166,7 @@ verify_stmts (void) if (gimple_bb (stmt) != bb) { error ("gimple_bb (stmt) is set to a wrong basic block"); + debug_gimple_stmt (stmt); err |= true; } @@ -4288,12 +4178,31 @@ verify_stmts (void) if (uid == -1 || VEC_index (basic_block, label_to_block_map, uid) != bb) { - error ("incorrect entry in label_to_block_map.\n"); + error ("incorrect entry in label_to_block_map"); err |= true; } + + uid = EH_LANDING_PAD_NR (decl); + if (uid) + { + eh_landing_pad lp = get_eh_landing_pad_from_number (uid); + if (decl != lp->post_landing_pad) + { + error ("incorrect setting of landing pad number"); + err |= true; + } + } } err |= verify_stmt (&gsi); + +#ifdef ENABLE_TYPES_CHECKING + if (verify_types_in_gimple_stmt (gsi_stmt (gsi))) + { + debug_gimple_stmt (stmt); + err |= true; + } +#endif addr = walk_gimple_op (gsi_stmt (gsi), verify_node_sharing, &wi); if (addr) { @@ -4380,6 +4289,15 @@ gimple_verify_flow_info (void) err = 1; } + if (prev_stmt && EH_LANDING_PAD_NR (label) != 0) + { + error ("EH landing pad label "); + print_generic_expr (stderr, label, 0); + fprintf (stderr, " is not first in a sequence of labels in bb %d", + bb->index); + err = 1; + } + if (label_to_block (label) != bb) { error ("label "); @@ -4429,6 +4347,9 @@ gimple_verify_flow_info (void) stmt = gsi_stmt (gsi); + if (gimple_code (stmt) == GIMPLE_LABEL) + continue; + err |= verify_eh_edges (stmt); if (is_ctrl_stmt (stmt)) @@ -4461,7 +4382,7 @@ gimple_verify_flow_info (void) { edge true_edge; edge false_edge; - + extract_true_false_edges_from_block (bb, &true_edge, &false_edge); if (!true_edge @@ -4598,8 +4519,14 @@ gimple_verify_flow_info (void) FOR_EACH_EDGE (e, ei, bb->succs) e->dest->aux = (void *)0; } + break; + + case GIMPLE_EH_DISPATCH: + err |= verify_eh_dispatch_edge (stmt); + break; - default: ; + default: + break; } } @@ -4633,13 +4560,14 @@ gimple_make_forwarder_block (edge fallthru) for (gsi = gsi_start_phis (dummy); !gsi_end_p (gsi); gsi_next (&gsi)) { gimple phi, new_phi; - + phi = gsi_stmt (gsi); var = gimple_phi_result (phi); new_phi = create_phi_node (var, bb); SSA_NAME_DEF_STMT (var) = new_phi; gimple_phi_set_result (phi, make_ssa_name (SSA_NAME_VAR (var), phi)); - add_phi_arg (new_phi, gimple_phi_result (phi), fallthru); + add_phi_arg (new_phi, gimple_phi_result (phi), fallthru, + UNKNOWN_LOCATION); } /* Add the arguments we have stored on edges. */ @@ -4678,7 +4606,7 @@ gimple_block_label (basic_block bb) } } - label = create_artificial_label (); + label = create_artificial_label (UNKNOWN_LOCATION); stmt = gimple_build_label (label); gsi_insert_before (&s, stmt, GSI_NEW_STMT); return label; @@ -4738,17 +4666,23 @@ gimple_redirect_edge_and_branch (edge e, basic_block dest) if (e->flags & EDGE_ABNORMAL) return NULL; - if (e->src != ENTRY_BLOCK_PTR - && (ret = gimple_try_redirect_by_replacing_jump (e, dest))) - return ret; - if (e->dest == dest) return NULL; + if (e->flags & EDGE_EH) + return redirect_eh_edge (e, dest); + + if (e->src != ENTRY_BLOCK_PTR) + { + ret = gimple_try_redirect_by_replacing_jump (e, dest); + if (ret) + return ret; + } + gsi = gsi_last_bb (bb); stmt = gsi_end_p (gsi) ? NULL : gsi_stmt (gsi); - switch (stmt ? gimple_code (stmt) : ERROR_MARK) + switch (stmt ? gimple_code (stmt) : GIMPLE_ERROR_MARK) { case GIMPLE_COND: /* For COND_EXPR, we only need to redirect the edge. */ @@ -4788,6 +4722,7 @@ gimple_redirect_edge_and_branch (edge e, basic_block dest) TREE_CHAIN (last) = TREE_CHAIN (cases2); TREE_CHAIN (cases2) = first; } + bitmap_set_bit (touched_switch_bbs, gimple_bb (stmt)->index); } else { @@ -4800,9 +4735,31 @@ gimple_redirect_edge_and_branch (edge e, basic_block dest) CASE_LABEL (elt) = label; } } + } + break; - break; + case GIMPLE_ASM: + { + int i, n = gimple_asm_nlabels (stmt); + tree label = NULL; + + for (i = 0; i < n; ++i) + { + tree cons = gimple_asm_label_op (stmt, i); + if (label_to_block (TREE_VALUE (cons)) == e->dest) + { + if (!label) + label = gimple_block_label (dest); + TREE_VALUE (cons) = label; + } + } + + /* If we didn't find any label matching the former edge in the + asm labels, we must be redirecting the fallthrough + edge. */ + gcc_assert (label || (e->flags & EDGE_FALLTHRU)); } + break; case GIMPLE_RETURN: gsi_remove (&gsi, true); @@ -4816,6 +4773,11 @@ gimple_redirect_edge_and_branch (edge e, basic_block dest) /* The edges from OMP constructs can be simply redirected. */ break; + case GIMPLE_EH_DISPATCH: + if (!(e->flags & EDGE_FALLTHRU)) + redirect_eh_dispatch_edge (stmt, e, dest); + break; + default: /* Otherwise it must be a fallthru edge, and we don't need to do anything besides redirecting it. */ @@ -4837,7 +4799,7 @@ gimple_redirect_edge_and_branch (edge e, basic_block dest) static bool gimple_can_remove_branch_p (const_edge e) { - if (e->flags & EDGE_ABNORMAL) + if (e->flags & (EDGE_ABNORMAL | EDGE_EH)) return false; return true; @@ -4901,7 +4863,7 @@ gimple_split_block (basic_block bb, void *stmt) return new_bb; /* Split the statement list - avoid re-creating new containers as this - brings ugly quadratic memory consumption in the inliner. + brings ugly quadratic memory consumption in the inliner. (We are still quadratic since we need to update stmt BB pointers, sadly.) */ list = gsi_split_seq_before (&gsi); @@ -4965,7 +4927,6 @@ gimple_duplicate_bb (basic_block bb) { def_operand_p def_p; ssa_op_iter op_iter; - int region; stmt = gsi_stmt (gsi); if (gimple_code (stmt) == GIMPLE_LABEL) @@ -4975,9 +4936,8 @@ gimple_duplicate_bb (basic_block bb) operands. */ copy = gimple_copy (stmt); gsi_insert_after (&gsi_tgt, copy, GSI_NEW_STMT); - region = lookup_stmt_eh_region (stmt); - if (region >= 0) - add_stmt_to_eh_region (copy, region); + + maybe_duplicate_eh_stmt (copy, stmt); gimple_duplicate_stmt_histograms (cfun, copy, cfun, stmt); /* Create new names for all the definitions created by COPY and @@ -5035,7 +4995,8 @@ add_phi_args_after_copy_edge (edge e_copy) phi = gsi_stmt (psi); phi_copy = gsi_stmt (psi_copy); def = PHI_ARG_DEF_FROM_EDGE (phi, e); - add_phi_arg (phi_copy, def, e_copy); + add_phi_arg (phi_copy, def, e_copy, + gimple_phi_arg_location_from_edge (phi, e)); } } @@ -5233,8 +5194,8 @@ gimple_duplicate_sese_region (edge entry, edge exit, is moved to ENTRY. Returns true if duplication succeeds, false otherwise. - For example, - + For example, + some_code; if (cond) A; @@ -5269,9 +5230,15 @@ gimple_duplicate_sese_tail (edge entry ATTRIBUTE_UNUSED, edge exit ATTRIBUTE_UNU int total_freq = 0, exit_freq = 0; gcov_type total_count = 0, exit_count = 0; edge exits[2], nexits[2], e; - gimple_stmt_iterator gsi; + gimple_stmt_iterator gsi,gsi1; gimple cond_stmt; edge sorig, snew; + basic_block exit_bb; + basic_block iters_bb; + tree new_rhs; + gimple_stmt_iterator psi; + gimple phi; + tree def; gcc_assert (EDGE_COUNT (exit->src->succs) == 2); exits[0] = exit; @@ -5280,24 +5247,9 @@ gimple_duplicate_sese_tail (edge entry ATTRIBUTE_UNUSED, edge exit ATTRIBUTE_UNU if (!can_copy_bbs_p (region, n_region)) return false; - /* Some sanity checking. Note that we do not check for all possible - missuses of the functions. I.e. if you ask to copy something weird - (e.g., in the example, if there is a jump from inside to the middle - of some_code, or come_code defines some of the values used in cond) - it will work, but the resulting code will not be correct. */ - for (i = 0; i < n_region; i++) - { - /* We do not handle subloops, i.e. all the blocks must belong to the - same loop. */ - if (region[i]->loop_father != orig_loop) - return false; - - if (region[i] == orig_loop->latch) - return false; - } - initialize_original_copy_tables (); set_loop_copy (orig_loop, loop); + duplicate_subloops (orig_loop, loop); if (!region_copy) { @@ -5363,8 +5315,36 @@ gimple_duplicate_sese_tail (edge entry ATTRIBUTE_UNUSED, edge exit ATTRIBUTE_UNU cond_stmt = last_stmt (exit->src); gcc_assert (gimple_code (cond_stmt) == GIMPLE_COND); cond_stmt = gimple_copy (cond_stmt); + + /* If the block consisting of the exit condition has the latch as + successor, then the body of the loop is executed before + the exit condition is tested. In such case, moving the + condition to the entry, causes that the loop will iterate + one less iteration (which is the wanted outcome, since we + peel out the last iteration). If the body is executed after + the condition, moving the condition to the entry requires + decrementing one iteration. */ + if (exits[1]->dest == orig_loop->latch) + new_rhs = gimple_cond_rhs (cond_stmt); + else + { + new_rhs = fold_build2 (MINUS_EXPR, TREE_TYPE (gimple_cond_rhs (cond_stmt)), + gimple_cond_rhs (cond_stmt), + build_int_cst (TREE_TYPE (gimple_cond_rhs (cond_stmt)), 1)); + + if (TREE_CODE (gimple_cond_rhs (cond_stmt)) == SSA_NAME) + { + iters_bb = gimple_bb (SSA_NAME_DEF_STMT (gimple_cond_rhs (cond_stmt))); + for (gsi1 = gsi_start_bb (iters_bb); !gsi_end_p (gsi1); gsi_next (&gsi1)) + if (gsi_stmt (gsi1) == SSA_NAME_DEF_STMT (gimple_cond_rhs (cond_stmt))) + break; + + new_rhs = force_gimple_operand_gsi (&gsi1, new_rhs, true, + NULL_TREE,false,GSI_CONTINUE_LINKING); + } + } + gimple_cond_set_rhs (cond_stmt, unshare_expr (new_rhs)); gimple_cond_set_lhs (cond_stmt, unshare_expr (gimple_cond_lhs (cond_stmt))); - gimple_cond_set_rhs (cond_stmt, unshare_expr (gimple_cond_rhs (cond_stmt))); gsi_insert_after (&gsi, cond_stmt, GSI_NEW_STMT); sorig = single_succ_edge (switch_bb); @@ -5379,16 +5359,35 @@ gimple_duplicate_sese_tail (edge entry ATTRIBUTE_UNUSED, edge exit ATTRIBUTE_UNU /* Get rid of now superfluous conditions and associated edges (and phi node arguments). */ + exit_bb = exit->dest; + e = redirect_edge_and_branch (exits[0], exits[1]->dest); PENDING_STMT (e) = NULL; - e = redirect_edge_and_branch (nexits[1], nexits[0]->dest); - PENDING_STMT (e) = NULL; + /* The latch of ORIG_LOOP was copied, and so was the backedge + to the original header. We redirect this backedge to EXIT_BB. */ + for (i = 0; i < n_region; i++) + if (get_bb_original (region_copy[i]) == orig_loop->latch) + { + gcc_assert (single_succ_edge (region_copy[i])); + e = redirect_edge_and_branch (single_succ_edge (region_copy[i]), exit_bb); + PENDING_STMT (e) = NULL; + for (psi = gsi_start_phis (exit_bb); + !gsi_end_p (psi); + gsi_next (&psi)) + { + phi = gsi_stmt (psi); + def = PHI_ARG_DEF (phi, nexits[0]->dest_idx); + add_phi_arg (phi, def, e, gimple_phi_arg_location_from_edge (phi, e)); + } + } + e = redirect_edge_and_branch (nexits[0], nexits[1]->dest); + PENDING_STMT (e) = NULL; + /* Anything that is outside of the region, but was dominated by something inside needs to update dominance info. */ iterate_fix_dominators (CDI_DOMINATORS, doms, false); VEC_free (basic_block, heap, doms); - /* Update the SSA web. */ update_ssa (TODO_update_ssa); @@ -5504,6 +5503,7 @@ struct move_stmt_d tree to_context; struct pointer_map_t *vars_map; htab_t new_label_map; + struct pointer_map_t *eh_map; bool remap_decls_p; }; @@ -5552,7 +5552,7 @@ move_stmt_op (tree *tp, int *walk_subtrees, void *data) && !is_global_var (t)) || TREE_CODE (t) == CONST_DECL) replace_by_duplicate_decl (tp, p->vars_map, p->to_context); - + if (SSA_VAR_P (t) && gimple_in_ssa_p (cfun)) { @@ -5569,6 +5569,35 @@ move_stmt_op (tree *tp, int *walk_subtrees, void *data) return NULL_TREE; } +/* Helper for move_stmt_r. Given an EH region number for the source + function, map that to the duplicate EH regio number in the dest. */ + +static int +move_stmt_eh_region_nr (int old_nr, struct move_stmt_d *p) +{ + eh_region old_r, new_r; + void **slot; + + old_r = get_eh_region_from_number (old_nr); + slot = pointer_map_contains (p->eh_map, old_r); + new_r = (eh_region) *slot; + + return new_r->index; +} + +/* Similar, but operate on INTEGER_CSTs. */ + +static tree +move_stmt_eh_region_tree_nr (tree old_t_nr, struct move_stmt_d *p) +{ + int old_nr, new_nr; + + old_nr = tree_low_cst (old_t_nr, 0); + new_nr = move_stmt_eh_region_nr (old_nr, p); + + return build_int_cst (NULL, new_nr); +} + /* Like move_stmt_op, but for gimple statements. Helper for move_block_to_fn. Set GIMPLE_BLOCK in every expression @@ -5597,21 +5626,70 @@ move_stmt_r (gimple_stmt_iterator *gsi_p, bool *handled_ops_p, } #endif - if (is_gimple_omp (stmt) - && gimple_code (stmt) != GIMPLE_OMP_RETURN - && gimple_code (stmt) != GIMPLE_OMP_CONTINUE) + switch (gimple_code (stmt)) { - /* Do not remap variables inside OMP directives. Variables - referenced in clauses and directive header belong to the - parent function and should not be moved into the child - function. */ - bool save_remap_decls_p = p->remap_decls_p; - p->remap_decls_p = false; - *handled_ops_p = true; + case GIMPLE_CALL: + /* Remap the region numbers for __builtin_eh_{pointer,filter}. */ + { + tree r, fndecl = gimple_call_fndecl (stmt); + if (fndecl && DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_NORMAL) + switch (DECL_FUNCTION_CODE (fndecl)) + { + case BUILT_IN_EH_COPY_VALUES: + r = gimple_call_arg (stmt, 1); + r = move_stmt_eh_region_tree_nr (r, p); + gimple_call_set_arg (stmt, 1, r); + /* FALLTHRU */ + + case BUILT_IN_EH_POINTER: + case BUILT_IN_EH_FILTER: + r = gimple_call_arg (stmt, 0); + r = move_stmt_eh_region_tree_nr (r, p); + gimple_call_set_arg (stmt, 0, r); + break; + + default: + break; + } + } + break; + + case GIMPLE_RESX: + { + int r = gimple_resx_region (stmt); + r = move_stmt_eh_region_nr (r, p); + gimple_resx_set_region (stmt, r); + } + break; - walk_gimple_seq (gimple_omp_body (stmt), move_stmt_r, move_stmt_op, wi); + case GIMPLE_EH_DISPATCH: + { + int r = gimple_eh_dispatch_region (stmt); + r = move_stmt_eh_region_nr (r, p); + gimple_eh_dispatch_set_region (stmt, r); + } + break; - p->remap_decls_p = save_remap_decls_p; + case GIMPLE_OMP_RETURN: + case GIMPLE_OMP_CONTINUE: + break; + default: + if (is_gimple_omp (stmt)) + { + /* Do not remap variables inside OMP directives. Variables + referenced in clauses and directive header belong to the + parent function and should not be moved into the child + function. */ + bool save_remap_decls_p = p->remap_decls_p; + p->remap_decls_p = false; + *handled_ops_p = true; + + walk_gimple_seq (gimple_omp_body (stmt), move_stmt_r, + move_stmt_op, wi); + + p->remap_decls_p = save_remap_decls_p; + } + break; } return NULL_TREE; @@ -5645,7 +5723,7 @@ mark_virtual_ops_in_bb (basic_block bb) static void move_block_to_fn (struct function *dest_cfun, basic_block bb, basic_block after, bool update_edge_count_p, - struct move_stmt_d *d, int eh_offset) + struct move_stmt_d *d) { struct control_flow_graph *cfg; edge_iterator ei; @@ -5721,7 +5799,6 @@ move_block_to_fn (struct function *dest_cfun, basic_block bb, for (si = gsi_start_bb (bb); !gsi_end_p (si); gsi_next (&si)) { gimple stmt = gsi_stmt (si); - int region; struct walk_stmt_info wi; memset (&wi, 0, sizeof (wi)); @@ -5751,17 +5828,12 @@ move_block_to_fn (struct function *dest_cfun, basic_block bb, if (uid >= dest_cfun->cfg->last_label_uid) dest_cfun->cfg->last_label_uid = uid + 1; } - else if (gimple_code (stmt) == GIMPLE_RESX && eh_offset != 0) - gimple_resx_set_region (stmt, gimple_resx_region (stmt) + 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); - gimple_duplicate_stmt_histograms (dest_cfun, stmt, cfun, stmt); - gimple_remove_stmt_histograms (cfun, stmt); - } + maybe_duplicate_eh_stmt_fn (dest_cfun, stmt, cfun, stmt, d->eh_map, 0); + remove_stmt_from_eh_lp_fn (cfun, stmt); + + gimple_duplicate_stmt_histograms (dest_cfun, stmt, cfun, stmt); + gimple_remove_stmt_histograms (cfun, stmt); /* We cannot leave any operands allocated from the operand caches of the current function. */ @@ -5792,29 +5864,28 @@ move_block_to_fn (struct function *dest_cfun, basic_block bb, /* Examine the statements in BB (which is in SRC_CFUN); find and return the outermost EH region. Use REGION as the incoming base EH region. */ -static int +static eh_region find_outermost_region_in_block (struct function *src_cfun, - basic_block bb, int region) + basic_block bb, eh_region region) { gimple_stmt_iterator si; for (si = gsi_start_bb (bb); !gsi_end_p (si); gsi_next (&si)) { gimple stmt = gsi_stmt (si); - int stmt_region; + eh_region stmt_region; + int lp_nr; - if (gimple_code (stmt) == GIMPLE_RESX) - stmt_region = gimple_resx_region (stmt); - else - stmt_region = lookup_stmt_eh_region_fn (src_cfun, stmt); - if (stmt_region > 0) + lp_nr = lookup_stmt_eh_lp_fn (src_cfun, stmt); + stmt_region = get_eh_region_from_lp_number_fn (src_cfun, lp_nr); + if (stmt_region) { - if (region < 0) + if (region == NULL) region = stmt_region; else if (stmt_region != region) { region = eh_region_outermost (src_cfun, stmt_region, region); - gcc_assert (region != -1); + gcc_assert (region != NULL); } } } @@ -5834,7 +5905,7 @@ new_label_mapper (tree decl, void *data) m = XNEW (struct tree_map); m->hash = DECL_UID (decl); m->base.from = decl; - m->to = create_artificial_label (); + m->to = create_artificial_label (UNKNOWN_LOCATION); LABEL_DECL_UID (m->to) = LABEL_DECL_UID (decl); if (LABEL_DECL_UID (m->to) >= cfun->cfg->last_label_uid) cfun->cfg->last_label_uid = LABEL_DECL_UID (m->to) + 1; @@ -5904,13 +5975,13 @@ move_sese_region_to_fn (struct function *dest_cfun, basic_block entry_bb, basic_block dom_entry = get_immediate_dominator (CDI_DOMINATORS, entry_bb); basic_block after, bb, *entry_pred, *exit_succ, abb; struct function *saved_cfun = cfun; - int *entry_flag, *exit_flag, eh_offset; + int *entry_flag, *exit_flag; unsigned *entry_prob, *exit_prob; unsigned i, num_entry_edges, num_exit_edges; edge e; edge_iterator ei; htab_t new_label_map; - struct pointer_map_t *vars_map; + struct pointer_map_t *vars_map, *eh_map; struct loop *loop = entry_bb->loop_father; struct move_stmt_d d; @@ -5980,21 +6051,21 @@ move_sese_region_to_fn (struct function *dest_cfun, basic_block entry_bb, init_empty_tree_cfg (); /* Initialize EH information for the new function. */ - eh_offset = 0; + eh_map = NULL; new_label_map = NULL; if (saved_cfun->eh) { - int region = -1; + eh_region region = NULL; 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) + if (region != NULL) { 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); + eh_map = duplicate_eh_regions (saved_cfun, region, 0, + new_label_mapper, new_label_map); } } @@ -6006,20 +6077,21 @@ move_sese_region_to_fn (struct function *dest_cfun, basic_block entry_bb, vars_map = pointer_map_create (); memset (&d, 0, sizeof (d)); - d.vars_map = vars_map; + d.orig_block = orig_block; + d.new_block = DECL_INITIAL (dest_cfun->decl); d.from_context = cfun->decl; d.to_context = dest_cfun->decl; + d.vars_map = vars_map; d.new_label_map = new_label_map; + d.eh_map = eh_map; d.remap_decls_p = true; - d.orig_block = orig_block; - d.new_block = DECL_INITIAL (dest_cfun->decl); for (i = 0; VEC_iterate (basic_block, bbs, i, bb); i++) { /* No need to update edge counts on the last block. It has already been updated earlier when we detached the region from the original CFG. */ - move_block_to_fn (dest_cfun, bb, after, bb != exit_bb, &d, eh_offset); + move_block_to_fn (dest_cfun, bb, after, bb != exit_bb, &d); after = bb; } @@ -6042,6 +6114,8 @@ move_sese_region_to_fn (struct function *dest_cfun, basic_block entry_bb, if (new_label_map) htab_delete (new_label_map); + if (eh_map) + pointer_map_destroy (eh_map); pointer_map_destroy (vars_map); /* Rewire the entry and exit blocks. The successor to the entry @@ -6128,7 +6202,7 @@ dump_function_to_file (tree fn, FILE *file, int flags) print_node (file, "", fn, 2); dsf = DECL_STRUCT_FUNCTION (fn); - if (dsf && (flags & TDF_DETAILS)) + if (dsf && (flags & TDF_EH)) dump_eh_tree (file, dsf); if (flags & TDF_RAW && !gimple_has_body_p (fn)) @@ -6274,7 +6348,7 @@ print_succ_bbs (FILE *file, basic_block bb) /* Print to FILE the basic block BB following the VERBOSITY level. */ -void +void print_loops_bb (FILE *file, basic_block bb, int indent, int verbosity) { char *s_indent = (char *) alloca ((size_t) indent + 1); @@ -6320,7 +6394,7 @@ print_loop (FILE *file, struct loop *loop, int indent, int verbosity) s_indent[indent] = '\0'; /* Print loop's header. */ - fprintf (file, "%sloop_%d (header = %d, latch = %d", s_indent, + fprintf (file, "%sloop_%d (header = %d, latch = %d", s_indent, loop->num, loop->header->index, loop->latch->index); fprintf (file, ", niter = "); print_generic_expr (file, loop->nb_iterations, 0); @@ -6411,7 +6485,7 @@ debug_loop_num (unsigned num, int verbosity) static bool gimple_block_ends_with_call_p (basic_block bb) { - gimple_stmt_iterator gsi = gsi_last_bb (bb); + gimple_stmt_iterator gsi = gsi_last_nondebug_bb (bb); return is_gimple_call (gsi_stmt (gsi)); } @@ -6626,20 +6700,6 @@ gimple_purge_dead_abnormal_call_edges (basic_block bb) return changed; } -/* Stores all basic blocks dominated by BB to DOM_BBS. */ - -static void -get_all_dominated_blocks (basic_block bb, VEC (basic_block, heap) **dom_bbs) -{ - basic_block son; - - VEC_safe_push (basic_block, heap, *dom_bbs, bb); - for (son = first_dom_son (CDI_DOMINATORS, bb); - son; - son = next_dom_son (CDI_DOMINATORS, son)) - get_all_dominated_blocks (son, dom_bbs); -} - /* Removes edge E and all the blocks dominated by it, and updates dominance information. The IL in E->src needs to be updated separately. If dominance info is not available, only the edge E is removed.*/ @@ -6699,7 +6759,7 @@ remove_edge_and_dominated_blocks (edge e) get_immediate_dominator (CDI_DOMINATORS, e->dest)->index); else { - get_all_dominated_blocks (e->dest, &bbs_to_remove); + bbs_to_remove = get_all_dominated_blocks (CDI_DOMINATORS, e->dest); for (i = 0; VEC_iterate (basic_block, bbs_to_remove, i, bb); i++) { FOR_EACH_EDGE (f, ei, bb->succs) @@ -6731,20 +6791,24 @@ remove_edge_and_dominated_blocks (edge e) remove_edge (e); else { - for (i = 0; VEC_iterate (basic_block, bbs_to_remove, i, bb); i++) - delete_basic_block (bb); + /* Walk backwards so as to get a chance to substitute all + released DEFs into debug stmts. See + eliminate_unnecessary_stmts() in tree-ssa-dce.c for more + details. */ + for (i = VEC_length (basic_block, bbs_to_remove); i-- > 0; ) + delete_basic_block (VEC_index (basic_block, bbs_to_remove, i)); } /* Update the dominance information. The immediate dominator may change only for blocks whose immediate dominator belongs to DF_IDOM: - + Suppose that idom(X) = Y before removal of E and idom(X) != Y after the removal. Let Z the arbitrary block such that idom(Z) = Y and Z dominates X after the removal. Before removal, there exists a path P from Y to X that avoids Z. Let F be the last edge on P that is removed, and let W = F->dest. Before removal, idom(W) = Y (since Y dominates W, and because of P, Z does not dominate W), and W belongs to - the dominance frontier of E. Therefore, Y belongs to DF_IDOM. */ + the dominance frontier of E. Therefore, Y belongs to DF_IDOM. */ EXECUTE_IF_SET_IN_BITMAP (df_idom, 0, i, bi) { bb = BASIC_BLOCK (i); @@ -6818,7 +6882,7 @@ gimple_execute_on_growing_pred (edge e) { basic_block bb = e->dest; - if (phi_nodes (bb)) + if (!gimple_seq_empty_p (phi_nodes (bb))) reserve_phi_args_for_new_edge (bb); } @@ -6828,7 +6892,7 @@ gimple_execute_on_growing_pred (edge e) static void gimple_execute_on_shrinking_pred (edge e) { - if (phi_nodes (e->dest)) + if (!gimple_seq_empty_p (phi_nodes (e->dest))) remove_phi_args (e); } @@ -6868,7 +6932,7 @@ gimple_lv_adjust_loop_header_phi (basic_block first, basic_block second, phi1 = gsi_stmt (psi1); phi2 = gsi_stmt (psi2); def = PHI_ARG_DEF (phi2, e2->dest_idx); - add_phi_arg (phi1, def, e); + add_phi_arg (phi1, def, e, gimple_phi_arg_location_from_edge (phi2, e2)); } } @@ -6951,10 +7015,31 @@ split_critical_edges (void) FOR_ALL_BB (bb) { FOR_EACH_EDGE (e, ei, bb->succs) - if (EDGE_CRITICAL_P (e) && !(e->flags & EDGE_ABNORMAL)) - { + { + if (EDGE_CRITICAL_P (e) && !(e->flags & EDGE_ABNORMAL)) split_edge (e); - } + /* PRE inserts statements to edges and expects that + since split_critical_edges was done beforehand, committing edge + insertions will not split more edges. In addition to critical + edges we must split edges that have multiple successors and + end by control flow statements, such as RESX. + Go ahead and split them too. This matches the logic in + gimple_find_edge_insert_loc. */ + else if ((!single_pred_p (e->dest) + || !gimple_seq_empty_p (phi_nodes (e->dest)) + || e->dest == EXIT_BLOCK_PTR) + && e->src != ENTRY_BLOCK_PTR + && !(e->flags & EDGE_ABNORMAL)) + { + gimple_stmt_iterator gsi; + + gsi = gsi_last_bb (e->src); + if (!gsi_end_p (gsi) + && stmt_ends_bb_p (gsi_stmt (gsi)) + && gimple_code (gsi_stmt (gsi)) != GIMPLE_RETURN) + split_edge (e); + } + } } end_recording_case_labels (); return 0; @@ -6975,7 +7060,7 @@ struct gimple_opt_pass pass_split_crit_edges = PROP_no_crit_edges, /* properties_provided */ 0, /* properties_destroyed */ 0, /* todo_flags_start */ - TODO_dump_func /* todo_flags_finish */ + TODO_dump_func | TODO_verify_flow /* todo_flags_finish */ } }; @@ -6988,8 +7073,9 @@ gimplify_build3 (gimple_stmt_iterator *gsi, enum tree_code code, tree type, tree a, tree b, tree c) { tree ret; + location_t loc = gimple_location (gsi_stmt (*gsi)); - ret = fold_build3 (code, type, a, b, c); + ret = fold_build3_loc (loc, code, type, a, b, c); STRIP_NOPS (ret); return force_gimple_operand_gsi (gsi, ret, true, NULL, true, @@ -7005,7 +7091,7 @@ gimplify_build2 (gimple_stmt_iterator *gsi, enum tree_code code, { tree ret; - ret = fold_build2 (code, type, a, b); + ret = fold_build2_loc (gimple_location (gsi_stmt (*gsi)), code, type, a, b); STRIP_NOPS (ret); return force_gimple_operand_gsi (gsi, ret, true, NULL, true, @@ -7021,7 +7107,7 @@ gimplify_build1 (gimple_stmt_iterator *gsi, enum tree_code code, tree type, { tree ret; - ret = fold_build1 (code, type, a); + ret = fold_build1_loc (gimple_location (gsi_stmt (*gsi)), code, type, a); STRIP_NOPS (ret); return force_gimple_operand_gsi (gsi, ret, true, NULL, true, @@ -7054,7 +7140,7 @@ execute_warn_function_return (void) } if (location == UNKNOWN_LOCATION) location = cfun->function_end_locus; - warning (0, "%H% function does return", &location); + warning_at (location, 0, "% function does return"); } /* If we see "return;" in some basic block, then we do reach the end @@ -7112,13 +7198,13 @@ struct gimple_opt_pass pass_warn_function_return = { { GIMPLE_PASS, - NULL, /* name */ + "*warn_function_return", /* name */ NULL, /* gate */ execute_warn_function_return, /* execute */ NULL, /* sub */ NULL, /* next */ 0, /* static_pass_number */ - 0, /* tv_id */ + TV_NONE, /* tv_id */ PROP_cfg, /* properties_required */ 0, /* properties_provided */ 0, /* properties_destroyed */ @@ -7136,9 +7222,9 @@ execute_warn_function_noreturn (void) && !TREE_THIS_VOLATILE (cfun->decl) && EDGE_COUNT (EXIT_BLOCK_PTR->preds) == 0 && !lang_hooks.missing_noreturn_ok_p (cfun->decl)) - warning (OPT_Wmissing_noreturn, "%Jfunction might be possible candidate " - "for attribute %", - cfun->decl); + warning_at (DECL_SOURCE_LOCATION (cfun->decl), OPT_Wmissing_noreturn, + "function might be possible candidate " + "for attribute %"); return 0; } @@ -7146,13 +7232,13 @@ struct gimple_opt_pass pass_warn_function_noreturn = { { GIMPLE_PASS, - NULL, /* name */ + "*warn_function_noreturn", /* name */ NULL, /* gate */ execute_warn_function_noreturn, /* execute */ NULL, /* sub */ NULL, /* next */ 0, /* static_pass_number */ - 0, /* tv_id */ + TV_NONE, /* tv_id */ PROP_cfg, /* properties_required */ 0, /* properties_provided */ 0, /* properties_destroyed */ @@ -7160,3 +7246,100 @@ struct gimple_opt_pass pass_warn_function_noreturn = 0 /* todo_flags_finish */ } }; + + +/* Walk a gimplified function and warn for functions whose return value is + ignored and attribute((warn_unused_result)) is set. This is done before + inlining, so we don't have to worry about that. */ + +static void +do_warn_unused_result (gimple_seq seq) +{ + tree fdecl, ftype; + gimple_stmt_iterator i; + + for (i = gsi_start (seq); !gsi_end_p (i); gsi_next (&i)) + { + gimple g = gsi_stmt (i); + + switch (gimple_code (g)) + { + case GIMPLE_BIND: + do_warn_unused_result (gimple_bind_body (g)); + break; + case GIMPLE_TRY: + do_warn_unused_result (gimple_try_eval (g)); + do_warn_unused_result (gimple_try_cleanup (g)); + break; + case GIMPLE_CATCH: + do_warn_unused_result (gimple_catch_handler (g)); + break; + case GIMPLE_EH_FILTER: + do_warn_unused_result (gimple_eh_filter_failure (g)); + break; + + case GIMPLE_CALL: + if (gimple_call_lhs (g)) + break; + + /* This is a naked call, as opposed to a GIMPLE_CALL with an + LHS. All calls whose value is ignored should be + represented like this. Look for the attribute. */ + fdecl = gimple_call_fndecl (g); + ftype = TREE_TYPE (TREE_TYPE (gimple_call_fn (g))); + + if (lookup_attribute ("warn_unused_result", TYPE_ATTRIBUTES (ftype))) + { + location_t loc = gimple_location (g); + + if (fdecl) + warning_at (loc, OPT_Wunused_result, + "ignoring return value of %qD, " + "declared with attribute warn_unused_result", + fdecl); + else + warning_at (loc, OPT_Wunused_result, + "ignoring return value of function " + "declared with attribute warn_unused_result"); + } + break; + + default: + /* Not a container, not a call, or a call whose value is used. */ + break; + } + } +} + +static unsigned int +run_warn_unused_result (void) +{ + do_warn_unused_result (gimple_body (current_function_decl)); + return 0; +} + +static bool +gate_warn_unused_result (void) +{ + return flag_warn_unused_result; +} + +struct gimple_opt_pass pass_warn_unused_result = +{ + { + GIMPLE_PASS, + "*warn_unused_result", /* name */ + gate_warn_unused_result, /* gate */ + run_warn_unused_result, /* execute */ + NULL, /* sub */ + NULL, /* next */ + 0, /* static_pass_number */ + TV_NONE, /* tv_id */ + PROP_gimple_any, /* properties_required */ + 0, /* properties_provided */ + 0, /* properties_destroyed */ + 0, /* todo_flags_start */ + 0, /* todo_flags_finish */ + } +}; +