summaryrefslogtreecommitdiffstats
path: root/gcc/bt-load.c
diff options
context:
space:
mode:
authoramylaar <amylaar@138bc75d-0d04-0410-961f-82ee72b054a4>2003-06-18 19:43:55 +0000
committeramylaar <amylaar@138bc75d-0d04-0410-961f-82ee72b054a4>2003-06-18 19:43:55 +0000
commit8af3db02b0b3bd115867b56dbef4c21c52dec1d2 (patch)
treeb7be589065a18041782f05367c05430c81cde781 /gcc/bt-load.c
parentf0dd33b55150788330b956f89774c627cadc8056 (diff)
downloadppe42-gcc-8af3db02b0b3bd115867b56dbef4c21c52dec1d2.tar.gz
ppe42-gcc-8af3db02b0b3bd115867b56dbef4c21c52dec1d2.zip
2003-06-18 Stephen Clarke <stephen.clarke@superh.com>
J"orn Rennecke <joern.rennecke@superh.com> * bt-load.c: New file. * Makefile.in (OBJS): Include bt-load.o (bt-load.o): Add dependencies. * flags.h (flag_branch_target_load_optimize): Declare. (flag_branch_target_load_optimize2): Likewise. * hooks.c (hook_reg_class_void_no_regs): New function. (hook_bool_bool_false): Likewise. * hooks.h (hook_reg_class_void_no_regs, hook_bool_bool_false): Declare. * rtl.h (branch_target_load_optimize): Declare. * target-def.h (TARGET_BRANCH_TARGET_REGISTER_CLASS): Define. (TARGET_BRANCH_TARGET_REGISTER_CALLEE_SAVED): Likewise. (TARGET_INITIALIZER): Include these. * target.h (struct gcc_target): Add branch_target_register_class and branch_target_register_callee_saved members. * toplev.c (enum dump_file_index): Add DFI_branch_target_load (dump_file) Add "tars" entry. (flag_branch_target_load_optimize): New variable. (flag_branch_target_load_optimize2): Likewise. (lang_independent_options): Add entries for new options. (rest_of_compilation): Call branch_target_load_optimize. * doc/tm.texi (TARGET_BRANCH_TARGET_REGISTER_CLASS): Document. (TARGET_BRANCH_TARGET_REGISTER_CALLEE_SAVED): Likewise. * doc/invoke.texi: Document -fbranch-target-load-optimize and -fbranch-target-load-optimize2. * rtl.h (epilogue_completed): Declare. * recog.c (epilogue_completed): New variable. * toplev.c (rest_of_compilation): Set it. * flow.c (mark_regs_live_at_end): Use it. * config/ia64/ia64.c (ia64_output_mi_thunk): Set it. * config/rs6000/rs6000.c (rs6000_output_mi_thunk): Likewise. * config/sh/sh.c (sh_output_mi_thunk): Likewise. * config/sparc/sparc.c (sparc_output_mi_thunk): Likewise. * sh.c (shmedia_space_reserved_for_target_registers): New variable. (sh_target_reg_class): New function. (sh_optimize_target_register_callee_saved): Likwise. (shmedia_target_regs_stack_space): Likewise. (shmedia_reserve_space_for_target_registers_p): Likewise. (shmedia_target_regs_stack_adjust): Likewise. (TARGET_BRANCH_TARGET_REGISTER_CLASS): Override. (TARGET_BRANCH_TARGET_REGISTER_CALLEE_SAVED): Likewise. (calc_live_regs): If flag_branch_target_load_optimize2 and TARGET_SAVE_ALL_TARGET_REGS is enabled, and we have space reserved for target registers, make sure that we save all target registers. (sh_expand_prologue, sh_expand_epilogue): Take target register optimizations into account. Collapse stack adjustments if that is beneficial. (initial_elimination_offset): Reserve space for target registers if necessary. * sh.h (SAVE_ALL_TR_BIT, TARGET_SAVE_ALL_TARGET_REGS): Define. (OPTIMIZATION_OPTIONS): Enable flag_branch_target_load_optimize. git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@68165 138bc75d-0d04-0410-961f-82ee72b054a4
Diffstat (limited to 'gcc/bt-load.c')
-rw-r--r--gcc/bt-load.c1380
1 files changed, 1380 insertions, 0 deletions
diff --git a/gcc/bt-load.c b/gcc/bt-load.c
new file mode 100644
index 00000000000..dd2dcb2b16b
--- /dev/null
+++ b/gcc/bt-load.c
@@ -0,0 +1,1380 @@
+/* Perform branch target register load optimizations.
+ Copyright (C) 2001, 2002, 2003 Free Software Foundation, Inc.
+
+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 2, 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 COPYING. If not, write to the Free
+Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+02111-1307, USA. */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "tm.h"
+#include "bitmap.h"
+#include "sbitmap.h"
+#include "rtl.h"
+#include "hard-reg-set.h"
+#include "basic-block.h"
+#include "regs.h"
+#include "obstack.h"
+#include "fibheap.h"
+#include "output.h"
+#include "target.h"
+#include "expr.h"
+#include "flags.h"
+#include "insn-attr.h"
+#include "function.h"
+
+/* Target register optimizations - these are performed after reload. */
+
+typedef struct btr_def_group_s
+{
+ struct btr_def_group_s *next;
+ rtx src;
+ struct btr_def_s *members;
+} *btr_def_group;
+
+typedef struct btr_user_s
+{
+ struct btr_user_s *next;
+ basic_block bb;
+ int luid;
+ rtx insn;
+ /* If INSN has a single use of a single branch register, then
+ USE points to it within INSN. If there is more than
+ one branch register use, or the use is in some way ambiguous,
+ then USE is NULL. */
+ rtx use;
+ int n_reaching_defs;
+ int first_reaching_def;
+ char other_use_this_block;
+} *btr_user;
+
+/* btr_def structs appear on three lists:
+ 1. A list of all btr_def structures (head is
+ ALL_BTR_DEFS, linked by the NEXT field).
+ 2. A list of branch reg definitions per basic block (head is
+ BB_BTR_DEFS[i], linked by the NEXT_THIS_BB field).
+ 3. A list of all branch reg definitions belonging to the same
+ group (head is in a BTR_DEF_GROUP struct, linked by
+ NEXT_THIS_GROUP field). */
+
+typedef struct btr_def_s
+{
+ struct btr_def_s *next_this_bb;
+ struct btr_def_s *next_this_group;
+ basic_block bb;
+ int luid;
+ rtx insn;
+ int btr;
+ int cost;
+ /* For a branch register setting insn that has a constant
+ source (i.e. a label), group links together all the
+ insns with the same source. For other branch register
+ setting insns, group is NULL. */
+ btr_def_group group;
+ btr_user uses;
+ /* If this def has a reaching use which is not a simple use
+ in a branch instruction, then has_ambiguous_use will be true,
+ and we will not attempt to migrate this definition. */
+ char has_ambiguous_use;
+ /* live_range is an approximation to the true live range for this
+ def/use web, because it records the set of blocks that contain
+ the live range. There could be other live ranges for the same
+ branch register in that set of blocks, either in the block
+ containing the def (before the def), or in a block containing
+ a use (after the use). If there are such other live ranges, then
+ other_btr_uses_before_def or other_btr_uses_after_use must be set true
+ as appropriate. */
+ char other_btr_uses_before_def;
+ char other_btr_uses_after_use;
+ bitmap live_range;
+} *btr_def;
+
+static int issue_rate;
+
+static int basic_block_freq (basic_block);
+static int insn_sets_btr_p (rtx, int, int *);
+static rtx *find_btr_use (rtx);
+static int btr_referenced_p (rtx, rtx *);
+static int find_btr_reference (rtx *, void *);
+static void find_btr_def_group (btr_def_group *, btr_def);
+static btr_def add_btr_def (fibheap_t, basic_block, int, rtx,
+ unsigned int, int, btr_def_group *);
+static btr_user new_btr_user (basic_block, int, rtx);
+static void dump_hard_reg_set (HARD_REG_SET);
+static void dump_btrs_live (int);
+static void note_other_use_this_block (unsigned int, btr_user);
+static void compute_defs_uses_and_gen (fibheap_t, btr_def *,btr_user *,
+ sbitmap *, sbitmap *, HARD_REG_SET *);
+static void compute_kill (sbitmap *, sbitmap *, HARD_REG_SET *);
+static void compute_out (sbitmap *bb_out, sbitmap *, sbitmap *, int);
+static void link_btr_uses (btr_def *, btr_user *, sbitmap *, sbitmap *, int);
+static void build_btr_def_use_webs (fibheap_t);
+static int block_at_edge_of_live_range_p (int, btr_def);
+static void clear_btr_from_live_range (btr_def def);
+static void add_btr_to_live_range (btr_def);
+static void augment_live_range (bitmap, HARD_REG_SET *, basic_block,
+ basic_block);
+static int choose_btr (HARD_REG_SET);
+static void combine_btr_defs (btr_def, HARD_REG_SET *);
+static void btr_def_live_range (btr_def, HARD_REG_SET *);
+static void move_btr_def (basic_block, int, btr_def, bitmap, HARD_REG_SET *);
+static int migrate_btr_def (btr_def, int);
+static void migrate_btr_defs (enum reg_class, int);
+static int can_move_up (basic_block, rtx, int);
+static void note_btr_set (rtx, rtx, void *);
+
+/* The following code performs code motion of target load instructions
+ (instructions that set branch target registers), to move them
+ forward away from the branch instructions and out of loops (or,
+ more generally, from a more frequently executed place to a less
+ frequently executed place).
+ Moving target load instructions further in front of the branch
+ instruction that uses the target register value means that the hardware
+ has a better chance of preloading the instructions at the branch
+ target by the time the branch is reached. This avoids bubbles
+ when a taken branch needs to flush out the pipeline.
+ Moving target load instructions out of loops means they are executed
+ less frequently. */
+
+/* An obstack to hold the def-use web data structures built up for
+ migrating branch target load instructions. */
+static struct obstack migrate_btrl_obstack;
+
+/* Basic block dominator information used when migrating PT instructions */
+static dominance_info dom;
+
+/* Array indexed by basic block number, giving the set of registers
+ live in that block. */
+static HARD_REG_SET *btrs_live;
+
+/* Set of all target registers that we are willing to allocate. */
+static HARD_REG_SET all_btrs;
+
+/* Provide lower and upper bounds for target register numbers, so that
+ we don't need to search through all the hard registers all the time. */
+static int first_btr, last_btr;
+
+
+
+/* Return an estimate of the frequency of execution of block bb.
+ If we have a profiling count available, we could use it here. */
+static int
+basic_block_freq (basic_block bb)
+{
+ return bb->frequency;
+}
+
+static rtx *btr_reference_found;
+
+/* A subroutine of btr_referenced_p, called through for_each_rtx.
+ PREG is a pointer to an rtx that is to be excluded from the
+ traversal. If we find a reference to a target register anywhere
+ else, return 1, and put a pointer to it into btr_reference_found. */
+static int
+find_btr_reference (rtx *px, void *preg)
+{
+ rtx x;
+ int regno, i;
+
+ if (px == preg)
+ return -1;
+ x = *px;
+ if (GET_CODE (x) != REG)
+ return 0;
+ regno = REGNO (x);
+ for (i = HARD_REGNO_NREGS (regno, GET_MODE (x)) - 1; i >= 0; i--)
+ if (TEST_HARD_REG_BIT (all_btrs, regno+i))
+ {
+ btr_reference_found = px;
+ return 1;
+ }
+ return -1;
+}
+
+/* Return nonzero if X references (sets or reads) any branch target register.
+ If EXCLUDEP is set, disregard any references within the rtx pointed to
+ by it. If returning nonzero, also set btr_reference_found as above. */
+static int
+btr_referenced_p (rtx x, rtx *excludep)
+{
+ return for_each_rtx (&x, find_btr_reference, excludep);
+}
+
+/* Return true if insn is an instruction that sets a target register.
+ if CHECK_CONST is true, only return true if the source is constant.
+ If such a set is found and REGNO is nonzero, assign the register number
+ of the destination register to *REGNO. */
+static int
+insn_sets_btr_p (rtx insn, int check_const, int *regno)
+{
+ rtx set;
+
+ if (GET_CODE (insn) == INSN
+ && (set = single_set (insn)))
+ {
+ rtx dest = SET_DEST (set);
+ rtx src = SET_SRC (set);
+
+ if (GET_CODE (dest) == SUBREG)
+ dest = XEXP (dest, 0);
+
+ if (GET_CODE (dest) == REG
+ && TEST_HARD_REG_BIT (all_btrs, REGNO (dest)))
+ {
+ if (btr_referenced_p (src, NULL))
+ abort();
+ if (!check_const || CONSTANT_P (src))
+ {
+ if (regno)
+ *regno = REGNO (dest);
+ return 1;
+ }
+ }
+ }
+ return 0;
+}
+
+/* Find and return a use of a target register within an instruction INSN. */
+static rtx *
+find_btr_use (rtx insn)
+{
+ return btr_referenced_p (insn, NULL) ? btr_reference_found : NULL;
+}
+
+/* Find the group that the target register definition DEF belongs
+ to in the list starting with *ALL_BTR_DEF_GROUPS. If no such
+ group exists, create one. Add def to the group. */
+static void
+find_btr_def_group (btr_def_group *all_btr_def_groups, btr_def def)
+{
+ if (insn_sets_btr_p (def->insn, 1, NULL))
+ {
+ btr_def_group this_group;
+ rtx def_src = SET_SRC (single_set (def->insn));
+
+ /* ?? This linear search is an efficiency concern, particularly
+ as the search will almost always fail to find a match. */
+ for (this_group = *all_btr_def_groups;
+ this_group != NULL;
+ this_group = this_group->next)
+ if (rtx_equal_p (def_src, this_group->src))
+ break;
+
+ if (!this_group)
+ {
+ this_group = (btr_def_group)
+ obstack_alloc (&migrate_btrl_obstack,
+ sizeof (struct btr_def_group_s));
+ this_group->src = def_src;
+ this_group->members = NULL;
+ this_group->next = *all_btr_def_groups;
+ *all_btr_def_groups = this_group;
+ }
+ def->group = this_group;
+ def->next_this_group = this_group->members;
+ this_group->members = def;
+ }
+ else
+ def->group = NULL;
+}
+
+/* Create a new target register definition structure, for a definition in
+ block BB, instruction INSN, and insert it into ALL_BTR_DEFS. Return
+ the new definition. */
+static btr_def
+add_btr_def (fibheap_t all_btr_defs, basic_block bb, int insn_luid, rtx insn,
+ unsigned int dest_reg, int other_btr_uses_before_def,
+ btr_def_group *all_btr_def_groups)
+{
+ btr_def this = (btr_def)
+ obstack_alloc (&migrate_btrl_obstack, sizeof (struct btr_def_s));
+ this->bb = bb;
+ this->luid = insn_luid;
+ this->insn = insn;
+ this->btr = dest_reg;
+ this->cost = basic_block_freq (bb);
+ this->has_ambiguous_use = 0;
+ this->other_btr_uses_before_def = other_btr_uses_before_def;
+ this->other_btr_uses_after_use = 0;
+ this->next_this_bb = NULL;
+ this->next_this_group = NULL;
+ this->uses = NULL;
+ this->live_range = NULL;
+ find_btr_def_group (all_btr_def_groups, this);
+
+ fibheap_insert (all_btr_defs, -this->cost, this);
+
+ if (rtl_dump_file)
+ fprintf (rtl_dump_file,
+ "Found target reg definition: sets %u { bb %d, insn %d }%s priority %d\n",
+ dest_reg, bb->index, INSN_UID (insn), (this->group ? "" : ":not const"),
+ this->cost);
+
+ return this;
+}
+
+/* Create a new target register user structure, for a use in block BB,
+ instruction INSN. Return the new user. */
+static btr_user
+new_btr_user (basic_block bb, int insn_luid, rtx insn)
+{
+ /* This instruction reads target registers. We need
+ to decide whether we can replace all target register
+ uses easily.
+ */
+ rtx *usep = find_btr_use (PATTERN (insn));
+ rtx use;
+ btr_user user = NULL;
+
+ if (usep)
+ {
+ int unambiguous_single_use;
+
+ /* We want to ensure that USE is the only use of a target
+ register in INSN, so that we know that to rewrite INSN to use
+ a different target register, all we have to do is replace USE. */
+ unambiguous_single_use = !btr_referenced_p (PATTERN (insn), usep);
+ if (!unambiguous_single_use)
+ usep = NULL;
+ }
+ use = usep ? *usep : NULL_RTX;
+ user = (btr_user)
+ obstack_alloc (&migrate_btrl_obstack, sizeof (struct btr_user_s));
+ user->bb = bb;
+ user->luid = insn_luid;
+ user->insn = insn;
+ user->use = use;
+ user->other_use_this_block = 0;
+ user->next = NULL;
+ user->n_reaching_defs = 0;
+ user->first_reaching_def = -1;
+
+ if (rtl_dump_file)
+ {
+ fprintf (rtl_dump_file, "Uses target reg: { bb %d, insn %d }",
+ bb->index, INSN_UID (insn));
+
+ if (user->use)
+ fprintf (rtl_dump_file, ": unambiguous use of reg %d\n",
+ REGNO (user->use));
+ }
+
+ return user;
+}
+
+/* Write the contents of S to the dump file. */
+static void
+dump_hard_reg_set (HARD_REG_SET s)
+{
+ int reg;
+ for (reg = 0; reg < FIRST_PSEUDO_REGISTER; reg++)
+ if (TEST_HARD_REG_BIT (s, reg))
+ fprintf (rtl_dump_file, " %d", reg);
+}
+
+/* Write the set of target regs live in block BB to the dump file. */
+static void
+dump_btrs_live (int bb)
+{
+ fprintf (rtl_dump_file, "BB%d live:", bb);
+ dump_hard_reg_set (btrs_live[bb]);
+ fprintf (rtl_dump_file, "\n");
+}
+
+/* REGNO is the number of a branch target register that is being used or
+ set. USERS_THIS_BB is a list of preceding branch target register users;
+ If any of them use the same register, set their other_use_this_block
+ flag. */
+static void
+note_other_use_this_block (unsigned int regno, btr_user users_this_bb)
+{
+ btr_user user;
+
+ for (user = users_this_bb; user != NULL; user = user->next)
+ if (user->use && REGNO (user->use) == regno)
+ user->other_use_this_block = 1;
+}
+
+typedef struct {
+ btr_user users_this_bb;
+ HARD_REG_SET btrs_written_in_block;
+ HARD_REG_SET btrs_live_in_block;
+ sbitmap bb_gen;
+ sbitmap *btr_defset;
+} defs_uses_info;
+
+/* Called via note_stores or directly to register stores into /
+ clobbers of a branch target register DEST that are not recognized as
+ straightforward definitions. DATA points to information about the
+ current basic block that needs updating. */
+static void
+note_btr_set (rtx dest, rtx set ATTRIBUTE_UNUSED, void *data)
+{
+ defs_uses_info *info = data;
+ int regno, end_regno;
+
+ if (GET_CODE (dest) != REG)
+ return;
+ regno = REGNO (dest);
+ end_regno = regno + HARD_REGNO_NREGS (regno, GET_MODE (dest));
+ for (; regno < end_regno; regno++)
+ if (TEST_HARD_REG_BIT (all_btrs, regno))
+ {
+ note_other_use_this_block (regno, info->users_this_bb);
+ SET_HARD_REG_BIT (info->btrs_written_in_block, regno);
+ SET_HARD_REG_BIT (info->btrs_live_in_block, regno);
+ sbitmap_difference (info->bb_gen, info->bb_gen,
+ info->btr_defset[regno - first_btr]);
+ }
+}
+
+static void
+compute_defs_uses_and_gen (fibheap_t all_btr_defs, btr_def *def_array,
+ btr_user *use_array, sbitmap *btr_defset,
+ sbitmap *bb_gen, HARD_REG_SET *btrs_written)
+{
+ /* Scan the code building up the set of all defs and all uses.
+ For each target register, build the set of defs of that register.
+ For each block, calculate the set of target registers
+ written in that block.
+ Also calculate the set of btrs ever live in that block.
+ */
+ int i;
+ int insn_luid = 0;
+ btr_def_group all_btr_def_groups = NULL;
+ defs_uses_info info;
+
+ sbitmap_vector_zero (bb_gen, n_basic_blocks);
+ for (i = 0; i < n_basic_blocks; i++)
+ {
+ basic_block bb = BASIC_BLOCK (i);
+ int reg;
+ btr_def defs_this_bb = NULL;
+ rtx insn;
+ rtx last;
+
+ info.users_this_bb = NULL;
+ info.bb_gen = bb_gen[i];
+ info.btr_defset = btr_defset;
+
+ CLEAR_HARD_REG_SET (info.btrs_live_in_block);
+ CLEAR_HARD_REG_SET (info.btrs_written_in_block);
+ for (reg = first_btr; reg <= last_btr; reg++)
+ if (TEST_HARD_REG_BIT (all_btrs, reg)
+ && REGNO_REG_SET_P (bb->global_live_at_start, reg))
+ SET_HARD_REG_BIT (info.btrs_live_in_block, reg);
+
+ for (insn = bb->head, last = NEXT_INSN (bb->end);
+ insn != last;
+ insn = NEXT_INSN (insn), insn_luid++)
+ {
+ if (INSN_P (insn))
+ {
+ int regno;
+ int insn_uid = INSN_UID (insn);
+
+ if (insn_sets_btr_p (insn, 0, &regno))
+ {
+ btr_def def = add_btr_def (
+ all_btr_defs, bb, insn_luid, insn, regno,
+ TEST_HARD_REG_BIT (info.btrs_live_in_block, regno),
+ &all_btr_def_groups);
+
+ def_array[insn_uid] = def;
+ SET_HARD_REG_BIT (info.btrs_written_in_block, regno);
+ SET_HARD_REG_BIT (info.btrs_live_in_block, regno);
+ sbitmap_difference (bb_gen[i], bb_gen[i],
+ btr_defset[regno - first_btr]);
+ SET_BIT (bb_gen[i], insn_uid);
+ def->next_this_bb = defs_this_bb;
+ defs_this_bb = def;
+ SET_BIT (btr_defset[regno - first_btr], insn_uid);
+ note_other_use_this_block (regno, info.users_this_bb);
+ }
+ else
+ {
+ if (btr_referenced_p (PATTERN (insn), NULL))
+ {
+ btr_user user = new_btr_user (bb, insn_luid, insn);
+
+ use_array[insn_uid] = user;
+ if (user->use)
+ SET_HARD_REG_BIT (info.btrs_live_in_block,
+ REGNO (user->use));
+ else
+ {
+ int reg;
+ for (reg = first_btr; reg <= last_btr; reg++)
+ if (TEST_HARD_REG_BIT (all_btrs, reg)
+ && refers_to_regno_p (reg, reg + 1, user->insn,
+ NULL))
+ {
+ note_other_use_this_block (reg,
+ info.users_this_bb);
+ SET_HARD_REG_BIT (info.btrs_live_in_block, reg);
+ }
+ note_stores (PATTERN (insn), note_btr_set, &info);
+ }
+ user->next = info.users_this_bb;
+ info.users_this_bb = user;
+ }
+ if (GET_CODE (insn) == CALL_INSN)
+ {
+ HARD_REG_SET *clobbered = &call_used_reg_set;
+ HARD_REG_SET call_saved;
+ rtx pat = PATTERN (insn);
+ int i;
+
+ /* Check for sibcall. */
+ if (GET_CODE (pat) == PARALLEL)
+ for (i = XVECLEN (pat, 0) - 1; i >= 0; i--)
+ if (GET_CODE (XVECEXP (pat, 0, i)) == RETURN)
+ {
+ COMPL_HARD_REG_SET (call_saved,
+ call_used_reg_set);
+ clobbered = &call_saved;
+ }
+
+ for (regno = first_btr; regno <= last_btr; regno++)
+ if (TEST_HARD_REG_BIT (*clobbered, regno))
+ note_btr_set (regno_reg_rtx[regno], NULL_RTX, &info);
+ }
+ }
+ }
+ }
+
+ COPY_HARD_REG_SET (btrs_live[i], info.btrs_live_in_block);
+ COPY_HARD_REG_SET (btrs_written[i], info.btrs_written_in_block);
+ if (rtl_dump_file)
+ dump_btrs_live(i);
+ }
+}
+
+static void
+compute_kill (sbitmap *bb_kill, sbitmap *btr_defset,
+ HARD_REG_SET *btrs_written)
+{
+ int i;
+ int regno;
+
+ /* For each basic block, form the set BB_KILL - the set
+ of definitions that the block kills. */
+ sbitmap_vector_zero (bb_kill, n_basic_blocks);
+ for (i = 0; i < n_basic_blocks; i++)
+ {
+ for (regno = first_btr; regno <= last_btr; regno++)
+ if (TEST_HARD_REG_BIT (all_btrs, regno)
+ && TEST_HARD_REG_BIT (btrs_written[i], regno))
+ sbitmap_a_or_b (bb_kill[i], bb_kill[i],
+ btr_defset[regno - first_btr]);
+ }
+}
+
+static void
+compute_out (sbitmap *bb_out, sbitmap *bb_gen, sbitmap *bb_kill, int max_uid)
+{
+ /* Perform iterative dataflow:
+ Initially, for all blocks, BB_OUT = BB_GEN.
+ For each block,
+ BB_IN = union over predecessors of BB_OUT(pred)
+ BB_OUT = (BB_IN - BB_KILL) + BB_GEN
+ Iterate until the bb_out sets stop growing. */
+ int i;
+ int changed;
+ sbitmap bb_in = sbitmap_alloc (max_uid);
+
+ for (i = 0; i < n_basic_blocks; i++)
+ sbitmap_copy (bb_out[i], bb_gen[i]);
+
+ changed = 1;
+ while (changed)
+ {
+ changed = 0;
+ for (i = 0; i < n_basic_blocks; i++)
+ {
+ sbitmap_union_of_preds (bb_in, bb_out, i);
+ changed |= sbitmap_union_of_diff_cg (bb_out[i], bb_gen[i],
+ bb_in, bb_kill[i]);
+ }
+ }
+ sbitmap_free (bb_in);
+}
+
+static void
+link_btr_uses (btr_def *def_array, btr_user *use_array, sbitmap *bb_out,
+ sbitmap *btr_defset, int max_uid)
+{
+ int i;
+ sbitmap reaching_defs = sbitmap_alloc (max_uid);
+
+ /* Link uses to the uses lists of all of their reaching defs.
+ Count up the number of reaching defs of each use. */
+ for (i = 0; i < n_basic_blocks; i++)
+ {
+ basic_block bb = BASIC_BLOCK (i);
+ rtx insn;
+ rtx last;
+
+ sbitmap_union_of_preds (reaching_defs, bb_out, i);
+ for (insn = bb->head, last = NEXT_INSN (bb->end);
+ insn != last;
+ insn = NEXT_INSN (insn))
+ {
+ if (INSN_P (insn))
+ {
+ int insn_uid = INSN_UID (insn);
+
+ btr_def def = def_array[insn_uid];
+ btr_user user = use_array[insn_uid];
+ if (def != NULL)
+ {
+ /* Remove all reaching defs of regno except
+ for this one. */
+ sbitmap_difference (reaching_defs, reaching_defs,
+ btr_defset[def->btr - first_btr]);
+ SET_BIT(reaching_defs, insn_uid);
+ }
+
+ if (user != NULL)
+ {
+ /* Find all the reaching defs for this use */
+ sbitmap reaching_defs_of_reg = sbitmap_alloc(max_uid);
+ int uid;
+
+ if (user->use)
+ sbitmap_a_and_b (
+ reaching_defs_of_reg,
+ reaching_defs,
+ btr_defset[REGNO (user->use) - first_btr]);
+ else
+ {
+ int reg;
+
+ sbitmap_zero (reaching_defs_of_reg);
+ for (reg = first_btr; reg <= last_btr; reg++)
+ if (TEST_HARD_REG_BIT (all_btrs, reg)
+ && refers_to_regno_p (reg, reg + 1, user->insn,
+ NULL))
+ sbitmap_a_or_b_and_c (reaching_defs_of_reg,
+ reaching_defs_of_reg,
+ reaching_defs,
+ btr_defset[reg - first_btr]);
+ }
+ EXECUTE_IF_SET_IN_SBITMAP (reaching_defs_of_reg, 0, uid,
+ {
+ btr_def def = def_array[uid];
+
+ /* We now know that def reaches user */
+
+ if (rtl_dump_file)
+ fprintf (rtl_dump_file,
+ "Def in insn %d reaches use in insn %d\n",
+ uid, insn_uid);
+
+ user->n_reaching_defs++;
+ if (!user->use)
+ def->has_ambiguous_use = 1;
+ if (user->first_reaching_def != -1)
+ { /* There is more than one reaching def. This is
+ a rare case, so just give up on this def/use
+ web when it occurs. */
+ def->has_ambiguous_use = 1;
+ def_array[user->first_reaching_def]
+ ->has_ambiguous_use = 1;
+ if (rtl_dump_file)
+ fprintf (rtl_dump_file,
+ "(use %d has multiple reaching defs)\n",
+ insn_uid);
+ }
+ else
+ user->first_reaching_def = uid;
+ if (user->other_use_this_block)
+ def->other_btr_uses_after_use = 1;
+ user->next = def->uses;
+ def->uses = user;
+ });
+ sbitmap_free (reaching_defs_of_reg);
+ }
+
+ if (GET_CODE (insn) == CALL_INSN)
+ {
+ int regno;
+
+ for (regno = first_btr; regno <= last_btr; regno++)
+ if (TEST_HARD_REG_BIT (all_btrs, regno)
+ && TEST_HARD_REG_BIT (call_used_reg_set, regno))
+ sbitmap_difference (reaching_defs, reaching_defs,
+ btr_defset[regno - first_btr]);
+ }
+ }
+ }
+ }
+ sbitmap_free (reaching_defs);
+}
+
+static void
+build_btr_def_use_webs (fibheap_t all_btr_defs)
+{
+ const int max_uid = get_max_uid ();
+ btr_def *def_array = xcalloc (max_uid, sizeof (btr_def));
+ btr_user *use_array = xcalloc (max_uid, sizeof (btr_user));
+ sbitmap *btr_defset = sbitmap_vector_alloc (
+ (last_btr - first_btr) + 1, max_uid);
+ sbitmap *bb_gen = sbitmap_vector_alloc (n_basic_blocks, max_uid);
+ HARD_REG_SET *btrs_written = (HARD_REG_SET *) xcalloc (
+ n_basic_blocks, sizeof (HARD_REG_SET));
+ sbitmap *bb_kill;
+ sbitmap *bb_out;
+
+ sbitmap_vector_zero (btr_defset, (last_btr - first_btr) + 1);
+
+ compute_defs_uses_and_gen (all_btr_defs, def_array, use_array, btr_defset,
+ bb_gen, btrs_written);
+
+ bb_kill = sbitmap_vector_alloc (n_basic_blocks, max_uid);
+ compute_kill (bb_kill, btr_defset, btrs_written);
+ free (btrs_written);
+
+ bb_out = sbitmap_vector_alloc (n_basic_blocks, max_uid);
+ compute_out (bb_out, bb_gen, bb_kill, max_uid);
+
+ sbitmap_vector_free (bb_gen);
+ sbitmap_vector_free (bb_kill);
+
+ link_btr_uses (def_array, use_array, bb_out, btr_defset, max_uid);
+
+ sbitmap_vector_free (bb_out);
+ sbitmap_vector_free (btr_defset);
+ free (use_array);
+ free (def_array);
+}
+
+/* Return true if basic block BB contains the start or end of the
+ live range of the definition DEF, AND there are other live
+ ranges of the same target register that include BB. */
+static int
+block_at_edge_of_live_range_p (int bb, btr_def def)
+{
+ if (def->other_btr_uses_before_def && BASIC_BLOCK (bb) == def->bb)
+ return 1;
+ else if (def->other_btr_uses_after_use)
+ {
+ btr_user user;
+ for (user = def->uses; user != NULL; user = user->next)
+ if (BASIC_BLOCK (bb) == user->bb)
+ return 1;
+ }
+ return 0;
+}
+
+/* We are removing the def/use web DEF. The target register
+ used in this web is therefore no longer live in the live range
+ of this web, so remove it from the live set of all basic blocks
+ in the live range of the web.
+ Blocks at the boundary of the live range may contain other live
+ ranges for the same target register, so we have to be careful
+ to remove the target register from the live set of these blocks
+ only if they do not contain other live ranges for the same register. */
+static void
+clear_btr_from_live_range (btr_def def)
+{
+ int bb;
+
+ EXECUTE_IF_SET_IN_BITMAP
+ (def->live_range, 0, bb,
+ {
+ if ((!def->other_btr_uses_before_def
+ && !def->other_btr_uses_after_use)
+ || !block_at_edge_of_live_range_p (bb, def))
+ {
+ CLEAR_HARD_REG_BIT (btrs_live[bb], def->btr);
+ if (rtl_dump_file)
+ dump_btrs_live (bb);
+ }
+ });
+}
+
+
+/* We are adding the def/use web DEF. Add the target register used
+ in this web to the live set of all of the basic blocks that contain
+ the live range of the web. */
+static void
+add_btr_to_live_range (btr_def def)
+{
+ int bb;
+ EXECUTE_IF_SET_IN_BITMAP
+ (def->live_range, 0, bb,
+ {
+ SET_HARD_REG_BIT (btrs_live[bb], def->btr);
+ if (rtl_dump_file)
+ dump_btrs_live (bb);
+ });
+}
+
+/* Update a live range to contain the basic block NEW_BLOCK, and all
+ blocks on paths between the existing live range and NEW_BLOCK.
+ HEAD is a block contained in the existing live range that dominates
+ all other blocks in the existing live range.
+ Also add to the set BTRS_LIVE_IN_RANGE all target registers that
+ are live in the blocks that we add to the live range.
+ It is a precondition that either NEW_BLOCK dominates HEAD,or
+ HEAD dom NEW_BLOCK. This is used to speed up the
+ implementation of this function. */
+static void
+augment_live_range (bitmap live_range, HARD_REG_SET *btrs_live_in_range,
+ basic_block head_bb, basic_block new_bb)
+{
+ basic_block *worklist, *tos;
+
+ tos = worklist =
+ (basic_block *) xmalloc (sizeof (basic_block) * (n_basic_blocks + 1));
+
+ if (dominated_by_p (dom, new_bb, head_bb))
+ *tos++ = new_bb;
+ else if (dominated_by_p (dom, head_bb, new_bb))
+ {
+ edge e;
+ int new_block = new_bb->index;
+
+ bitmap_set_bit (live_range, new_block);
+ IOR_HARD_REG_SET (*btrs_live_in_range, btrs_live[new_block]);
+ if (rtl_dump_file)
+ {
+ fprintf (rtl_dump_file,
+ "Adding block %d to live range\n", new_block);
+ fprintf (rtl_dump_file,"Now live btrs are ");
+ dump_hard_reg_set (*btrs_live_in_range);
+ fprintf (rtl_dump_file, "\n");
+ }
+ for (e = head_bb->pred; e; e = e->pred_next)
+ *tos++ = e->src;
+ }
+ else
+ abort();
+
+ while (tos != worklist)
+ {
+ basic_block bb = *--tos;
+ if (!bitmap_bit_p (live_range, bb->index))
+ {
+ edge e;
+
+ bitmap_set_bit (live_range, bb->index);
+ IOR_HARD_REG_SET (*btrs_live_in_range,
+ btrs_live[bb->index]);
+ if (rtl_dump_file)
+ {
+ fprintf (rtl_dump_file,
+ "Adding block %d to live range\n", bb->index);
+ fprintf (rtl_dump_file,"Now live btrs are ");
+ dump_hard_reg_set (*btrs_live_in_range);
+ fprintf (rtl_dump_file, "\n");
+ }
+
+ for (e = bb->pred; e != NULL; e = e->pred_next)
+ {
+ basic_block pred = e->src;
+ if (!bitmap_bit_p (live_range, pred->index))
+ *tos++ = pred;
+ }
+ }
+ }
+
+ free (worklist);
+}
+
+/* Return the most desirable target register that is not in
+ the set USED_BTRS. */
+static int
+choose_btr (HARD_REG_SET used_btrs)
+{
+ int i;
+ GO_IF_HARD_REG_SUBSET (all_btrs, used_btrs, give_up);
+
+ for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+ {
+#ifdef REG_ALLOC_ORDER
+ int regno = reg_alloc_order[i];
+#else
+ int regno = i;
+#endif
+ if (TEST_HARD_REG_BIT (all_btrs, regno)
+ && !TEST_HARD_REG_BIT (used_btrs, regno))
+ return regno;
+ }
+give_up:
+ return -1;
+}
+
+/* Calculate the set of basic blocks that contain the live range of
+ the def/use web DEF.
+ Also calculate the set of target registers that are live at time
+ in this live range, but ignore the live range represented by DEF
+ when calculating this set. */
+static void
+btr_def_live_range (btr_def def, HARD_REG_SET *btrs_live_in_range)
+{
+ if (!def->live_range)
+ {
+ btr_user user;
+
+ def->live_range = BITMAP_XMALLOC ();
+
+ bitmap_set_bit (def->live_range, def->bb->index);
+ COPY_HARD_REG_SET (*btrs_live_in_range, btrs_live[def->bb->index]);
+
+ for (user = def->uses; user != NULL; user = user->next)
+ augment_live_range (def->live_range, btrs_live_in_range,
+ def->bb, user->bb);
+ }
+ else
+ {
+ /* def->live_range is accurate, but we need to recompute
+ the set of target registers live over it, because migration
+ of other PT instructions may have affected it.
+ */
+ int bb;
+
+ CLEAR_HARD_REG_SET (*btrs_live_in_range);
+ EXECUTE_IF_SET_IN_BITMAP
+ (def->live_range, 0, bb,
+ {
+ IOR_HARD_REG_SET (*btrs_live_in_range,
+ btrs_live[bb]);
+ });
+ }
+ if (!def->other_btr_uses_before_def &&
+ !def->other_btr_uses_after_use)
+ CLEAR_HARD_REG_BIT (*btrs_live_in_range, def->btr);
+}
+
+/* Merge into the def/use web DEF any other def/use webs in the same
+ group that are dominated by DEF, provided that there is a target
+ register available to allocate to the merged web. */
+static void
+combine_btr_defs (btr_def def, HARD_REG_SET *btrs_live_in_range)
+{
+ btr_def other_def;
+
+ for (other_def = def->group->members;
+ other_def != NULL;
+ other_def = other_def->next_this_group)
+ {
+ if (other_def != def
+ && other_def->uses != NULL
+ && ! other_def->has_ambiguous_use
+ && dominated_by_p (dom, other_def->bb, def->bb))
+ {
+ /* def->bb dominates the other def, so def and other_def could
+ be combined. */
+ /* Merge their live ranges, and get the set of
+ target registers live over the merged range. */
+ int btr;
+ HARD_REG_SET combined_btrs_live;
+ bitmap combined_live_range = BITMAP_XMALLOC ();
+ btr_user user;
+
+ if (other_def->live_range == NULL)
+ {
+ HARD_REG_SET dummy_btrs_live_in_range;
+ btr_def_live_range (other_def, &dummy_btrs_live_in_range);
+ }
+ COPY_HARD_REG_SET (combined_btrs_live, *btrs_live_in_range);
+ bitmap_copy (combined_live_range, def->live_range);
+
+ for (user = other_def->uses; user != NULL; user = user->next)
+ augment_live_range (combined_live_range, &combined_btrs_live,
+ def->bb, user->bb);
+
+ btr = choose_btr (combined_btrs_live);
+ if (btr != -1)
+ {
+ /* We can combine them */
+ if (rtl_dump_file)
+ fprintf (rtl_dump_file,
+ "Combining def in insn %d with def in insn %d\n",
+ INSN_UID (other_def->insn), INSN_UID (def->insn));
+
+ def->btr = btr;
+ user = other_def->uses;
+ while (user != NULL)
+ {
+ btr_user next = user->next;
+
+ user->next = def->uses;
+ def->uses = user;
+ user = next;
+ }
+ /* Combining def/use webs can make target registers live
+ after uses where they previously were not. This means
+ some REG_DEAD notes may no longer be correct. We could
+ be more precise about this if we looked at the combined
+ live range, but here I just delete any REG_DEAD notes
+ in case they are no longer correct. */
+ for (user = def->uses; user != NULL; user = user->next)
+ remove_note (user->insn,
+ find_regno_note (user->insn, REG_DEAD,
+ REGNO (user->use)));
+ clear_btr_from_live_range (other_def);
+ other_def->uses = NULL;
+ bitmap_copy (def->live_range, combined_live_range);
+ if (other_def->other_btr_uses_after_use)
+ def->other_btr_uses_after_use = 1;
+ COPY_HARD_REG_SET (*btrs_live_in_range, combined_btrs_live);
+
+ /* Delete the old target register initialization */
+ delete_insn (other_def->insn);
+
+ }
+ BITMAP_XFREE (combined_live_range);
+ }
+ }
+}
+
+/* Move the definition DEF from its current position to basic
+ block NEW_DEF_BB, and modify it to use branch target register BTR.
+ Delete the old defining insn, and insert a new one in NEW_DEF_BB.
+ Update all reaching uses of DEF in the RTL to use BTR.
+ If this new position means that other defs in the
+ same group can be combined with DEF then combine them. */
+static void
+move_btr_def (basic_block new_def_bb, int btr, btr_def def, bitmap live_range,
+ HARD_REG_SET *btrs_live_in_range)
+{
+ /* We can move the instruction.
+ Set a target register in block NEW_DEF_BB to the value
+ needed for this target register definition.
+ Replace all uses of the old target register definition by
+ uses of the new definition. Delete the old definition. */
+ basic_block b = new_def_bb;
+ rtx insp = b->head;
+ rtx old_insn = def->insn;
+ rtx src;
+ rtx btr_rtx;
+ rtx new_insn;
+ enum machine_mode btr_mode;
+ btr_user user;
+ rtx set;
+
+ if (rtl_dump_file)
+ fprintf(rtl_dump_file, "migrating to basic block %d, using reg %d\n",
+ new_def_bb->index, btr);
+
+ clear_btr_from_live_range (def);
+ def->btr = btr;
+ def->bb = new_def_bb;
+ def->luid = 0;
+ def->cost = basic_block_freq (new_def_bb);
+ def->other_btr_uses_before_def = 0;
+ bitmap_copy (def->live_range, live_range);
+ combine_btr_defs (def, btrs_live_in_range);
+ btr = def->btr;
+ add_btr_to_live_range (def);
+ if (GET_CODE (insp) == CODE_LABEL)
+ insp = NEXT_INSN (insp);
+ /* N.B.: insp is expected to be NOTE_INSN_BASIC_BLOCK now. Some
+ optimizations can result in insp being both first and last insn of
+ its basic block. */
+ /* ?? some assertions to check that insp is sensible? */
+
+ set = single_set (old_insn);
+ src = SET_SRC (set);
+ btr_mode = GET_MODE (SET_DEST (set));
+ btr_rtx = gen_rtx (REG, btr_mode, btr);
+
+ new_insn = gen_move_insn (btr_rtx, src);
+
+ /* Insert target register initialization at head of basic block. */
+ def->insn = emit_insn_after (new_insn, insp);
+
+ regs_ever_live[btr] = 1;
+
+ if (rtl_dump_file)
+ fprintf (rtl_dump_file, "New pt is insn %d, inserted after insn %d\n",
+ INSN_UID (def->insn), INSN_UID (insp));
+
+ /* Delete the old target register initialization */
+ delete_insn (old_insn);
+
+ /* Replace each use of the old target register by a use of the new target
+ register. */
+ for (user = def->uses; user != NULL; user = user->next)
+ {
+ /* Some extra work here to ensure consistent modes, because
+ it seems that a target register REG rtx can be given a different
+ mode depending on the context (surely that should not be
+ the case?). */
+ rtx replacement_rtx;
+ if (GET_MODE (user->use) == GET_MODE (btr_rtx)
+ || GET_MODE (user->use) == VOIDmode)
+ replacement_rtx = btr_rtx;
+ else
+ replacement_rtx = gen_rtx (REG, GET_MODE (user->use), btr);
+ replace_rtx (user->insn, user->use, replacement_rtx);
+ user->use = replacement_rtx;
+ }
+}
+
+/* We anticipate intra-block scheduling to be done. See if INSN could move
+ up within BB by N_INSNS. */
+static int
+can_move_up (basic_block bb, rtx insn, int n_insns)
+{
+ while (insn != bb->head && n_insns > 0)
+ {
+ insn = PREV_INSN (insn);
+ /* ??? What if we have an anti-dependency that actually prevents the
+ scheduler from doing the move? We'd like to re-allocate the register,
+ but not necessarily put the load into another basic block. */
+ if (INSN_P (insn))
+ n_insns--;
+ }
+ return n_insns <= 0;
+}
+
+/* Attempt to migrate the target register definition DEF to an
+ earlier point in the flowgraph.
+
+ It is a precondition of this function that DEF is migratable:
+ i.e. it has a constant source, and all uses are unambiguous.
+
+ Only migrations that reduce the cost of DEF will be made.
+ MIN_COST is the lower bound on the cost of the DEF after migration.
+ If we migrate DEF so that its cost falls below MIN_COST,
+ then we do not attempt to migrate further. The idea is that
+ we migrate defintions in a priority order based on their cost,
+ when the cost of this definition falls below MIN_COST, then
+ there is another definition with cost == MIN_COST which now
+ has a higher priority than this definition.
+
+ Return non-zero if there may be benefit from attempting to
+ migrate this DEF further (i.e. we have reduced the cost below
+ MIN_COST, but we may be able to reduce it further).
+ Return zero if no further migration is possible. */
+static int
+migrate_btr_def (btr_def def, int min_cost)
+{
+ bitmap live_range;
+ HARD_REG_SET btrs_live_in_range;
+ int btr_used_near_def = 0;
+ int def_basic_block_freq;
+ basic_block try;
+ int give_up = 0;
+ int def_moved = 0;
+ btr_user user;
+ int def_latency;
+
+ if (rtl_dump_file)
+ fprintf (rtl_dump_file,
+ "Attempting to migrate pt from insn %d (cost = %d, min_cost = %d) ... ",
+ INSN_UID (def->insn), def->cost, min_cost);
+
+ if (!def->group || def->has_ambiguous_use)
+ /* These defs are not migratable */
+ {
+ if (rtl_dump_file)
+ fprintf (rtl_dump_file, "it's not migratable\n");
+ return 0;
+ }
+
+ if (!def->uses)
+ /* We have combined this def with another in the same group, so
+ no need to consider it further.
+ */
+ {
+ if (rtl_dump_file)
+ fprintf (rtl_dump_file, "it's already combined with another pt\n");
+ return 0;
+ }
+
+ btr_def_live_range (def, &btrs_live_in_range);
+ live_range = BITMAP_XMALLOC ();
+ bitmap_copy (live_range, def->live_range);
+
+ if ((*targetm.sched.use_dfa_pipeline_interface) ())
+ def_latency = insn_default_latency (def->insn);
+ else
+ def_latency = result_ready_cost (def->insn);
+ def_latency *= issue_rate;
+
+ for (user = def->uses; user != NULL; user = user->next)
+ {
+ if (user->bb == def->bb
+ && user->luid > def->luid
+ && (def->luid + def_latency) > user->luid
+ && ! can_move_up (def->bb, def->insn,
+ (def->luid + def_latency) - user->luid))
+ {
+ btr_used_near_def = 1;
+ break;
+ }
+ }
+
+ def_basic_block_freq = basic_block_freq (def->bb);
+
+ for (try = get_immediate_dominator (dom, def->bb);
+ !give_up && try && try != ENTRY_BLOCK_PTR && def->cost >= min_cost;
+ try = get_immediate_dominator (dom, try))
+ {
+ /* Try to move the instruction that sets the target register into
+ basic block TRY. */
+ int try_freq = basic_block_freq (try);
+
+ if (rtl_dump_file)
+ fprintf (rtl_dump_file, "trying block %d ...", try->index);
+
+ if (try_freq < def_basic_block_freq
+ || (try_freq == def_basic_block_freq && btr_used_near_def))
+ {
+ int btr;
+ augment_live_range (live_range, &btrs_live_in_range, def->bb, try);
+ if (rtl_dump_file)
+ {
+ fprintf (rtl_dump_file, "Now btrs live in range are: ");
+ dump_hard_reg_set (btrs_live_in_range);
+ fprintf (rtl_dump_file, "\n");
+ }
+ btr = choose_btr (btrs_live_in_range);
+ if (btr != -1)
+ {
+ move_btr_def (try, btr, def, live_range, &btrs_live_in_range);
+ bitmap_copy(live_range, def->live_range);
+ btr_used_near_def = 0;
+ def_moved = 1;
+ def_basic_block_freq = basic_block_freq (def->bb);
+ }
+ else
+ {
+ /* There are no free target registers available to move
+ this far forward, so give up */
+ give_up = 1;
+ if (rtl_dump_file)
+ fprintf (rtl_dump_file,
+ "giving up because there are no free target registers\n");
+ }
+
+ }
+ }
+ if (!def_moved)
+ {
+ give_up = 1;
+ if (rtl_dump_file)
+ fprintf (rtl_dump_file, "failed to move\n");
+ }
+ BITMAP_XFREE (live_range);
+ return !give_up;
+}
+
+/* Attempt to move instructions that set target registers earlier
+ in the flowgraph, away from their corresponding uses. */
+static void
+migrate_btr_defs (enum reg_class btr_class, int allow_callee_save)
+{
+ fibheap_t all_btr_defs = fibheap_new ();
+ int reg;
+
+ gcc_obstack_init (&migrate_btrl_obstack);
+ if (rtl_dump_file)
+ {
+ int i;
+
+ for (i = 0; i < n_basic_blocks; i++)
+ {
+ basic_block bb = BASIC_BLOCK (i);
+ fprintf(rtl_dump_file,
+ "Basic block %d: count = %lld loop-depth = %d idom = %d\n",
+ i, bb->count, bb->loop_depth,
+ get_immediate_dominator (dom, bb)->index);
+ }
+ }
+
+ CLEAR_HARD_REG_SET (all_btrs);
+ for (first_btr = -1, reg = 0; reg < FIRST_PSEUDO_REGISTER; reg++)
+ if (TEST_HARD_REG_BIT (reg_class_contents[(int) btr_class], reg)
+ && (allow_callee_save || call_used_regs[reg] || regs_ever_live[reg]))
+ {
+ SET_HARD_REG_BIT (all_btrs, reg);
+ last_btr = reg;
+ if (first_btr < 0)
+ first_btr = reg;
+ }
+
+ btrs_live =
+ (HARD_REG_SET *) xcalloc (n_basic_blocks, sizeof (HARD_REG_SET));
+
+ build_btr_def_use_webs (all_btr_defs);
+
+ while (!fibheap_empty (all_btr_defs))
+ {
+ btr_def def =
+ (btr_def) fibheap_extract_min (all_btr_defs);
+ int min_cost = -fibheap_min_key (all_btr_defs);
+ if (migrate_btr_def (def, min_cost))
+ {
+ fibheap_insert (all_btr_defs, -def->cost, (void *) def);
+ if (rtl_dump_file)
+ {
+ fprintf (rtl_dump_file,
+ "Putting insn %d back on queue with priority %d\n",
+ INSN_UID (def->insn), def->cost);
+ }
+ }
+ else
+ {
+ if (def->live_range)
+ BITMAP_XFREE (def->live_range);
+ }
+ }
+
+ free (btrs_live);
+ obstack_free (&migrate_btrl_obstack, NULL);
+ fibheap_delete (all_btr_defs);
+}
+
+void
+branch_target_load_optimize (rtx insns, bool after_prologue_epilogue_gen)
+{
+ enum reg_class class = (*targetm.branch_target_register_class) ();
+ if (class != NO_REGS)
+ {
+ /* Initialize issue_rate. */
+ if (targetm.sched.issue_rate)
+ issue_rate = (*targetm.sched.issue_rate) ();
+ else
+ issue_rate = 1;
+
+ /* Build the CFG for migrate_btr_defs. */
+#if 1
+ /* This may or may not be needed, depending on where we
+ run this phase. */
+ cleanup_cfg (optimize ? CLEANUP_EXPENSIVE : 0);
+#endif
+
+ life_analysis (insns, NULL, 0);
+
+ /* Dominator info is also needed for migrate_btr_def. */
+ dom = calculate_dominance_info (CDI_DOMINATORS);
+ migrate_btr_defs (class,
+ ((*targetm.branch_target_register_callee_saved)
+ (after_prologue_epilogue_gen)));
+
+ free_dominance_info (dom);
+
+ update_life_info (NULL, UPDATE_LIFE_GLOBAL_RM_NOTES,
+ PROP_DEATH_NOTES | PROP_REG_INFO);
+ }
+}
OpenPOWER on IntegriCloud