| /* Instruction scheduling pass. |
| Copyright (C) 1992-2018 Free Software Foundation, Inc. |
| Contributed by Michael Tiemann (tiemann@cygnus.com) Enhanced by, |
| and currently maintained by, Jim Wilson (wilson@cygnus.com) |
| |
| This file is part of GCC. |
| |
| GCC is free software; you can redistribute it and/or modify it under |
| the terms of the GNU General Public License as published by the Free |
| Software Foundation; either version 3, or (at your option) any later |
| version. |
| |
| GCC is distributed in the hope that it will be useful, but WITHOUT ANY |
| WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with GCC; see the file COPYING3. If not see |
| <http://www.gnu.org/licenses/>. */ |
| |
| #include "config.h" |
| #include "system.h" |
| #include "coretypes.h" |
| #include "backend.h" |
| #include "target.h" |
| #include "rtl.h" |
| #include "cfghooks.h" |
| #include "df.h" |
| #include "profile.h" |
| #include "insn-attr.h" |
| #include "params.h" |
| #include "cfgrtl.h" |
| #include "cfgbuild.h" |
| #include "sched-int.h" |
| |
| |
| #ifdef INSN_SCHEDULING |
| |
| /* The number of insns to be scheduled in total. */ |
| static int rgn_n_insns; |
| |
| /* The number of insns scheduled so far. */ |
| static int sched_rgn_n_insns; |
| |
| /* Set of blocks, that already have their dependencies calculated. */ |
| static bitmap_head dont_calc_deps; |
| |
| /* Last basic block in current ebb. */ |
| static basic_block last_bb; |
| |
| /* Implementations of the sched_info functions for region scheduling. */ |
| static void init_ready_list (void); |
| static void begin_schedule_ready (rtx_insn *); |
| static int schedule_more_p (void); |
| static const char *ebb_print_insn (const rtx_insn *, int); |
| static int rank (rtx_insn *, rtx_insn *); |
| static int ebb_contributes_to_priority (rtx_insn *, rtx_insn *); |
| static basic_block earliest_block_with_similiar_load (basic_block, rtx); |
| static void add_deps_for_risky_insns (rtx_insn *, rtx_insn *); |
| static void debug_ebb_dependencies (rtx_insn *, rtx_insn *); |
| |
| static void ebb_add_remove_insn (rtx_insn *, int); |
| static void ebb_add_block (basic_block, basic_block); |
| static basic_block advance_target_bb (basic_block, rtx_insn *); |
| static void ebb_fix_recovery_cfg (int, int, int); |
| |
| /* Allocate memory and store the state of the frontend. Return the allocated |
| memory. */ |
| static void * |
| save_ebb_state (void) |
| { |
| int *p = XNEW (int); |
| *p = sched_rgn_n_insns; |
| return p; |
| } |
| |
| /* Restore the state of the frontend from P_, then free it. */ |
| static void |
| restore_ebb_state (void *p_) |
| { |
| int *p = (int *)p_; |
| sched_rgn_n_insns = *p; |
| free (p_); |
| } |
| |
| /* Return nonzero if there are more insns that should be scheduled. */ |
| |
| static int |
| schedule_more_p (void) |
| { |
| return sched_rgn_n_insns < rgn_n_insns; |
| } |
| |
| /* Print dependency information about ebb between HEAD and TAIL. */ |
| static void |
| debug_ebb_dependencies (rtx_insn *head, rtx_insn *tail) |
| { |
| fprintf (sched_dump, |
| ";; --------------- forward dependences: ------------ \n"); |
| |
| fprintf (sched_dump, "\n;; --- EBB Dependences --- from bb%d to bb%d \n", |
| BLOCK_NUM (head), BLOCK_NUM (tail)); |
| |
| debug_dependencies (head, tail); |
| } |
| |
| /* Add all insns that are initially ready to the ready list READY. Called |
| once before scheduling a set of insns. */ |
| |
| static void |
| init_ready_list (void) |
| { |
| int n = 0; |
| rtx_insn *prev_head = current_sched_info->prev_head; |
| rtx_insn *next_tail = current_sched_info->next_tail; |
| rtx_insn *insn; |
| |
| sched_rgn_n_insns = 0; |
| |
| /* Print debugging information. */ |
| if (sched_verbose >= 5) |
| debug_ebb_dependencies (NEXT_INSN (prev_head), PREV_INSN (next_tail)); |
| |
| /* Initialize ready list with all 'ready' insns in target block. |
| Count number of insns in the target block being scheduled. */ |
| for (insn = NEXT_INSN (prev_head); insn != next_tail; insn = NEXT_INSN (insn)) |
| { |
| try_ready (insn); |
| n++; |
| } |
| |
| gcc_assert (n == rgn_n_insns); |
| } |
| |
| /* INSN is being scheduled after LAST. Update counters. */ |
| static void |
| begin_schedule_ready (rtx_insn *insn ATTRIBUTE_UNUSED) |
| { |
| sched_rgn_n_insns++; |
| } |
| |
| /* INSN is being moved to its place in the schedule, after LAST. */ |
| static void |
| begin_move_insn (rtx_insn *insn, rtx_insn *last) |
| { |
| if (BLOCK_FOR_INSN (insn) == last_bb |
| /* INSN is a jump in the last block, ... */ |
| && control_flow_insn_p (insn) |
| /* that is going to be moved over some instructions. */ |
| && last != PREV_INSN (insn)) |
| { |
| edge e; |
| basic_block bb; |
| |
| /* An obscure special case, where we do have partially dead |
| instruction scheduled after last control flow instruction. |
| In this case we can create new basic block. It is |
| always exactly one basic block last in the sequence. */ |
| |
| e = find_fallthru_edge (last_bb->succs); |
| |
| gcc_checking_assert (!e || !(e->flags & EDGE_COMPLEX)); |
| |
| gcc_checking_assert (BLOCK_FOR_INSN (insn) == last_bb |
| && !IS_SPECULATION_CHECK_P (insn) |
| && BB_HEAD (last_bb) != insn |
| && BB_END (last_bb) == insn); |
| |
| { |
| rtx_insn *x = NEXT_INSN (insn); |
| if (e) |
| gcc_checking_assert (NOTE_P (x) || LABEL_P (x)); |
| else |
| gcc_checking_assert (BARRIER_P (x)); |
| } |
| |
| if (e) |
| { |
| bb = split_edge (e); |
| gcc_assert (NOTE_INSN_BASIC_BLOCK_P (BB_END (bb))); |
| } |
| else |
| { |
| /* Create an empty unreachable block after the INSN. */ |
| rtx_insn *next = NEXT_INSN (insn); |
| if (next && BARRIER_P (next)) |
| next = NEXT_INSN (next); |
| bb = create_basic_block (next, NULL_RTX, last_bb); |
| } |
| |
| /* split_edge () creates BB before E->DEST. Keep in mind, that |
| this operation extends scheduling region till the end of BB. |
| Hence, we need to shift NEXT_TAIL, so haifa-sched.c won't go out |
| of the scheduling region. */ |
| current_sched_info->next_tail = NEXT_INSN (BB_END (bb)); |
| gcc_assert (current_sched_info->next_tail); |
| |
| /* Append new basic block to the end of the ebb. */ |
| sched_init_only_bb (bb, last_bb); |
| gcc_assert (last_bb == bb); |
| } |
| } |
| |
| /* Return a string that contains the insn uid and optionally anything else |
| necessary to identify this insn in an output. It's valid to use a |
| static buffer for this. The ALIGNED parameter should cause the string |
| to be formatted so that multiple output lines will line up nicely. */ |
| |
| static const char * |
| ebb_print_insn (const rtx_insn *insn, int aligned ATTRIBUTE_UNUSED) |
| { |
| static char tmp[80]; |
| |
| /* '+' before insn means it is a new cycle start. */ |
| if (GET_MODE (insn) == TImode) |
| sprintf (tmp, "+ %4d", INSN_UID (insn)); |
| else |
| sprintf (tmp, " %4d", INSN_UID (insn)); |
| |
| return tmp; |
| } |
| |
| /* Compare priority of two insns. Return a positive number if the second |
| insn is to be preferred for scheduling, and a negative one if the first |
| is to be preferred. Zero if they are equally good. */ |
| |
| static int |
| rank (rtx_insn *insn1, rtx_insn *insn2) |
| { |
| basic_block bb1 = BLOCK_FOR_INSN (insn1); |
| basic_block bb2 = BLOCK_FOR_INSN (insn2); |
| |
| if (bb1->count > bb2->count) |
| return -1; |
| if (bb1->count < bb2->count) |
| return 1; |
| return 0; |
| } |
| |
| /* NEXT is an instruction that depends on INSN (a backward dependence); |
| return nonzero if we should include this dependence in priority |
| calculations. */ |
| |
| static int |
| ebb_contributes_to_priority (rtx_insn *next ATTRIBUTE_UNUSED, |
| rtx_insn *insn ATTRIBUTE_UNUSED) |
| { |
| return 1; |
| } |
| |
| /* INSN is a JUMP_INSN. Store the set of registers that |
| must be considered as used by this jump in USED. */ |
| |
| void |
| ebb_compute_jump_reg_dependencies (rtx insn, regset used) |
| { |
| basic_block b = BLOCK_FOR_INSN (insn); |
| edge e; |
| edge_iterator ei; |
| |
| FOR_EACH_EDGE (e, ei, b->succs) |
| if ((e->flags & EDGE_FALLTHRU) == 0) |
| bitmap_ior_into (used, df_get_live_in (e->dest)); |
| } |
| |
| /* Used in schedule_insns to initialize current_sched_info for scheduling |
| regions (or single basic blocks). */ |
| |
| static struct common_sched_info_def ebb_common_sched_info; |
| |
| static struct sched_deps_info_def ebb_sched_deps_info = |
| { |
| ebb_compute_jump_reg_dependencies, |
| NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, |
| NULL, |
| 1, 0, 0 |
| }; |
| |
| static struct haifa_sched_info ebb_sched_info = |
| { |
| init_ready_list, |
| NULL, |
| schedule_more_p, |
| NULL, |
| rank, |
| ebb_print_insn, |
| ebb_contributes_to_priority, |
| NULL, /* insn_finishes_block_p */ |
| |
| NULL, NULL, |
| NULL, NULL, |
| 1, 0, |
| |
| ebb_add_remove_insn, |
| begin_schedule_ready, |
| begin_move_insn, |
| advance_target_bb, |
| |
| save_ebb_state, |
| restore_ebb_state, |
| |
| SCHED_EBB |
| /* We can create new blocks in begin_schedule_ready (). */ |
| | NEW_BBS |
| }; |
| |
| /* Returns the earliest block in EBB currently being processed where a |
| "similar load" 'insn2' is found, and hence LOAD_INSN can move |
| speculatively into the found block. All the following must hold: |
| |
| (1) both loads have 1 base register (PFREE_CANDIDATEs). |
| (2) load_insn and load2 have a def-use dependence upon |
| the same insn 'insn1'. |
| |
| From all these we can conclude that the two loads access memory |
| addresses that differ at most by a constant, and hence if moving |
| load_insn would cause an exception, it would have been caused by |
| load2 anyhow. |
| |
| The function uses list (given by LAST_BLOCK) of already processed |
| blocks in EBB. The list is formed in `add_deps_for_risky_insns'. */ |
| |
| static basic_block |
| earliest_block_with_similiar_load (basic_block last_block, rtx load_insn) |
| { |
| sd_iterator_def back_sd_it; |
| dep_t back_dep; |
| basic_block bb, earliest_block = NULL; |
| |
| FOR_EACH_DEP (load_insn, SD_LIST_BACK, back_sd_it, back_dep) |
| { |
| rtx_insn *insn1 = DEP_PRO (back_dep); |
| |
| if (DEP_TYPE (back_dep) == REG_DEP_TRUE) |
| /* Found a DEF-USE dependence (insn1, load_insn). */ |
| { |
| sd_iterator_def fore_sd_it; |
| dep_t fore_dep; |
| |
| FOR_EACH_DEP (insn1, SD_LIST_FORW, fore_sd_it, fore_dep) |
| { |
| rtx_insn *insn2 = DEP_CON (fore_dep); |
| basic_block insn2_block = BLOCK_FOR_INSN (insn2); |
| |
| if (DEP_TYPE (fore_dep) == REG_DEP_TRUE) |
| { |
| if (earliest_block != NULL |
| && earliest_block->index < insn2_block->index) |
| continue; |
| |
| /* Found a DEF-USE dependence (insn1, insn2). */ |
| if (haifa_classify_insn (insn2) != PFREE_CANDIDATE) |
| /* insn2 not guaranteed to be a 1 base reg load. */ |
| continue; |
| |
| for (bb = last_block; bb; bb = (basic_block) bb->aux) |
| if (insn2_block == bb) |
| break; |
| |
| if (!bb) |
| /* insn2 is the similar load. */ |
| earliest_block = insn2_block; |
| } |
| } |
| } |
| } |
| |
| return earliest_block; |
| } |
| |
| /* The following function adds dependencies between jumps and risky |
| insns in given ebb. */ |
| |
| static void |
| add_deps_for_risky_insns (rtx_insn *head, rtx_insn *tail) |
| { |
| rtx_insn *insn, *prev; |
| int classification; |
| rtx_insn *last_jump = NULL; |
| rtx_insn *next_tail = NEXT_INSN (tail); |
| basic_block last_block = NULL, bb; |
| |
| for (insn = head; insn != next_tail; insn = NEXT_INSN (insn)) |
| { |
| add_delay_dependencies (insn); |
| if (control_flow_insn_p (insn)) |
| { |
| bb = BLOCK_FOR_INSN (insn); |
| bb->aux = last_block; |
| last_block = bb; |
| /* Ensure blocks stay in the same order. */ |
| if (last_jump) |
| add_dependence (insn, last_jump, REG_DEP_ANTI); |
| last_jump = insn; |
| } |
| else if (INSN_P (insn) && last_jump != NULL_RTX) |
| { |
| classification = haifa_classify_insn (insn); |
| prev = last_jump; |
| |
| switch (classification) |
| { |
| case PFREE_CANDIDATE: |
| if (flag_schedule_speculative_load) |
| { |
| bb = earliest_block_with_similiar_load (last_block, insn); |
| if (bb) |
| { |
| bb = (basic_block) bb->aux; |
| if (!bb) |
| break; |
| prev = BB_END (bb); |
| } |
| } |
| /* Fall through. */ |
| case TRAP_RISKY: |
| case IRISKY: |
| case PRISKY_CANDIDATE: |
| /* ??? We could implement better checking PRISKY_CANDIDATEs |
| analogous to sched-rgn.c. */ |
| /* We can not change the mode of the backward |
| dependency because REG_DEP_ANTI has the lowest |
| rank. */ |
| if (! sched_insns_conditions_mutex_p (insn, prev)) |
| { |
| if ((current_sched_info->flags & DO_SPECULATION) |
| && (spec_info->mask & BEGIN_CONTROL)) |
| { |
| dep_def _dep, *dep = &_dep; |
| |
| init_dep (dep, prev, insn, REG_DEP_ANTI); |
| |
| if (current_sched_info->flags & USE_DEPS_LIST) |
| { |
| DEP_STATUS (dep) = set_dep_weak (DEP_ANTI, BEGIN_CONTROL, |
| MAX_DEP_WEAK); |
| |
| } |
| sd_add_or_update_dep (dep, false); |
| } |
| else |
| add_dependence (insn, prev, REG_DEP_CONTROL); |
| } |
| |
| break; |
| |
| default: |
| break; |
| } |
| } |
| } |
| /* Maintain the invariant that bb->aux is clear after use. */ |
| while (last_block) |
| { |
| bb = (basic_block) last_block->aux; |
| last_block->aux = NULL; |
| last_block = bb; |
| } |
| } |
| |
| /* Schedule a single extended basic block, defined by the boundaries |
| HEAD and TAIL. |
| |
| We change our expectations about scheduler behavior depending on |
| whether MODULO_SCHEDULING is true. If it is, we expect that the |
| caller has already called set_modulo_params and created delay pairs |
| as appropriate. If the modulo schedule failed, we return |
| NULL_RTX. */ |
| |
| basic_block |
| schedule_ebb (rtx_insn *head, rtx_insn *tail, bool modulo_scheduling) |
| { |
| basic_block first_bb, target_bb; |
| struct deps_desc tmp_deps; |
| bool success; |
| |
| /* Blah. We should fix the rest of the code not to get confused by |
| a note or two. */ |
| while (head != tail) |
| { |
| if (NOTE_P (head) || DEBUG_INSN_P (head)) |
| head = NEXT_INSN (head); |
| else if (NOTE_P (tail) || DEBUG_INSN_P (tail)) |
| tail = PREV_INSN (tail); |
| else if (LABEL_P (head)) |
| head = NEXT_INSN (head); |
| else |
| break; |
| } |
| |
| first_bb = BLOCK_FOR_INSN (head); |
| last_bb = BLOCK_FOR_INSN (tail); |
| |
| if (no_real_insns_p (head, tail)) |
| return BLOCK_FOR_INSN (tail); |
| |
| gcc_assert (INSN_P (head) && INSN_P (tail)); |
| |
| if (!bitmap_bit_p (&dont_calc_deps, first_bb->index)) |
| { |
| init_deps_global (); |
| |
| /* Compute dependencies. */ |
| init_deps (&tmp_deps, false); |
| sched_analyze (&tmp_deps, head, tail); |
| free_deps (&tmp_deps); |
| |
| add_deps_for_risky_insns (head, tail); |
| |
| if (targetm.sched.dependencies_evaluation_hook) |
| targetm.sched.dependencies_evaluation_hook (head, tail); |
| |
| finish_deps_global (); |
| } |
| else |
| /* Only recovery blocks can have their dependencies already calculated, |
| and they always are single block ebbs. */ |
| gcc_assert (first_bb == last_bb); |
| |
| /* Set priorities. */ |
| current_sched_info->sched_max_insns_priority = 0; |
| rgn_n_insns = set_priorities (head, tail); |
| current_sched_info->sched_max_insns_priority++; |
| |
| current_sched_info->prev_head = PREV_INSN (head); |
| current_sched_info->next_tail = NEXT_INSN (tail); |
| |
| remove_notes (head, tail); |
| |
| unlink_bb_notes (first_bb, last_bb); |
| |
| target_bb = first_bb; |
| |
| /* Make ready list big enough to hold all the instructions from the ebb. */ |
| sched_extend_ready_list (rgn_n_insns); |
| success = schedule_block (&target_bb, NULL); |
| gcc_assert (success || modulo_scheduling); |
| |
| /* Free ready list. */ |
| sched_finish_ready_list (); |
| |
| /* We might pack all instructions into fewer blocks, |
| so we may made some of them empty. Can't assert (b == last_bb). */ |
| |
| /* Sanity check: verify that all region insns were scheduled. */ |
| gcc_assert (modulo_scheduling || sched_rgn_n_insns == rgn_n_insns); |
| |
| /* Free dependencies. */ |
| sched_free_deps (current_sched_info->head, current_sched_info->tail, true); |
| |
| gcc_assert (haifa_recovery_bb_ever_added_p |
| || deps_pools_are_empty_p ()); |
| |
| if (EDGE_COUNT (last_bb->preds) == 0) |
| /* LAST_BB is unreachable. */ |
| { |
| gcc_assert (first_bb != last_bb |
| && EDGE_COUNT (last_bb->succs) == 0); |
| last_bb = last_bb->prev_bb; |
| delete_basic_block (last_bb->next_bb); |
| } |
| |
| return success ? last_bb : NULL; |
| } |
| |
| /* Perform initializations before running schedule_ebbs or a single |
| schedule_ebb. */ |
| void |
| schedule_ebbs_init (void) |
| { |
| /* Setup infos. */ |
| { |
| memcpy (&ebb_common_sched_info, &haifa_common_sched_info, |
| sizeof (ebb_common_sched_info)); |
| |
| ebb_common_sched_info.fix_recovery_cfg = ebb_fix_recovery_cfg; |
| ebb_common_sched_info.add_block = ebb_add_block; |
| ebb_common_sched_info.sched_pass_id = SCHED_EBB_PASS; |
| |
| common_sched_info = &ebb_common_sched_info; |
| sched_deps_info = &ebb_sched_deps_info; |
| current_sched_info = &ebb_sched_info; |
| } |
| |
| haifa_sched_init (); |
| |
| compute_bb_for_insn (); |
| |
| /* Initialize DONT_CALC_DEPS and ebb-{start, end} markers. */ |
| bitmap_initialize (&dont_calc_deps, 0); |
| bitmap_clear (&dont_calc_deps); |
| } |
| |
| /* Perform cleanups after scheduling using schedules_ebbs or schedule_ebb. */ |
| void |
| schedule_ebbs_finish (void) |
| { |
| bitmap_clear (&dont_calc_deps); |
| |
| /* Reposition the prologue and epilogue notes in case we moved the |
| prologue/epilogue insns. */ |
| if (reload_completed) |
| reposition_prologue_and_epilogue_notes (); |
| |
| haifa_sched_finish (); |
| } |
| |
| /* The main entry point in this file. */ |
| |
| void |
| schedule_ebbs (void) |
| { |
| basic_block bb; |
| int probability_cutoff; |
| rtx_insn *tail; |
| |
| /* Taking care of this degenerate case makes the rest of |
| this code simpler. */ |
| if (n_basic_blocks_for_fn (cfun) == NUM_FIXED_BLOCKS) |
| return; |
| |
| if (profile_info && profile_status_for_fn (cfun) == PROFILE_READ) |
| probability_cutoff = PARAM_VALUE (TRACER_MIN_BRANCH_PROBABILITY_FEEDBACK); |
| else |
| probability_cutoff = PARAM_VALUE (TRACER_MIN_BRANCH_PROBABILITY); |
| probability_cutoff = REG_BR_PROB_BASE / 100 * probability_cutoff; |
| |
| schedule_ebbs_init (); |
| |
| /* Schedule every region in the subroutine. */ |
| FOR_EACH_BB_FN (bb, cfun) |
| { |
| rtx_insn *head = BB_HEAD (bb); |
| |
| if (bb->flags & BB_DISABLE_SCHEDULE) |
| continue; |
| |
| for (;;) |
| { |
| edge e; |
| tail = BB_END (bb); |
| if (bb->next_bb == EXIT_BLOCK_PTR_FOR_FN (cfun) |
| || LABEL_P (BB_HEAD (bb->next_bb))) |
| break; |
| e = find_fallthru_edge (bb->succs); |
| if (! e) |
| break; |
| if (e->probability.initialized_p () |
| && e->probability.to_reg_br_prob_base () <= probability_cutoff) |
| break; |
| if (e->dest->flags & BB_DISABLE_SCHEDULE) |
| break; |
| bb = bb->next_bb; |
| } |
| |
| bb = schedule_ebb (head, tail, false); |
| } |
| schedule_ebbs_finish (); |
| } |
| |
| /* INSN has been added to/removed from current ebb. */ |
| static void |
| ebb_add_remove_insn (rtx_insn *insn ATTRIBUTE_UNUSED, int remove_p) |
| { |
| if (!remove_p) |
| rgn_n_insns++; |
| else |
| rgn_n_insns--; |
| } |
| |
| /* BB was added to ebb after AFTER. */ |
| static void |
| ebb_add_block (basic_block bb, basic_block after) |
| { |
| /* Recovery blocks are always bounded by BARRIERS, |
| therefore, they always form single block EBB, |
| therefore, we can use rec->index to identify such EBBs. */ |
| if (after == EXIT_BLOCK_PTR_FOR_FN (cfun)) |
| bitmap_set_bit (&dont_calc_deps, bb->index); |
| else if (after == last_bb) |
| last_bb = bb; |
| } |
| |
| /* Return next block in ebb chain. For parameter meaning please refer to |
| sched-int.h: struct sched_info: advance_target_bb. */ |
| static basic_block |
| advance_target_bb (basic_block bb, rtx_insn *insn) |
| { |
| if (insn) |
| { |
| if (BLOCK_FOR_INSN (insn) != bb |
| && control_flow_insn_p (insn) |
| /* We handle interblock movement of the speculation check |
| or over a speculation check in |
| haifa-sched.c: move_block_after_check (). */ |
| && !IS_SPECULATION_BRANCHY_CHECK_P (insn) |
| && !IS_SPECULATION_BRANCHY_CHECK_P (BB_END (bb))) |
| { |
| /* Assert that we don't move jumps across blocks. */ |
| gcc_assert (!control_flow_insn_p (BB_END (bb)) |
| && NOTE_INSN_BASIC_BLOCK_P (BB_HEAD (bb->next_bb))); |
| return bb; |
| } |
| else |
| return 0; |
| } |
| else |
| /* Return next non empty block. */ |
| { |
| do |
| { |
| gcc_assert (bb != last_bb); |
| |
| bb = bb->next_bb; |
| } |
| while (bb_note (bb) == BB_END (bb)); |
| |
| return bb; |
| } |
| } |
| |
| /* Fix internal data after interblock movement of jump instruction. |
| For parameter meaning please refer to |
| sched-int.h: struct sched_info: fix_recovery_cfg. */ |
| static void |
| ebb_fix_recovery_cfg (int bbi ATTRIBUTE_UNUSED, int jump_bbi, |
| int jump_bb_nexti) |
| { |
| gcc_assert (last_bb->index != bbi); |
| |
| if (jump_bb_nexti == last_bb->index) |
| last_bb = BASIC_BLOCK_FOR_FN (cfun, jump_bbi); |
| } |
| |
| #endif /* INSN_SCHEDULING */ |