Mercurial > hg > CbC > CbC_gcc
diff gcc/cfgcleanup.c @ 111:04ced10e8804
gcc 7
author | kono |
---|---|
date | Fri, 27 Oct 2017 22:46:09 +0900 |
parents | f6334be47118 |
children | 84e7813d76e9 |
line wrap: on
line diff
--- a/gcc/cfgcleanup.c Sun Aug 21 07:07:55 2011 +0900 +++ b/gcc/cfgcleanup.c Fri Oct 27 22:46:09 2017 +0900 @@ -1,7 +1,5 @@ /* Control flow optimization code for GNU compiler. - Copyright (C) 1987, 1988, 1992, 1993, 1994, 1995, 1996, 1997, 1998, - 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2010 - Free Software Foundation, Inc. + Copyright (C) 1987-2017 Free Software Foundation, Inc. This file is part of GCC. @@ -34,45 +32,44 @@ #include "config.h" #include "system.h" #include "coretypes.h" -#include "tm.h" +#include "backend.h" +#include "target.h" #include "rtl.h" -#include "hard-reg-set.h" -#include "regs.h" -#include "timevar.h" -#include "output.h" +#include "tree.h" +#include "cfghooks.h" +#include "df.h" +#include "memmodel.h" +#include "tm_p.h" #include "insn-config.h" -#include "flags.h" -#include "recog.h" -#include "diagnostic-core.h" +#include "emit-rtl.h" #include "cselib.h" #include "params.h" -#include "tm_p.h" -#include "target.h" -#include "cfglayout.h" -#include "emit-rtl.h" #include "tree-pass.h" #include "cfgloop.h" -#include "expr.h" -#include "df.h" +#include "cfgrtl.h" +#include "cfganal.h" +#include "cfgbuild.h" +#include "cfgcleanup.h" #include "dce.h" #include "dbgcnt.h" +#include "rtl-iter.h" #define FORWARDER_BLOCK_P(BB) ((BB)->flags & BB_FORWARDER_BLOCK) /* Set to true when we are running first pass of try_optimize_cfg loop. */ static bool first_pass; -/* Set to true if crossjumps occured in the latest run of try_optimize_cfg. */ -static bool crossjumps_occured; +/* Set to true if crossjumps occurred in the latest run of try_optimize_cfg. */ +static bool crossjumps_occurred; /* Set to true if we couldn't run an optimization due to stale liveness information; we should run df_analyze to enable more opportunities. */ static bool block_was_dirty; -static bool try_crossjump_to_edge (int, edge, edge); +static bool try_crossjump_to_edge (int, edge, edge, enum replace_direction); static bool try_crossjump_bb (int, basic_block); static bool outgoing_edges_match (int, basic_block, basic_block); -static bool old_insns_match_p (int, rtx, rtx); +static enum replace_direction old_insns_match_p (int, rtx_insn *, rtx_insn *); static void merge_blocks_move_predecessor_nojumps (basic_block, basic_block); static void merge_blocks_move_successor_nojumps (basic_block, basic_block); @@ -83,7 +80,6 @@ static bool mark_effect (rtx, bitmap); static void notice_new_block (basic_block); static void update_forwarder_flag (basic_block); -static int mentions_nonequal_regs (rtx *, void *); static void merge_memattrs (rtx, rtx); /* Set flags for newly created block. */ @@ -117,7 +113,7 @@ { basic_block jump_block, jump_dest_block, cbranch_dest_block; edge cbranch_jump_edge, cbranch_fallthru_edge; - rtx cbranch_insn; + rtx_insn *cbranch_insn; /* Verify that there are exactly two successors. */ if (EDGE_COUNT (cbranch_block->succs) != 2) @@ -137,7 +133,7 @@ unconditional jump. */ jump_block = cbranch_fallthru_edge->dest; if (!single_pred_p (jump_block) - || jump_block->next_bb == EXIT_BLOCK_PTR + || jump_block->next_bb == EXIT_BLOCK_PTR_FOR_FN (cfun) || !FORWARDER_BLOCK_P (jump_block)) return false; jump_dest_block = single_succ (jump_block); @@ -160,12 +156,14 @@ unconditional branch. */ cbranch_dest_block = cbranch_jump_edge->dest; - if (cbranch_dest_block == EXIT_BLOCK_PTR + if (cbranch_dest_block == EXIT_BLOCK_PTR_FOR_FN (cfun) + || jump_dest_block == EXIT_BLOCK_PTR_FOR_FN (cfun) || !can_fallthru (jump_block, cbranch_dest_block)) return false; /* Invert the conditional branch. */ - if (!invert_jump (cbranch_insn, block_label (jump_dest_block), 0)) + if (!invert_jump (as_a <rtx_jump_insn *> (cbranch_insn), + block_label (jump_dest_block), 0)) return false; if (dump_file) @@ -197,25 +195,15 @@ static bool mark_effect (rtx exp, regset nonequal) { - int regno; rtx dest; switch (GET_CODE (exp)) { /* In case we do clobber the register, mark it as equal, as we know the value is dead so it don't have to match. */ case CLOBBER: - if (REG_P (XEXP (exp, 0))) - { - dest = XEXP (exp, 0); - regno = REGNO (dest); - CLEAR_REGNO_REG_SET (nonequal, regno); - if (regno < FIRST_PSEUDO_REGISTER) - { - int n = hard_regno_nregs[regno][GET_MODE (dest)]; - while (--n > 0) - CLEAR_REGNO_REG_SET (nonequal, regno + n); - } - } + dest = XEXP (exp, 0); + if (REG_P (dest)) + bitmap_clear_range (nonequal, REGNO (dest), REG_NREGS (dest)); return false; case SET: @@ -226,14 +214,7 @@ return false; if (!REG_P (dest)) return true; - regno = REGNO (dest); - SET_REGNO_REG_SET (nonequal, regno); - if (regno < FIRST_PSEUDO_REGISTER) - { - int n = hard_regno_nregs[regno][GET_MODE (dest)]; - while (--n > 0) - SET_REGNO_REG_SET (nonequal, regno + n); - } + bitmap_set_range (nonequal, REGNO (dest), REG_NREGS (dest)); return false; default: @@ -241,29 +222,25 @@ } } -/* Return nonzero if X is a register set in regset DATA. - Called via for_each_rtx. */ -static int -mentions_nonequal_regs (rtx *x, void *data) +/* Return true if X contains a register in NONEQUAL. */ +static bool +mentions_nonequal_regs (const_rtx x, regset nonequal) { - regset nonequal = (regset) data; - if (REG_P (*x)) + subrtx_iterator::array_type array; + FOR_EACH_SUBRTX (iter, array, x, NONCONST) { - int regno; - - regno = REGNO (*x); - if (REGNO_REG_SET_P (nonequal, regno)) - return 1; - if (regno < FIRST_PSEUDO_REGISTER) + const_rtx x = *iter; + if (REG_P (x)) { - int n = hard_regno_nregs[regno][GET_MODE (*x)]; - while (--n > 0) - if (REGNO_REG_SET_P (nonequal, regno + n)) - return 1; + unsigned int end_regno = END_REGNO (x); + for (unsigned int regno = REGNO (x); regno < end_regno; ++regno) + if (REGNO_REG_SET_P (nonequal, regno)) + return true; } } - return 0; + return false; } + /* Attempt to prove that the basic block B will have no side effects and always continues in the same edge if reached via E. Return the edge if exist, NULL otherwise. */ @@ -271,7 +248,8 @@ static edge thread_jump (edge e, basic_block b) { - rtx set1, set2, cond1, cond2, insn; + rtx set1, set2, cond1, cond2; + rtx_insn *insn; enum rtx_code code1, code2, reversed_code2; bool reverse1 = false; unsigned i; @@ -386,7 +364,7 @@ /* cond2 must not mention any register that is not equal to the former block. */ - if (for_each_rtx (&cond2, mentions_nonequal_regs, nonequal)) + if (mentions_nonequal_regs (cond2, nonequal)) goto failed_exit; EXECUTE_IF_SET_IN_REG_SET (nonequal, 0, i, rsi) @@ -426,13 +404,14 @@ partition boundaries). See the comments at the top of bb-reorder.c:partition_hot_cold_basic_blocks for complete details. */ - if (find_reg_note (BB_END (b), REG_CROSSING_JUMP, NULL_RTX)) + if (JUMP_P (BB_END (b)) && CROSSING_JUMP_P (BB_END (b))) return false; for (ei = ei_start (b->succs); (e = ei_safe_edge (ei)); ) { basic_block target, first; - int counter, goto_locus; + location_t goto_locus; + int counter; bool threaded = false; int nthreaded_edges = 0; bool may_thread = first_pass || (b->flags & BB_MODIFIED) != 0; @@ -462,11 +441,12 @@ bb-reorder.c:partition_hot_cold_basic_blocks for complete details. */ - if (first != EXIT_BLOCK_PTR - && find_reg_note (BB_END (first), REG_CROSSING_JUMP, NULL_RTX)) - return false; - - while (counter < n_basic_blocks) + if (first != EXIT_BLOCK_PTR_FOR_FN (cfun) + && JUMP_P (BB_END (first)) + && CROSSING_JUMP_P (BB_END (first))) + return changed; + + while (counter < n_basic_blocks_for_fn (cfun)) { basic_block new_target = NULL; bool new_target_threaded = false; @@ -474,40 +454,43 @@ if (FORWARDER_BLOCK_P (target) && !(single_succ_edge (target)->flags & EDGE_CROSSING) - && single_succ (target) != EXIT_BLOCK_PTR) + && single_succ (target) != EXIT_BLOCK_PTR_FOR_FN (cfun)) { /* Bypass trivial infinite loops. */ new_target = single_succ (target); if (target == new_target) - counter = n_basic_blocks; + counter = n_basic_blocks_for_fn (cfun); else if (!optimize) { /* When not optimizing, ensure that edges or forwarder blocks with different locus are not optimized out. */ - int new_locus = single_succ_edge (target)->goto_locus; - int locus = goto_locus; - - if (new_locus && locus && !locator_eq (new_locus, locus)) + location_t new_locus = single_succ_edge (target)->goto_locus; + location_t locus = goto_locus; + + if (LOCATION_LOCUS (new_locus) != UNKNOWN_LOCATION + && LOCATION_LOCUS (locus) != UNKNOWN_LOCATION + && new_locus != locus) new_target = NULL; else { - rtx last; - - if (new_locus) + if (LOCATION_LOCUS (new_locus) != UNKNOWN_LOCATION) locus = new_locus; - last = BB_END (target); + rtx_insn *last = BB_END (target); if (DEBUG_INSN_P (last)) last = prev_nondebug_insn (last); - - new_locus = last && INSN_P (last) - ? INSN_LOCATOR (last) : 0; - - if (new_locus && locus && !locator_eq (new_locus, locus)) + if (last && INSN_P (last)) + new_locus = INSN_LOCATION (last); + else + new_locus = UNKNOWN_LOCATION; + + if (LOCATION_LOCUS (new_locus) != UNKNOWN_LOCATION + && LOCATION_LOCUS (locus) != UNKNOWN_LOCATION + && new_locus != locus) new_target = NULL; else { - if (new_locus) + if (LOCATION_LOCUS (new_locus) != UNKNOWN_LOCATION) locus = new_locus; goto_locus = locus; @@ -524,7 +507,8 @@ if (t) { if (!threaded_edges) - threaded_edges = XNEWVEC (edge, n_basic_blocks); + threaded_edges = XNEWVEC (edge, + n_basic_blocks_for_fn (cfun)); else { int i; @@ -536,7 +520,7 @@ break; if (i < nthreaded_edges) { - counter = n_basic_blocks; + counter = n_basic_blocks_for_fn (cfun); break; } } @@ -545,7 +529,9 @@ if (t->dest == b) break; - gcc_assert (nthreaded_edges < n_basic_blocks - NUM_FIXED_BLOCKS); + gcc_assert (nthreaded_edges + < (n_basic_blocks_for_fn (cfun) + - NUM_FIXED_BLOCKS)); threaded_edges[nthreaded_edges++] = t; new_target = t->dest; @@ -561,7 +547,7 @@ threaded |= new_target_threaded; } - if (counter >= n_basic_blocks) + if (counter >= n_basic_blocks_for_fn (cfun)) { if (dump_file) fprintf (dump_file, "Infinite loop in BB %i.\n", @@ -572,15 +558,15 @@ else { /* Save the values now, as the edge may get removed. */ - gcov_type edge_count = e->count; - int edge_probability = e->probability; + profile_count edge_count = e->count (); + profile_probability edge_probability = e->probability; int edge_frequency; int n = 0; e->goto_locus = goto_locus; /* Don't force if target is exit block. */ - if (threaded && target != EXIT_BLOCK_PTR) + if (threaded && target != EXIT_BLOCK_PTR_FOR_FN (cfun)) { notice_new_block (redirect_edge_and_branch_force (e, target)); if (dump_file) @@ -599,12 +585,7 @@ /* We successfully forwarded the edge. Now update profile data: for each edge we traversed in the chain, remove the original edge's execution count. */ - edge_frequency = ((edge_probability * b->frequency - + REG_BR_PROB_BASE / 2) - / REG_BR_PROB_BASE); - - if (!FORWARDER_BLOCK_P (b) && forwarder_block_p (b)) - b->flags |= BB_FORWARDER_BLOCK; + edge_frequency = edge_probability.apply (b->frequency); do { @@ -622,8 +603,6 @@ else { first->count -= edge_count; - if (first->count < 0) - first->count = 0; first->frequency -= edge_frequency; if (first->frequency < 0) first->frequency = 0; @@ -637,9 +616,6 @@ t = single_succ_edge (first); } - t->count -= edge_count; - if (t->count < 0) - t->count = 0; first = t->dest; } while (first != target); @@ -650,8 +626,7 @@ ei_next (&ei); } - if (threaded_edges) - free (threaded_edges); + free (threaded_edges); return changed; } @@ -663,7 +638,7 @@ static void merge_blocks_move_predecessor_nojumps (basic_block a, basic_block b) { - rtx barrier; + rtx_insn *barrier; /* If we are partitioning hot/cold basic blocks, we don't want to mess up unconditional or indirect jumps that cross between hot @@ -707,8 +682,9 @@ static void merge_blocks_move_successor_nojumps (basic_block a, basic_block b) { - rtx barrier, real_b_end; - rtx label, table; + rtx_insn *barrier, *real_b_end; + rtx_insn *label; + rtx_jump_table_data *table; /* If we are partitioning hot/cold basic blocks, we don't want to mess up unconditional or indirect jumps that cross between hot @@ -787,6 +763,11 @@ if (e->flags & EDGE_FALLTHRU) { int b_index = b->index, c_index = c->index; + + /* Protect the loop latches. */ + if (current_loops && c->loop_father->latch == c) + return NULL; + merge_blocks (b, c); update_forwarder_flag (b); @@ -794,7 +775,7 @@ fprintf (dump_file, "Merged %d and %d without moving.\n", b_index, c_index); - return b->prev_bb == ENTRY_BLOCK_PTR ? b : b->prev_bb; + return b->prev_bb == ENTRY_BLOCK_PTR_FOR_FN (cfun) ? b : b->prev_bb; } /* Otherwise we will need to move code around. Do that only if expensive @@ -832,7 +813,7 @@ if (! c_has_outgoing_fallthru) { merge_blocks_move_successor_nojumps (b, c); - return next == ENTRY_BLOCK_PTR ? next->next_bb : next; + return next == ENTRY_BLOCK_PTR_FOR_FN (cfun) ? next->next_bb : next; } /* If B does not have an incoming fallthru, then it can be moved @@ -844,7 +825,7 @@ { basic_block bb; - if (b_fallthru_edge->src == ENTRY_BLOCK_PTR) + if (b_fallthru_edge->src == ENTRY_BLOCK_PTR_FOR_FN (cfun)) return NULL; bb = force_nonfallthru (b_fallthru_edge); if (bb) @@ -852,7 +833,7 @@ } merge_blocks_move_predecessor_nojumps (b, c); - return next == ENTRY_BLOCK_PTR ? next->next_bb : next; + return next == ENTRY_BLOCK_PTR_FOR_FN (cfun) ? next->next_bb : next; } return NULL; @@ -862,7 +843,7 @@ /* Removes the memory attributes of MEM expression if they are not equal. */ -void +static void merge_memattrs (rtx x, rtx y) { int i; @@ -883,7 +864,7 @@ if (GET_MODE (x) != GET_MODE (y)) return; - if (code == MEM && MEM_ATTRS (x) != MEM_ATTRS (y)) + if (code == MEM && !mem_attrs_eq_p (MEM_ATTRS (x), MEM_ATTRS (y))) { if (! MEM_ATTRS (x)) MEM_ATTRS (y) = 0; @@ -891,7 +872,7 @@ MEM_ATTRS (x) = 0; else { - rtx mem_size; + HOST_WIDE_INT mem_size; if (MEM_ALIAS_SET (x) != MEM_ALIAS_SET (y)) { @@ -903,29 +884,51 @@ { set_mem_expr (x, 0); set_mem_expr (y, 0); - set_mem_offset (x, 0); - set_mem_offset (y, 0); + clear_mem_offset (x); + clear_mem_offset (y); } - else if (MEM_OFFSET (x) != MEM_OFFSET (y)) + else if (MEM_OFFSET_KNOWN_P (x) != MEM_OFFSET_KNOWN_P (y) + || (MEM_OFFSET_KNOWN_P (x) + && MEM_OFFSET (x) != MEM_OFFSET (y))) { - set_mem_offset (x, 0); - set_mem_offset (y, 0); + clear_mem_offset (x); + clear_mem_offset (y); } - if (!MEM_SIZE (x)) - mem_size = NULL_RTX; - else if (!MEM_SIZE (y)) - mem_size = NULL_RTX; + if (MEM_SIZE_KNOWN_P (x) && MEM_SIZE_KNOWN_P (y)) + { + mem_size = MAX (MEM_SIZE (x), MEM_SIZE (y)); + set_mem_size (x, mem_size); + set_mem_size (y, mem_size); + } else - mem_size = GEN_INT (MAX (INTVAL (MEM_SIZE (x)), - INTVAL (MEM_SIZE (y)))); - set_mem_size (x, mem_size); - set_mem_size (y, mem_size); + { + clear_mem_size (x); + clear_mem_size (y); + } set_mem_align (x, MIN (MEM_ALIGN (x), MEM_ALIGN (y))); set_mem_align (y, MEM_ALIGN (x)); } } + if (code == MEM) + { + if (MEM_READONLY_P (x) != MEM_READONLY_P (y)) + { + MEM_READONLY_P (x) = 0; + MEM_READONLY_P (y) = 0; + } + if (MEM_NOTRAP_P (x) != MEM_NOTRAP_P (y)) + { + MEM_NOTRAP_P (x) = 0; + MEM_NOTRAP_P (y) = 0; + } + if (MEM_VOLATILE_P (x) != MEM_VOLATILE_P (y)) + { + MEM_VOLATILE_P (x) = 1; + MEM_VOLATILE_P (y) = 1; + } + } fmt = GET_RTX_FORMAT (code); for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) @@ -950,27 +953,249 @@ } -/* Return true if I1 and I2 are equivalent and thus can be crossjumped. */ + /* Checks if patterns P1 and P2 are equivalent, apart from the possibly + different single sets S1 and S2. */ static bool -old_insns_match_p (int mode ATTRIBUTE_UNUSED, rtx i1, rtx i2) +equal_different_set_p (rtx p1, rtx s1, rtx p2, rtx s2) +{ + int i; + rtx e1, e2; + + if (p1 == s1 && p2 == s2) + return true; + + if (GET_CODE (p1) != PARALLEL || GET_CODE (p2) != PARALLEL) + return false; + + if (XVECLEN (p1, 0) != XVECLEN (p2, 0)) + return false; + + for (i = 0; i < XVECLEN (p1, 0); i++) + { + e1 = XVECEXP (p1, 0, i); + e2 = XVECEXP (p2, 0, i); + if (e1 == s1 && e2 == s2) + continue; + if (reload_completed + ? rtx_renumbered_equal_p (e1, e2) : rtx_equal_p (e1, e2)) + continue; + + return false; + } + + return true; +} + + +/* NOTE1 is the REG_EQUAL note, if any, attached to an insn + that is a single_set with a SET_SRC of SRC1. Similarly + for NOTE2/SRC2. + + So effectively NOTE1/NOTE2 are an alternate form of + SRC1/SRC2 respectively. + + Return nonzero if SRC1 or NOTE1 has the same constant + integer value as SRC2 or NOTE2. Else return zero. */ +static int +values_equal_p (rtx note1, rtx note2, rtx src1, rtx src2) +{ + if (note1 + && note2 + && CONST_INT_P (XEXP (note1, 0)) + && rtx_equal_p (XEXP (note1, 0), XEXP (note2, 0))) + return 1; + + if (!note1 + && !note2 + && CONST_INT_P (src1) + && CONST_INT_P (src2) + && rtx_equal_p (src1, src2)) + return 1; + + if (note1 + && CONST_INT_P (src2) + && rtx_equal_p (XEXP (note1, 0), src2)) + return 1; + + if (note2 + && CONST_INT_P (src1) + && rtx_equal_p (XEXP (note2, 0), src1)) + return 1; + + return 0; +} + +/* Examine register notes on I1 and I2 and return: + - dir_forward if I1 can be replaced by I2, or + - dir_backward if I2 can be replaced by I1, or + - dir_both if both are the case. */ + +static enum replace_direction +can_replace_by (rtx_insn *i1, rtx_insn *i2) +{ + rtx s1, s2, d1, d2, src1, src2, note1, note2; + bool c1, c2; + + /* Check for 2 sets. */ + s1 = single_set (i1); + s2 = single_set (i2); + if (s1 == NULL_RTX || s2 == NULL_RTX) + return dir_none; + + /* Check that the 2 sets set the same dest. */ + d1 = SET_DEST (s1); + d2 = SET_DEST (s2); + if (!(reload_completed + ? rtx_renumbered_equal_p (d1, d2) : rtx_equal_p (d1, d2))) + return dir_none; + + /* Find identical req_equiv or reg_equal note, which implies that the 2 sets + set dest to the same value. */ + note1 = find_reg_equal_equiv_note (i1); + note2 = find_reg_equal_equiv_note (i2); + + src1 = SET_SRC (s1); + src2 = SET_SRC (s2); + + if (!values_equal_p (note1, note2, src1, src2)) + return dir_none; + + if (!equal_different_set_p (PATTERN (i1), s1, PATTERN (i2), s2)) + return dir_none; + + /* Although the 2 sets set dest to the same value, we cannot replace + (set (dest) (const_int)) + by + (set (dest) (reg)) + because we don't know if the reg is live and has the same value at the + location of replacement. */ + c1 = CONST_INT_P (src1); + c2 = CONST_INT_P (src2); + if (c1 && c2) + return dir_both; + else if (c2) + return dir_forward; + else if (c1) + return dir_backward; + + return dir_none; +} + +/* Merges directions A and B. */ + +static enum replace_direction +merge_dir (enum replace_direction a, enum replace_direction b) +{ + /* Implements the following table: + |bo fw bw no + ---+----------- + bo |bo fw bw no + fw |-- fw no no + bw |-- -- bw no + no |-- -- -- no. */ + + if (a == b) + return a; + + if (a == dir_both) + return b; + if (b == dir_both) + return a; + + return dir_none; +} + +/* Array of flags indexed by reg note kind, true if the given + reg note is CFA related. */ +static const bool reg_note_cfa_p[] = { +#undef REG_CFA_NOTE +#define DEF_REG_NOTE(NAME) false, +#define REG_CFA_NOTE(NAME) true, +#include "reg-notes.def" +#undef REG_CFA_NOTE +#undef DEF_REG_NOTE + false +}; + +/* Return true if I1 and I2 have identical CFA notes (the same order + and equivalent content). */ + +static bool +insns_have_identical_cfa_notes (rtx_insn *i1, rtx_insn *i2) +{ + rtx n1, n2; + for (n1 = REG_NOTES (i1), n2 = REG_NOTES (i2); ; + n1 = XEXP (n1, 1), n2 = XEXP (n2, 1)) + { + /* Skip over reg notes not related to CFI information. */ + while (n1 && !reg_note_cfa_p[REG_NOTE_KIND (n1)]) + n1 = XEXP (n1, 1); + while (n2 && !reg_note_cfa_p[REG_NOTE_KIND (n2)]) + n2 = XEXP (n2, 1); + if (n1 == NULL_RTX && n2 == NULL_RTX) + return true; + if (n1 == NULL_RTX || n2 == NULL_RTX) + return false; + if (XEXP (n1, 0) == XEXP (n2, 0)) + ; + else if (XEXP (n1, 0) == NULL_RTX || XEXP (n2, 0) == NULL_RTX) + return false; + else if (!(reload_completed + ? rtx_renumbered_equal_p (XEXP (n1, 0), XEXP (n2, 0)) + : rtx_equal_p (XEXP (n1, 0), XEXP (n2, 0)))) + return false; + } +} + +/* Examine I1 and I2 and return: + - dir_forward if I1 can be replaced by I2, or + - dir_backward if I2 can be replaced by I1, or + - dir_both if both are the case. */ + +static enum replace_direction +old_insns_match_p (int mode ATTRIBUTE_UNUSED, rtx_insn *i1, rtx_insn *i2) { rtx p1, p2; /* Verify that I1 and I2 are equivalent. */ if (GET_CODE (i1) != GET_CODE (i2)) - return false; + return dir_none; /* __builtin_unreachable() may lead to empty blocks (ending with NOTE_INSN_BASIC_BLOCK). They may be crossjumped. */ if (NOTE_INSN_BASIC_BLOCK_P (i1) && NOTE_INSN_BASIC_BLOCK_P (i2)) - return true; + return dir_both; + + /* ??? Do not allow cross-jumping between different stack levels. */ + p1 = find_reg_note (i1, REG_ARGS_SIZE, NULL); + p2 = find_reg_note (i2, REG_ARGS_SIZE, NULL); + if (p1 && p2) + { + p1 = XEXP (p1, 0); + p2 = XEXP (p2, 0); + if (!rtx_equal_p (p1, p2)) + return dir_none; + + /* ??? Worse, this adjustment had better be constant lest we + have differing incoming stack levels. */ + if (!frame_pointer_needed + && find_args_size_adjust (i1) == HOST_WIDE_INT_MIN) + return dir_none; + } + else if (p1 || p2) + return dir_none; + + /* Do not allow cross-jumping between frame related insns and other + insns. */ + if (RTX_FRAME_RELATED_P (i1) != RTX_FRAME_RELATED_P (i2)) + return dir_none; p1 = PATTERN (i1); p2 = PATTERN (i2); if (GET_CODE (p1) != GET_CODE (p2)) - return false; + return dir_none; /* If this is a CALL_INSN, compare register usage information. If we don't check this on stack register machines, the two @@ -991,17 +1216,44 @@ rtx n2 = find_reg_note (i2, REG_EH_REGION, 0); if (!n1 && n2) - return false; + return dir_none; if (n1 && (!n2 || XEXP (n1, 0) != XEXP (n2, 0))) - return false; + return dir_none; if (!rtx_equal_p (CALL_INSN_FUNCTION_USAGE (i1), CALL_INSN_FUNCTION_USAGE (i2)) || SIBLING_CALL_P (i1) != SIBLING_CALL_P (i2)) - return false; + return dir_none; + + /* For address sanitizer, never crossjump __asan_report_* builtins, + otherwise errors might be reported on incorrect lines. */ + if (flag_sanitize & SANITIZE_ADDRESS) + { + rtx call = get_call_rtx_from (i1); + if (call && GET_CODE (XEXP (XEXP (call, 0), 0)) == SYMBOL_REF) + { + rtx symbol = XEXP (XEXP (call, 0), 0); + if (SYMBOL_REF_DECL (symbol) + && TREE_CODE (SYMBOL_REF_DECL (symbol)) == FUNCTION_DECL) + { + if ((DECL_BUILT_IN_CLASS (SYMBOL_REF_DECL (symbol)) + == BUILT_IN_NORMAL) + && DECL_FUNCTION_CODE (SYMBOL_REF_DECL (symbol)) + >= BUILT_IN_ASAN_REPORT_LOAD1 + && DECL_FUNCTION_CODE (SYMBOL_REF_DECL (symbol)) + <= BUILT_IN_ASAN_STOREN) + return dir_none; + } + } + } } + /* If both i1 and i2 are frame related, verify all the CFA notes + in the same order and with the same content. */ + if (RTX_FRAME_RELATED_P (i1) && !insns_have_identical_cfa_notes (i1, i2)) + return dir_none; + #ifdef STACK_REGS /* If cross_jump_death_matters is not 0, the insn's mode indicates whether or not the insn contains any stack-like @@ -1028,22 +1280,22 @@ SET_HARD_REG_BIT (i2_regset, REGNO (XEXP (note, 0))); if (!hard_reg_set_equal_p (i1_regset, i2_regset)) - return false; + return dir_none; } #endif if (reload_completed ? rtx_renumbered_equal_p (p1, p2) : rtx_equal_p (p1, p2)) - return true; - - return false; + return dir_both; + + return can_replace_by (i1, i2); } /* When comparing insns I1 and I2 in flow_find_cross_jump or flow_find_head_matching_sequence, ensure the notes match. */ static void -merge_notes (rtx i1, rtx i2) +merge_notes (rtx_insn *i1, rtx_insn *i2) { /* If the merged insns have different REG_EQUAL notes, then remove them. */ @@ -1062,24 +1314,76 @@ } } + /* Walks from I1 in BB1 backward till the next non-debug insn, and returns the + resulting insn in I1, and the corresponding bb in BB1. At the head of a + bb, if there is a predecessor bb that reaches this bb via fallthru, and + FOLLOW_FALLTHRU, walks further in the predecessor bb and registers this in + DID_FALLTHRU. Otherwise, stops at the head of the bb. */ + +static void +walk_to_nondebug_insn (rtx_insn **i1, basic_block *bb1, bool follow_fallthru, + bool *did_fallthru) +{ + edge fallthru; + + *did_fallthru = false; + + /* Ignore notes. */ + while (!NONDEBUG_INSN_P (*i1)) + { + if (*i1 != BB_HEAD (*bb1)) + { + *i1 = PREV_INSN (*i1); + continue; + } + + if (!follow_fallthru) + return; + + fallthru = find_fallthru_edge ((*bb1)->preds); + if (!fallthru || fallthru->src == ENTRY_BLOCK_PTR_FOR_FN (cfun) + || !single_succ_p (fallthru->src)) + return; + + *bb1 = fallthru->src; + *i1 = BB_END (*bb1); + *did_fallthru = true; + } +} + /* Look through the insns at the end of BB1 and BB2 and find the longest - sequence that are equivalent. Store the first insns for that sequence - in *F1 and *F2 and return the sequence length. + sequence that are either equivalent, or allow forward or backward + replacement. Store the first insns for that sequence in *F1 and *F2 and + return the sequence length. + + DIR_P indicates the allowed replacement direction on function entry, and + the actual replacement direction on function exit. If NULL, only equivalent + sequences are allowed. To simplify callers of this function, if the blocks match exactly, store the head of the blocks in *F1 and *F2. */ int -flow_find_cross_jump (basic_block bb1, basic_block bb2, rtx *f1, rtx *f2) +flow_find_cross_jump (basic_block bb1, basic_block bb2, rtx_insn **f1, + rtx_insn **f2, enum replace_direction *dir_p) { - rtx i1, i2, last1, last2, afterlast1, afterlast2; + rtx_insn *i1, *i2, *last1, *last2, *afterlast1, *afterlast2; int ninsns = 0; + enum replace_direction dir, last_dir, afterlast_dir; + bool follow_fallthru, did_fallthru; + + if (dir_p) + dir = *dir_p; + else + dir = dir_both; + afterlast_dir = dir; + last_dir = afterlast_dir; /* Skip simple jumps at the end of the blocks. Complex jumps still need to be compared for equivalence, which we'll do below. */ i1 = BB_END (bb1); - last1 = afterlast1 = last2 = afterlast2 = NULL_RTX; + last1 = afterlast1 = last2 = afterlast2 = NULL; if (onlyjump_p (i1) || (returnjump_p (i1) && !side_effects_p (PATTERN (i1)))) { @@ -1092,25 +1396,53 @@ || (returnjump_p (i2) && !side_effects_p (PATTERN (i2)))) { last2 = i2; - /* Count everything except for unconditional jump as insn. */ - if (!simplejump_p (i2) && !returnjump_p (i2) && last1) + /* Count everything except for unconditional jump as insn. + Don't count any jumps if dir_p is NULL. */ + if (!simplejump_p (i2) && !returnjump_p (i2) && last1 && dir_p) ninsns++; i2 = PREV_INSN (i2); } while (true) { - /* Ignore notes. */ - while (!NONDEBUG_INSN_P (i1) && i1 != BB_HEAD (bb1)) - i1 = PREV_INSN (i1); - - while (!NONDEBUG_INSN_P (i2) && i2 != BB_HEAD (bb2)) - i2 = PREV_INSN (i2); + /* In the following example, we can replace all jumps to C by jumps to A. + + This removes 4 duplicate insns. + [bb A] insn1 [bb C] insn1 + insn2 insn2 + [bb B] insn3 insn3 + insn4 insn4 + jump_insn jump_insn + + We could also replace all jumps to A by jumps to C, but that leaves B + alive, and removes only 2 duplicate insns. In a subsequent crossjump + step, all jumps to B would be replaced with jumps to the middle of C, + achieving the same result with more effort. + So we allow only the first possibility, which means that we don't allow + fallthru in the block that's being replaced. */ + + follow_fallthru = dir_p && dir != dir_forward; + walk_to_nondebug_insn (&i1, &bb1, follow_fallthru, &did_fallthru); + if (did_fallthru) + dir = dir_backward; + + follow_fallthru = dir_p && dir != dir_backward; + walk_to_nondebug_insn (&i2, &bb2, follow_fallthru, &did_fallthru); + if (did_fallthru) + dir = dir_forward; if (i1 == BB_HEAD (bb1) || i2 == BB_HEAD (bb2)) break; - if (!old_insns_match_p (0, i1, i2)) + /* Do not turn corssing edge to non-crossing or vice versa after + reload. */ + if (BB_PARTITION (BLOCK_FOR_INSN (i1)) + != BB_PARTITION (BLOCK_FOR_INSN (i2)) + && reload_completed) + break; + + dir = merge_dir (dir, old_insns_match_p (0, i1, i2)); + if (dir == dir_none || (!dir_p && dir != dir_both)) break; merge_memattrs (i1, i2); @@ -1122,31 +1454,35 @@ afterlast1 = last1, afterlast2 = last2; last1 = i1, last2 = i2; - ninsns++; + afterlast_dir = last_dir; + last_dir = dir; + if (active_insn_p (i1)) + ninsns++; } i1 = PREV_INSN (i1); i2 = PREV_INSN (i2); } -#ifdef HAVE_cc0 /* Don't allow the insn after a compare to be shared by cross-jumping unless the compare is also shared. */ - if (ninsns && reg_mentioned_p (cc0_rtx, last1) && ! sets_cc0_p (last1)) - last1 = afterlast1, last2 = afterlast2, ninsns--; -#endif + if (HAVE_cc0 && ninsns && reg_mentioned_p (cc0_rtx, last1) + && ! sets_cc0_p (last1)) + last1 = afterlast1, last2 = afterlast2, last_dir = afterlast_dir, ninsns--; /* Include preceding notes and labels in the cross-jump. One, this may bring us to the head of the blocks as requested above. Two, it keeps line number notes as matched as may be. */ if (ninsns) { + bb1 = BLOCK_FOR_INSN (last1); while (last1 != BB_HEAD (bb1) && !NONDEBUG_INSN_P (PREV_INSN (last1))) last1 = PREV_INSN (last1); if (last1 != BB_HEAD (bb1) && LABEL_P (PREV_INSN (last1))) last1 = PREV_INSN (last1); + bb2 = BLOCK_FOR_INSN (last2); while (last2 != BB_HEAD (bb2) && !NONDEBUG_INSN_P (PREV_INSN (last2))) last2 = PREV_INSN (last2); @@ -1157,19 +1493,22 @@ *f2 = last2; } + if (dir_p) + *dir_p = last_dir; return ninsns; } /* Like flow_find_cross_jump, except start looking for a matching sequence from the head of the two blocks. Do not include jumps at the end. If STOP_AFTER is nonzero, stop after finding that many matching - instructions. */ + instructions. If STOP_AFTER is zero, count all INSN_P insns, if it is + non-zero, only count active insns. */ int -flow_find_head_matching_sequence (basic_block bb1, basic_block bb2, rtx *f1, - rtx *f2, int stop_after) +flow_find_head_matching_sequence (basic_block bb1, basic_block bb2, rtx_insn **f1, + rtx_insn **f2, int stop_after) { - rtx i1, i2, last1, last2, beforelast1, beforelast2; + rtx_insn *i1, *i2, *last1, *last2, *beforelast1, *beforelast2; int ninsns = 0; edge e; edge_iterator ei; @@ -1184,7 +1523,7 @@ i1 = BB_HEAD (bb1); i2 = BB_HEAD (bb2); - last1 = beforelast1 = last2 = beforelast2 = NULL_RTX; + last1 = beforelast1 = last2 = beforelast2 = NULL; while (true) { @@ -1223,7 +1562,7 @@ && nehedges1 != nehedges2)) break; - if (!old_insns_match_p (0, i1, i2)) + if (old_insns_match_p (0, i1, i2) != dir_both) break; merge_memattrs (i1, i2); @@ -1235,7 +1574,8 @@ beforelast1 = last1, beforelast2 = last2; last1 = i1, last2 = i2; - ninsns++; + if (!stop_after || active_insn_p (i1)) + ninsns++; } if (i1 == BB_END (bb1) || i2 == BB_END (bb2) @@ -1246,12 +1586,11 @@ i2 = NEXT_INSN (i2); } -#ifdef HAVE_cc0 /* Don't allow a compare to be shared by cross-jumping unless the insn after the compare is also shared. */ - if (ninsns && reg_mentioned_p (cc0_rtx, last1) && sets_cc0_p (last1)) + if (HAVE_cc0 && ninsns && reg_mentioned_p (cc0_rtx, last1) + && sets_cc0_p (last1)) last1 = beforelast1, last2 = beforelast2, ninsns--; -#endif if (ninsns) { @@ -1276,6 +1615,17 @@ edge e1, e2; edge_iterator ei; + /* If we performed shrink-wrapping, edges to the exit block can + only be distinguished for JUMP_INSNs. The two paths may differ in + whether they went through the prologue. Sibcalls are fine, we know + that we either didn't need or inserted an epilogue before them. */ + if (crtl->shrink_wrapped + && single_succ_p (bb1) + && single_succ (bb1) == EXIT_BLOCK_PTR_FOR_FN (cfun) + && !JUMP_P (BB_END (bb1)) + && !(CALL_P (BB_END (bb1)) && SIBLING_CALL_P (BB_END (bb1)))) + return false; + /* If BB1 has only one successor, we may be looking at either an unconditional jump, or a fake edge to exit. */ if (single_succ_p (bb1) @@ -1366,24 +1716,28 @@ && optimize_bb_for_speed_p (bb1) && optimize_bb_for_speed_p (bb2)) { - int prob2; + profile_probability prob2; if (b1->dest == b2->dest) prob2 = b2->probability; else /* Do not use f2 probability as f2 may be forwarded. */ - prob2 = REG_BR_PROB_BASE - b2->probability; + prob2 = b2->probability.invert (); /* Fail if the difference in probabilities is greater than 50%. This rules out two well-predicted branches with opposite outcomes. */ - if (abs (b1->probability - prob2) > REG_BR_PROB_BASE / 2) + if (b1->probability.differs_lot_from_p (prob2)) { if (dump_file) - fprintf (dump_file, - "Outcomes of branch in bb %i and %i differ too much (%i %i)\n", - bb1->index, bb2->index, b1->probability, prob2); - + { + fprintf (dump_file, + "Outcomes of branch in bb %i and %i differ too" + " much (", bb1->index, bb2->index); + b1->probability.dump (dump_file); + prob2.dump (dump_file); + fprintf (dump_file, ")\n"); + } return false; } } @@ -1401,8 +1755,8 @@ /* Check whether there are tablejumps in the end of BB1 and BB2. Return true if they are identical. */ { - rtx label1, label2; - rtx table1, table2; + rtx_insn *label1, *label2; + rtx_jump_table_data *table1, *table2; if (tablejump_p (BB_END (bb1), &label1, &table1) && tablejump_p (BB_END (bb2), &label2, &table2) @@ -1442,17 +1796,14 @@ if (identical) { - replace_label_data rr; bool match; /* Temporarily replace references to LABEL1 with LABEL2 in BB1->END so that we could compare the instructions. */ - rr.r1 = label1; - rr.r2 = label2; - rr.update_label_nuses = false; - for_each_rtx (&BB_END (bb1), replace_label, &rr); - - match = old_insns_match_p (mode, BB_END (bb1), BB_END (bb2)); + replace_label_in_insn (BB_END (bb1), label1, label2, false); + + match = (old_insns_match_p (mode, BB_END (bb1), BB_END (bb2)) + == dir_both); if (dump_file && match) fprintf (dump_file, "Tablejumps in bb %i and %i match.\n", @@ -1461,9 +1812,7 @@ /* Set the original label in BB1->END because when deleting a block whose end is a tablejump, the tablejump referenced from the instruction is deleted too. */ - rr.r1 = label2; - rr.r2 = label1; - for_each_rtx (&BB_END (bb1), replace_label, &rr); + replace_label_in_insn (BB_END (bb1), label2, label1, false); return match; } @@ -1472,9 +1821,23 @@ } } + /* Find the last non-debug non-note instruction in each bb, except + stop when we see the NOTE_INSN_BASIC_BLOCK, as old_insns_match_p + handles that case specially. old_insns_match_p does not handle + other types of instruction notes. */ + rtx_insn *last1 = BB_END (bb1); + rtx_insn *last2 = BB_END (bb2); + while (!NOTE_INSN_BASIC_BLOCK_P (last1) && + (DEBUG_INSN_P (last1) || NOTE_P (last1))) + last1 = PREV_INSN (last1); + while (!NOTE_INSN_BASIC_BLOCK_P (last2) && + (DEBUG_INSN_P (last2) || NOTE_P (last2))) + last2 = PREV_INSN (last2); + gcc_assert (last1 && last2); + /* First ensure that the instructions match. There may be many outgoing edges so this test is generally cheaper. */ - if (!old_insns_match_p (mode, BB_END (bb1), BB_END (bb2))) + if (old_insns_match_p (mode, last1, last2) != dir_both) return false; /* Search the outgoing edges, ensure that the counts do match, find possible @@ -1483,10 +1846,14 @@ if (EDGE_COUNT (bb1->succs) != EDGE_COUNT (bb2->succs)) return false; + bool nonfakeedges = false; FOR_EACH_EDGE (e1, ei, bb1->succs) { e2 = EDGE_SUCC (bb2, ei.index); + if ((e1->flags & EDGE_FAKE) == 0) + nonfakeedges = true; + if (e1->flags & EDGE_EH) nehedges1++; @@ -1504,6 +1871,18 @@ || (fallthru1 != 0) != (fallthru2 != 0)) return false; + /* If !ACCUMULATE_OUTGOING_ARGS, bb1 (and bb2) have no successors + and the last real insn doesn't have REG_ARGS_SIZE note, don't + attempt to optimize, as the two basic blocks might have different + REG_ARGS_SIZE depths. For noreturn calls and unconditional + traps there should be REG_ARG_SIZE notes, they could be missing + for __builtin_unreachable () uses though. */ + if (!nonfakeedges + && !ACCUMULATE_OUTGOING_ARGS + && (!INSN_P (last1) + || !find_reg_note (last1, REG_ARGS_SIZE, NULL))) + return false; + /* fallthru edges must be forwarded to the same destination. */ if (fallthru1) { @@ -1567,31 +1946,23 @@ /* E1 and E2 are edges with the same destination block. Search their predecessors for common code. If found, redirect control flow from - (maybe the middle of) E1->SRC to (maybe the middle of) E2->SRC. */ + (maybe the middle of) E1->SRC to (maybe the middle of) E2->SRC (dir_forward), + or the other way around (dir_backward). DIR specifies the allowed + replacement direction. */ static bool -try_crossjump_to_edge (int mode, edge e1, edge e2) +try_crossjump_to_edge (int mode, edge e1, edge e2, + enum replace_direction dir) { int nmatch; basic_block src1 = e1->src, src2 = e2->src; basic_block redirect_to, redirect_from, to_remove; - rtx newpos1, newpos2; + basic_block osrc1, osrc2, redirect_edges_to, tmp; + rtx_insn *newpos1, *newpos2; edge s; edge_iterator ei; - newpos1 = newpos2 = NULL_RTX; - - /* If we have partitioned hot/cold basic blocks, it is a bad idea - to try this optimization. - - Basic block partitioning may result in some jumps that appear to - be optimizable (or blocks that appear to be mergeable), but which really - must be left untouched (they are required to make it safely across - partition boundaries). See the comments at the top of - bb-reorder.c:partition_hot_cold_basic_blocks for complete details. */ - - if (flag_reorder_blocks_and_partition && reload_completed) - return false; + newpos1 = newpos2 = NULL; /* Search backward through forwarder blocks. We don't need to worry about multiple entry or chained forwarders, as they will be optimized @@ -1606,7 +1977,8 @@ e2 = single_pred_edge (src2), src2 = e2->src; /* Nothing to do if we reach ENTRY, or a common source block. */ - if (src1 == ENTRY_BLOCK_PTR || src2 == ENTRY_BLOCK_PTR) + if (src1 == ENTRY_BLOCK_PTR_FOR_FN (cfun) || src2 + == ENTRY_BLOCK_PTR_FOR_FN (cfun)) return false; if (src1 == src2) return false; @@ -1625,12 +1997,37 @@ if (EDGE_COUNT (src1->preds) == 0 || EDGE_COUNT (src2->preds) == 0) return false; + /* Do not turn corssing edge to non-crossing or vice versa after reload. */ + if (BB_PARTITION (src1) != BB_PARTITION (src2) + && reload_completed) + return false; + /* Look for the common insn sequence, part the first ... */ if (!outgoing_edges_match (mode, src1, src2)) return false; /* ... and part the second. */ - nmatch = flow_find_cross_jump (src1, src2, &newpos1, &newpos2); + nmatch = flow_find_cross_jump (src1, src2, &newpos1, &newpos2, &dir); + + osrc1 = src1; + osrc2 = src2; + if (newpos1 != NULL_RTX) + src1 = BLOCK_FOR_INSN (newpos1); + if (newpos2 != NULL_RTX) + src2 = BLOCK_FOR_INSN (newpos2); + + /* Check that SRC1 and SRC2 have preds again. They may have changed + above due to the call to flow_find_cross_jump. */ + if (EDGE_COUNT (src1->preds) == 0 || EDGE_COUNT (src2->preds) == 0) + return false; + + if (dir == dir_backward) + { + std::swap (osrc1, osrc2); + std::swap (src1, src2); + std::swap (e1, e2); + std::swap (newpos1, newpos2); + } /* Don't proceed with the crossjump unless we found a sufficient number of matching instructions or the 'from' block was totally matched @@ -1650,31 +2047,27 @@ If we have tablejumps in the end of SRC1 and SRC2 they have been already compared for equivalence in outgoing_edges_match () so replace the references to TABLE1 by references to TABLE2. */ - { - rtx label1, label2; - rtx table1, table2; - - if (tablejump_p (BB_END (src1), &label1, &table1) - && tablejump_p (BB_END (src2), &label2, &table2) + { + rtx_insn *label1, *label2; + rtx_jump_table_data *table1, *table2; + + if (tablejump_p (BB_END (osrc1), &label1, &table1) + && tablejump_p (BB_END (osrc2), &label2, &table2) && label1 != label2) { - replace_label_data rr; - rtx insn; + rtx_insn *insn; /* Replace references to LABEL1 with LABEL2. */ - rr.r1 = label1; - rr.r2 = label2; - rr.update_label_nuses = true; for (insn = get_insns (); insn; insn = NEXT_INSN (insn)) { /* Do not replace the label in SRC1->END because when deleting a block whose end is a tablejump, the tablejump referenced from the instruction is deleted too. */ - if (insn != BB_END (src1)) - for_each_rtx (&insn, replace_label, &rr); + if (insn != BB_END (osrc1)) + replace_label_in_insn (insn, label1, label2, true); } } - } + } /* Avoid splitting if possible. We must always split when SRC2 has EH predecessor edges, or we may end up with basic blocks with both @@ -1711,8 +2104,13 @@ /* We may have some registers visible through the block. */ df_set_bb_dirty (redirect_to); + if (osrc2 == src2) + redirect_edges_to = redirect_to; + else + redirect_edges_to = osrc2; + /* Recompute the frequencies and counts of outgoing edges. */ - FOR_EACH_EDGE (s, ei, redirect_to->succs) + FOR_EACH_EDGE (s, ei, redirect_edges_to->succs) { edge s2; edge_iterator ei; @@ -1730,49 +2128,44 @@ break; } - s->count += s2->count; - /* Take care to update possible forwarder blocks. We verified that there is no more than one in the chain, so we can't run into infinite loop. */ if (FORWARDER_BLOCK_P (s->dest)) { - single_succ_edge (s->dest)->count += s2->count; - s->dest->count += s2->count; s->dest->frequency += EDGE_FREQUENCY (s); } if (FORWARDER_BLOCK_P (s2->dest)) { - single_succ_edge (s2->dest)->count -= s2->count; - if (single_succ_edge (s2->dest)->count < 0) - single_succ_edge (s2->dest)->count = 0; - s2->dest->count -= s2->count; s2->dest->frequency -= EDGE_FREQUENCY (s); if (s2->dest->frequency < 0) s2->dest->frequency = 0; - if (s2->dest->count < 0) - s2->dest->count = 0; } - if (!redirect_to->frequency && !src1->frequency) - s->probability = (s->probability + s2->probability) / 2; - else - s->probability - = ((s->probability * redirect_to->frequency + - s2->probability * src1->frequency) - / (redirect_to->frequency + src1->frequency)); + if (!redirect_edges_to->frequency && !src1->frequency) + s->probability = s->probability.combine_with_freq + (redirect_edges_to->frequency, + s2->probability, src1->frequency); } /* Adjust count and frequency for the block. An earlier jump threading pass may have left the profile in an inconsistent state (see update_bb_profile_for_threading) so we must be prepared for overflows. */ - redirect_to->count += src1->count; - redirect_to->frequency += src1->frequency; - if (redirect_to->frequency > BB_FREQ_MAX) - redirect_to->frequency = BB_FREQ_MAX; - update_br_prob_note (redirect_to); + tmp = redirect_to; + do + { + tmp->count += src1->count; + tmp->frequency += src1->frequency; + if (tmp->frequency > BB_FREQ_MAX) + tmp->frequency = BB_FREQ_MAX; + if (tmp == redirect_edges_to) + break; + tmp = find_fallthru_edge (tmp->succs)->dest; + } + while (true); + update_br_prob_note (redirect_edges_to); /* Edit SRC1 to go to REDIRECT_TO at NEWPOS1. */ @@ -1812,7 +2205,6 @@ edge e, e2, fallthru; bool changed; unsigned max, ix, ix2; - basic_block ev, ev2; /* Nothing to do if there is not at least two incoming edges. */ if (EDGE_COUNT (bb->preds) < 2) @@ -1821,7 +2213,7 @@ /* Don't crossjump if this block ends in a computed jump, unless we are optimizing for size. */ if (optimize_bb_for_size_p (bb) - && bb != EXIT_BLOCK_PTR + && bb != EXIT_BLOCK_PTR_FOR_FN (cfun) && computed_jump_p (BB_END (bb))) return false; @@ -1852,9 +2244,9 @@ fallthru = find_fallthru_edge (bb->preds); changed = false; - for (ix = 0, ev = bb; ix < EDGE_COUNT (ev->preds); ) + for (ix = 0; ix < EDGE_COUNT (bb->preds);) { - e = EDGE_PRED (ev, ix); + e = EDGE_PRED (bb, ix); ix++; /* As noted above, first try with the fallthru predecessor (or, a @@ -1872,11 +2264,10 @@ || (fallthru->src->flags & BB_MODIFIED))) continue; - if (try_crossjump_to_edge (mode, e, fallthru)) + if (try_crossjump_to_edge (mode, e, fallthru, dir_forward)) { changed = true; ix = 0; - ev = bb; continue; } } @@ -1896,10 +2287,9 @@ if (EDGE_SUCC (e->src, 0) != e) continue; - for (ix2 = 0, ev2 = bb; ix2 < EDGE_COUNT (ev2->preds); ) + for (ix2 = 0; ix2 < EDGE_COUNT (bb->preds); ix2++) { - e2 = EDGE_PRED (ev2, ix2); - ix2++; + e2 = EDGE_PRED (bb, ix2); if (e2 == e) continue; @@ -1922,10 +2312,11 @@ || (e2->src->flags & BB_MODIFIED))) continue; - if (try_crossjump_to_edge (mode, e, e2)) + /* Both e and e2 are not fallthru edges, so we can crossjump in either + direction. */ + if (try_crossjump_to_edge (mode, e, e2, dir_both)) { changed = true; - ev2 = bb; ix = 0; break; } @@ -1933,7 +2324,7 @@ } if (changed) - crossjumps_occured = true; + crossjumps_occurred = true; return changed; } @@ -1948,12 +2339,14 @@ basic_block final_dest_bb = NULL; int max_match = INT_MAX; edge e0; - rtx *headptr, *currptr, *nextptr; + rtx_insn **headptr, **currptr, **nextptr; bool changed, moveall; unsigned ix; - rtx e0_last_head, cond, move_before; + rtx_insn *e0_last_head; + rtx cond; + rtx_insn *move_before; unsigned nedges = EDGE_COUNT (bb->succs); - rtx jump = BB_END (bb); + rtx_insn *jump = BB_END (bb); regset live, live_union; /* Nothing to do if there is not at least two outgoing edges. */ @@ -1963,16 +2356,21 @@ /* Don't crossjump if this block ends in a computed jump, unless we are optimizing for size. */ if (optimize_bb_for_size_p (bb) - && bb != EXIT_BLOCK_PTR + && bb != EXIT_BLOCK_PTR_FOR_FN (cfun) && computed_jump_p (BB_END (bb))) return false; cond = get_condition (jump, &move_before, true, false); if (cond == NULL_RTX) - move_before = jump; + { + if (HAVE_cc0 && reg_mentioned_p (cc0_rtx, jump)) + move_before = prev_nonnote_nondebug_insn (jump); + else + move_before = jump; + } for (ix = 0; ix < nedges; ix++) - if (EDGE_SUCC (bb, ix)->dest == EXIT_BLOCK_PTR) + if (EDGE_SUCC (bb, ix)->dest == EXIT_BLOCK_PTR_FOR_FN (cfun)) return false; for (ix = 0; ix < nedges; ix++) @@ -2036,13 +2434,13 @@ } e0 = EDGE_SUCC (bb, 0); - e0_last_head = NULL_RTX; + e0_last_head = NULL; changed = false; for (ix = 1; ix < nedges; ix++) { edge e = EDGE_SUCC (bb, ix); - rtx e0_last, e_last; + rtx_insn *e0_last, *e_last; int nmatch; nmatch = flow_find_head_matching_sequence (e0->dest, e->dest, @@ -2079,15 +2477,15 @@ live = BITMAP_ALLOC (NULL); live_union = BITMAP_ALLOC (NULL); - currptr = XNEWVEC (rtx, nedges); - headptr = XNEWVEC (rtx, nedges); - nextptr = XNEWVEC (rtx, nedges); + currptr = XNEWVEC (rtx_insn *, nedges); + headptr = XNEWVEC (rtx_insn *, nedges); + nextptr = XNEWVEC (rtx_insn *, nedges); for (ix = 0; ix < nedges; ix++) { int j; basic_block merge_bb = EDGE_SUCC (bb, ix)->dest; - rtx head = BB_HEAD (merge_bb); + rtx_insn *head = BB_HEAD (merge_bb); while (!NONDEBUG_INSN_P (head)) head = NEXT_INSN (head); @@ -2108,7 +2506,7 @@ with the final move. */ if (final_dest_bb != NULL) { - rtx move_upto; + rtx_insn *move_upto; moveall = can_move_insns_across (currptr[0], e0_last_head, move_before, jump, e0->dest, live_union, @@ -2131,12 +2529,17 @@ jump = BB_END (final_dest_bb); cond = get_condition (jump, &move_before, true, false); if (cond == NULL_RTX) - move_before = jump; + { + if (HAVE_cc0 && reg_mentioned_p (cc0_rtx, jump)) + move_before = prev_nonnote_nondebug_insn (jump); + else + move_before = jump; + } } do { - rtx move_upto; + rtx_insn *move_upto; moveall = can_move_insns_across (currptr[0], e0_last_head, move_before, jump, e0->dest, live_union, NULL, &move_upto); @@ -2148,12 +2551,10 @@ /* Try again, using a different insertion point. */ move_before = jump; -#ifdef HAVE_cc0 /* Don't try moving before a cc0 user, as that may invalidate the cc0. */ - if (reg_mentioned_p (cc0_rtx, jump)) + if (HAVE_cc0 && reg_mentioned_p (cc0_rtx, jump)) break; -#endif continue; } @@ -2170,7 +2571,7 @@ break; for (ix = 0; ix < nedges; ix++) { - rtx curr = currptr[ix]; + rtx_insn *curr = currptr[ix]; do curr = NEXT_INSN (curr); while (!NONDEBUG_INSN_P (curr)); @@ -2183,7 +2584,7 @@ if (!moveall) for (ix = 0; ix < nedges; ix++) { - rtx curr = currptr[ix]; + rtx_insn *curr = currptr[ix]; do curr = NEXT_INSN (curr); while (!NONDEBUG_INSN_P (curr)); @@ -2208,12 +2609,10 @@ /* For the unmerged insns, try a different insertion point. */ move_before = jump; -#ifdef HAVE_cc0 /* Don't try moving before a cc0 user, as that may invalidate the cc0. */ - if (reg_mentioned_p (cc0_rtx, jump)) + if (HAVE_cc0 && reg_mentioned_p (cc0_rtx, jump)) break; -#endif for (ix = 0; ix < nedges; ix++) currptr[ix] = headptr[ix] = nextptr[ix]; @@ -2226,7 +2625,7 @@ free (headptr); free (nextptr); - crossjumps_occured |= changed; + crossjumps_occurred |= changed; return changed; } @@ -2237,7 +2636,7 @@ static bool trivially_empty_bb_p (basic_block bb) { - rtx insn = BB_END (bb); + rtx_insn *insn = BB_END (bb); while (1) { @@ -2249,6 +2648,37 @@ } } +/* Return true if BB contains just a return and possibly a USE of the + return value. Fill in *RET and *USE with the return and use insns + if any found, otherwise NULL. All CLOBBERs are ignored. */ + +static bool +bb_is_just_return (basic_block bb, rtx_insn **ret, rtx_insn **use) +{ + *ret = *use = NULL; + rtx_insn *insn; + + if (bb == EXIT_BLOCK_PTR_FOR_FN (cfun)) + return false; + + FOR_BB_INSNS (bb, insn) + if (NONDEBUG_INSN_P (insn)) + { + rtx pat = PATTERN (insn); + + if (!*ret && ANY_RETURN_P (pat)) + *ret = insn; + else if (!*ret && !*use && GET_CODE (pat) == USE + && REG_P (XEXP (pat, 0)) + && REG_FUNCTION_VALUE_P (XEXP (pat, 0))) + *use = insn; + else if (GET_CODE (pat) != CLOBBER) + return false; + } + + return !!*ret; +} + /* Do simple CFG optimizations - basic block merging, simplifying of jump instructions etc. Return nonzero if changes were made. */ @@ -2263,9 +2693,9 @@ if (mode & (CLEANUP_CROSSJUMP | CLEANUP_THREADING)) clear_bb_flags (); - crossjumps_occured = false; - - FOR_EACH_BB (bb) + crossjumps_occurred = false; + + FOR_EACH_BB_FN (bb, cfun) update_forwarder_flag (bb); if (! targetm.cannot_modify_jumps_p ()) @@ -2285,7 +2715,8 @@ "\n\ntry_optimize_cfg iteration %i\n\n", iterations); - for (b = ENTRY_BLOCK_PTR->next_bb; b != EXIT_BLOCK_PTR;) + for (b = ENTRY_BLOCK_PTR_FOR_FN (cfun)->next_bb; b + != EXIT_BLOCK_PTR_FOR_FN (cfun);) { basic_block c; edge s; @@ -2302,7 +2733,8 @@ if (EDGE_COUNT (b->preds) == 0 || (EDGE_COUNT (b->succs) == 0 && trivially_empty_bb_p (b) - && single_succ_edge (ENTRY_BLOCK_PTR)->dest != b)) + && single_succ_edge (ENTRY_BLOCK_PTR_FOR_FN (cfun))->dest + != b)) { c = b->prev_bb; if (EDGE_COUNT (b->preds) > 0) @@ -2312,28 +2744,28 @@ if (current_ir_type () == IR_RTL_CFGLAYOUT) { - if (b->il.rtl->footer - && BARRIER_P (b->il.rtl->footer)) + if (BB_FOOTER (b) + && BARRIER_P (BB_FOOTER (b))) FOR_EACH_EDGE (e, ei, b->preds) if ((e->flags & EDGE_FALLTHRU) - && e->src->il.rtl->footer == NULL) + && BB_FOOTER (e->src) == NULL) { - if (b->il.rtl->footer) + if (BB_FOOTER (b)) { - e->src->il.rtl->footer = b->il.rtl->footer; - b->il.rtl->footer = NULL; + BB_FOOTER (e->src) = BB_FOOTER (b); + BB_FOOTER (b) = NULL; } else { start_sequence (); - e->src->il.rtl->footer = emit_barrier (); + BB_FOOTER (e->src) = emit_barrier (); end_sequence (); } } } else { - rtx last = get_last_bb_insn (b); + rtx_insn *last = get_last_bb_insn (b); if (last && BARRIER_P (last)) FOR_EACH_EDGE (e, ei, b->preds) if ((e->flags & EDGE_FALLTHRU)) @@ -2342,8 +2774,8 @@ } delete_basic_block (b); changed = true; - /* Avoid trying to remove ENTRY_BLOCK_PTR. */ - b = (c == ENTRY_BLOCK_PTR ? c->next_bb : c); + /* Avoid trying to remove the exit block. */ + b = (c == ENTRY_BLOCK_PTR_FOR_FN (cfun) ? c->next_bb : c); continue; } @@ -2352,38 +2784,26 @@ && (single_pred_edge (b)->flags & EDGE_FALLTHRU) && !(single_pred_edge (b)->flags & EDGE_COMPLEX) && LABEL_P (BB_HEAD (b)) + && !LABEL_PRESERVE_P (BB_HEAD (b)) /* If the previous block ends with a branch to this block, we can't delete the label. Normally this is a condjump that is yet to be simplified, but if CASE_DROPS_THRU, this can be a tablejump with some element going to the same place as the default (fallthru). */ - && (single_pred (b) == ENTRY_BLOCK_PTR + && (single_pred (b) == ENTRY_BLOCK_PTR_FOR_FN (cfun) || !JUMP_P (BB_END (single_pred (b))) || ! label_is_jump_target_p (BB_HEAD (b), BB_END (single_pred (b))))) { - rtx label = BB_HEAD (b); - - delete_insn_chain (label, label, false); - /* If the case label is undeletable, move it after the - BASIC_BLOCK note. */ - if (NOTE_KIND (BB_HEAD (b)) == NOTE_INSN_DELETED_LABEL) - { - rtx bb_note = NEXT_INSN (BB_HEAD (b)); - - reorder_insns_nobb (label, label, bb_note); - BB_HEAD (b) = bb_note; - if (BB_END (b) == bb_note) - BB_END (b) = label; - } + delete_insn (BB_HEAD (b)); if (dump_file) fprintf (dump_file, "Deleted label in block %i.\n", b->index); } /* If we fall through an empty block, we can remove it. */ - if (!(mode & CLEANUP_CFGLAYOUT) + if (!(mode & (CLEANUP_CFGLAYOUT | CLEANUP_NO_INSN_DEL)) && single_pred_p (b) && (single_pred_edge (b)->flags & EDGE_FALLTHRU) && !LABEL_P (BB_HEAD (b)) @@ -2391,14 +2811,15 @@ /* Note that forwarder_block_p true ensures that there is a successor for this block. */ && (single_succ_edge (b)->flags & EDGE_FALLTHRU) - && n_basic_blocks > NUM_FIXED_BLOCKS + 1) + && n_basic_blocks_for_fn (cfun) > NUM_FIXED_BLOCKS + 1) { if (dump_file) fprintf (dump_file, "Deleting fallthru block %i.\n", b->index); - c = b->prev_bb == ENTRY_BLOCK_PTR ? b->next_bb : b->prev_bb; + c = ((b->prev_bb == ENTRY_BLOCK_PTR_FOR_FN (cfun)) + ? b->next_bb : b->prev_bb); redirect_edge_succ_nodup (single_pred_edge (b), single_succ (b)); delete_basic_block (b); @@ -2411,7 +2832,7 @@ if (single_succ_p (b) && (s = single_succ_edge (b)) && !(s->flags & EDGE_COMPLEX) - && (c = s->dest) != EXIT_BLOCK_PTR + && (c = s->dest) != EXIT_BLOCK_PTR_FOR_FN (cfun) && single_pred_p (c) && b != c) { @@ -2444,6 +2865,99 @@ } } + /* Try to change a branch to a return to just that return. */ + rtx_insn *ret, *use; + if (single_succ_p (b) + && onlyjump_p (BB_END (b)) + && bb_is_just_return (single_succ (b), &ret, &use)) + { + if (redirect_jump (as_a <rtx_jump_insn *> (BB_END (b)), + PATTERN (ret), 0)) + { + if (use) + emit_insn_before (copy_insn (PATTERN (use)), + BB_END (b)); + if (dump_file) + fprintf (dump_file, "Changed jump %d->%d to return.\n", + b->index, single_succ (b)->index); + redirect_edge_succ (single_succ_edge (b), + EXIT_BLOCK_PTR_FOR_FN (cfun)); + single_succ_edge (b)->flags &= ~EDGE_CROSSING; + changed_here = true; + } + } + + /* Try to change a conditional branch to a return to the + respective conditional return. */ + if (EDGE_COUNT (b->succs) == 2 + && any_condjump_p (BB_END (b)) + && bb_is_just_return (BRANCH_EDGE (b)->dest, &ret, &use)) + { + if (redirect_jump (as_a <rtx_jump_insn *> (BB_END (b)), + PATTERN (ret), 0)) + { + if (use) + emit_insn_before (copy_insn (PATTERN (use)), + BB_END (b)); + if (dump_file) + fprintf (dump_file, "Changed conditional jump %d->%d " + "to conditional return.\n", + b->index, BRANCH_EDGE (b)->dest->index); + redirect_edge_succ (BRANCH_EDGE (b), + EXIT_BLOCK_PTR_FOR_FN (cfun)); + BRANCH_EDGE (b)->flags &= ~EDGE_CROSSING; + changed_here = true; + } + } + + /* Try to flip a conditional branch that falls through to + a return so that it becomes a conditional return and a + new jump to the original branch target. */ + if (EDGE_COUNT (b->succs) == 2 + && BRANCH_EDGE (b)->dest != EXIT_BLOCK_PTR_FOR_FN (cfun) + && any_condjump_p (BB_END (b)) + && bb_is_just_return (FALLTHRU_EDGE (b)->dest, &ret, &use)) + { + if (invert_jump (as_a <rtx_jump_insn *> (BB_END (b)), + JUMP_LABEL (BB_END (b)), 0)) + { + basic_block new_ft = BRANCH_EDGE (b)->dest; + if (redirect_jump (as_a <rtx_jump_insn *> (BB_END (b)), + PATTERN (ret), 0)) + { + if (use) + emit_insn_before (copy_insn (PATTERN (use)), + BB_END (b)); + if (dump_file) + fprintf (dump_file, "Changed conditional jump " + "%d->%d to conditional return, adding " + "fall-through jump.\n", + b->index, BRANCH_EDGE (b)->dest->index); + redirect_edge_succ (BRANCH_EDGE (b), + EXIT_BLOCK_PTR_FOR_FN (cfun)); + BRANCH_EDGE (b)->flags &= ~EDGE_CROSSING; + std::swap (BRANCH_EDGE (b)->probability, + FALLTHRU_EDGE (b)->probability); + update_br_prob_note (b); + basic_block jb = force_nonfallthru (FALLTHRU_EDGE (b)); + notice_new_block (jb); + if (!redirect_jump (as_a <rtx_jump_insn *> (BB_END (jb)), + block_label (new_ft), 0)) + gcc_unreachable (); + redirect_edge_succ (single_succ_edge (jb), new_ft); + changed_here = true; + } + else + { + /* Invert the jump back to what it was. This should + never fail. */ + if (!invert_jump (as_a <rtx_jump_insn *> (BB_END (b)), + JUMP_LABEL (BB_END (b)), 0)) + gcc_unreachable (); + } + } + } + /* Simplify branch over branch. */ if ((mode & CLEANUP_EXPENSIVE) && !(mode & CLEANUP_CFGLAYOUT) @@ -2455,9 +2969,9 @@ can either delete the jump entirely, or replace it with a simple unconditional jump. */ if (single_succ_p (b) - && single_succ (b) != EXIT_BLOCK_PTR + && single_succ (b) != EXIT_BLOCK_PTR_FOR_FN (cfun) && onlyjump_p (BB_END (b)) - && !find_reg_note (BB_END (b), REG_CROSSING_JUMP, NULL_RTX) + && !CROSSING_JUMP_P (BB_END (b)) && try_redirect_by_replacing_jump (single_succ_edge (b), single_succ (b), (mode & CLEANUP_CFGLAYOUT) != 0)) @@ -2468,7 +2982,10 @@ /* Simplify branch to branch. */ if (try_forward_edges (mode, b)) - changed_here = true; + { + update_forwarder_flag (b); + changed_here = true; + } /* Look for shared code between blocks. */ if ((mode & CLEANUP_CROSSJUMP) @@ -2491,7 +3008,7 @@ } if ((mode & CLEANUP_CROSSJUMP) - && try_crossjump_bb (mode, EXIT_BLOCK_PTR)) + && try_crossjump_bb (mode, EXIT_BLOCK_PTR_FOR_FN (cfun))) changed = true; if (block_was_dirty) @@ -2501,10 +3018,18 @@ df_analyze (); } -#ifdef ENABLE_CHECKING if (changed) - verify_flow_info (); -#endif + { + /* Edge forwarding in particular can cause hot blocks previously + reached by both hot and cold blocks to become dominated only + by cold blocks. This will cause the verification below to fail, + and lead to now cold code in the hot section. This is not easy + to detect and fix during edge forwarding, and in some cases + is only visible after newly unreachable blocks are deleted, + which will be done in fixup_partitions. */ + fixup_partitions (); + checking_verify_flow_info (); + } changed_overall |= changed; first_pass = false; @@ -2512,7 +3037,7 @@ while (changed); } - FOR_ALL_BB (b) + FOR_ALL_BB_FN (b, cfun) b->flags &= ~(BB_FORWARDER_BLOCK | BB_NONTHREADABLE_BLOCK); return changed_overall; @@ -2534,10 +3059,11 @@ have dominators information, walking blocks backward gets us a better chance of retaining most debug information than otherwise. */ - if (MAY_HAVE_DEBUG_STMTS && current_ir_type () == IR_GIMPLE + if (MAY_HAVE_DEBUG_INSNS && current_ir_type () == IR_GIMPLE && dom_info_available_p (CDI_DOMINATORS)) { - for (b = EXIT_BLOCK_PTR->prev_bb; b != ENTRY_BLOCK_PTR; b = prev_bb) + for (b = EXIT_BLOCK_PTR_FOR_FN (cfun)->prev_bb; + b != ENTRY_BLOCK_PTR_FOR_FN (cfun); b = prev_bb) { prev_bb = b->prev_bb; @@ -2550,12 +3076,12 @@ delete_basic_block (b); else { - VEC (basic_block, heap) *h + vec<basic_block> h = get_all_dominated_blocks (CDI_DOMINATORS, b); - while (VEC_length (basic_block, h)) + while (h.length ()) { - b = VEC_pop (basic_block, h); + b = h.pop (); prev_bb = b->prev_bb; @@ -2564,7 +3090,7 @@ delete_basic_block (b); } - VEC_free (basic_block, heap, h); + h.release (); } changed = true; @@ -2573,7 +3099,8 @@ } else { - for (b = EXIT_BLOCK_PTR->prev_bb; b != ENTRY_BLOCK_PTR; b = prev_bb) + for (b = EXIT_BLOCK_PTR_FOR_FN (cfun)->prev_bb; + b != ENTRY_BLOCK_PTR_FOR_FN (cfun); b = prev_bb) { prev_bb = b->prev_bb; @@ -2601,9 +3128,9 @@ /* A dead jump table does not belong to any basic block. Scan insns between two adjacent basic blocks. */ - FOR_EACH_BB (bb) + FOR_EACH_BB_FN (bb, cfun) { - rtx insn, next; + rtx_insn *insn, *next; for (insn = NEXT_INSN (BB_END (bb)); insn && !NOTE_INSN_BASIC_BLOCK_P (insn); @@ -2614,7 +3141,7 @@ && LABEL_NUSES (insn) == LABEL_PRESERVE_P (insn) && JUMP_TABLE_DATA_P (next)) { - rtx label = insn, jump = next; + rtx_insn *label = insn, *jump = next; if (dump_file) fprintf (dump_file, "Dead jumptable %i removed\n", @@ -2685,7 +3212,7 @@ if ((mode & CLEANUP_EXPENSIVE) && !reload_completed && !delete_trivially_dead_insns (get_insns (), max_reg_num ())) break; - if ((mode & CLEANUP_CROSSJUMP) && crossjumps_occured) + if ((mode & CLEANUP_CROSSJUMP) && crossjumps_occurred) run_fast_dce (); } else @@ -2704,41 +3231,54 @@ if (!(mode & CLEANUP_CFGLAYOUT)) delete_dead_jumptables (); + /* ??? We probably do this way too often. */ + if (current_loops + && (changed + || (mode & CLEANUP_CFG_CHANGED))) + { + timevar_push (TV_REPAIR_LOOPS); + /* The above doesn't preserve dominance info if available. */ + gcc_assert (!dom_info_available_p (CDI_DOMINATORS)); + calculate_dominance_info (CDI_DOMINATORS); + fix_loop_structure (NULL); + free_dominance_info (CDI_DOMINATORS); + timevar_pop (TV_REPAIR_LOOPS); + } + timevar_pop (TV_CLEANUP_CFG); return changed; } -static unsigned int -rest_of_handle_jump (void) -{ - if (crtl->tail_call_emit) - fixup_tail_calls (); - return 0; -} - -struct rtl_opt_pass pass_jump = +namespace { + +const pass_data pass_data_jump = { - { - RTL_PASS, - "sibling", /* name */ - NULL, /* gate */ - rest_of_handle_jump, /* execute */ - NULL, /* sub */ - NULL, /* next */ - 0, /* static_pass_number */ - TV_JUMP, /* tv_id */ - 0, /* properties_required */ - 0, /* properties_provided */ - 0, /* properties_destroyed */ - TODO_ggc_collect, /* todo_flags_start */ - TODO_verify_flow, /* todo_flags_finish */ - } + RTL_PASS, /* type */ + "jump", /* name */ + OPTGROUP_NONE, /* optinfo_flags */ + TV_JUMP, /* tv_id */ + 0, /* properties_required */ + 0, /* properties_provided */ + 0, /* properties_destroyed */ + 0, /* todo_flags_start */ + 0, /* todo_flags_finish */ }; - -static unsigned int -rest_of_handle_jump2 (void) +class pass_jump : public rtl_opt_pass +{ +public: + pass_jump (gcc::context *ctxt) + : rtl_opt_pass (pass_data_jump, ctxt) + {} + + /* opt_pass methods: */ + virtual unsigned int execute (function *); + +}; // class pass_jump + +unsigned int +pass_jump::execute (function *) { delete_trivially_dead_insns (get_insns (), max_reg_num ()); if (dump_file) @@ -2748,24 +3288,49 @@ return 0; } - -struct rtl_opt_pass pass_jump2 = +} // anon namespace + +rtl_opt_pass * +make_pass_jump (gcc::context *ctxt) +{ + return new pass_jump (ctxt); +} + +namespace { + +const pass_data pass_data_jump2 = { - { - RTL_PASS, - "jump", /* name */ - NULL, /* gate */ - rest_of_handle_jump2, /* execute */ - NULL, /* sub */ - NULL, /* next */ - 0, /* static_pass_number */ - TV_JUMP, /* tv_id */ - 0, /* properties_required */ - 0, /* properties_provided */ - 0, /* properties_destroyed */ - TODO_ggc_collect, /* todo_flags_start */ - TODO_dump_func | TODO_verify_rtl_sharing,/* todo_flags_finish */ - } + RTL_PASS, /* type */ + "jump2", /* name */ + OPTGROUP_NONE, /* optinfo_flags */ + TV_JUMP, /* tv_id */ + 0, /* properties_required */ + 0, /* properties_provided */ + 0, /* properties_destroyed */ + 0, /* todo_flags_start */ + 0, /* todo_flags_finish */ }; - +class pass_jump2 : public rtl_opt_pass +{ +public: + pass_jump2 (gcc::context *ctxt) + : rtl_opt_pass (pass_data_jump2, ctxt) + {} + + /* opt_pass methods: */ + virtual unsigned int execute (function *) + { + cleanup_cfg (flag_crossjumping ? CLEANUP_CROSSJUMP : 0); + return 0; + } + +}; // class pass_jump2 + +} // anon namespace + +rtl_opt_pass * +make_pass_jump2 (gcc::context *ctxt) +{ + return new pass_jump2 (ctxt); +}