/* Register renaming for the GNU compiler.
- Copyright (C) 2000 Free Software Foundation, Inc.
+ Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009
+ Free Software Foundation, Inc.
- This file is part of GNU CC.
+ This file is part of GCC.
- GNU CC 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)
+ 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.
- GNU CC 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.
+ 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 GNU CC; see the file COPYING. If not, write to
- the Free Software Foundation, 59 Temple Place - Suite 330,
- Boston, MA 02111-1307, USA. */
+ along with GCC; see the file COPYING3. If not see
+ <http://www.gnu.org/licenses/>. */
#include "config.h"
#include "system.h"
-#include "tree.h"
+#include "coretypes.h"
+#include "tm.h"
#include "rtl.h"
-#include "basic-block.h"
+#include "tm_p.h"
#include "insn-config.h"
#include "regs.h"
+#include "addresses.h"
#include "hard-reg-set.h"
-#include "flags.h"
+#include "basic-block.h"
+#include "reload.h"
#include "output.h"
#include "function.h"
#include "recog.h"
-#include "resource.h"
-
-static const char *const reg_class_names[] = REG_CLASS_NAMES;
-
-/* ??? Consider a more sparse data structure? */
-typedef struct def_uses
- {
- /* high bound of defs and uses */
- int high_bound;
-
- /* 1 if insn y defines a reg whose use crosses a call
- y is the ordinal position of the insn within the block */
- sbitmap require_call_save_reg;
-
- /* REGNO x INSN y 1 if insn y sets reg x */
- sbitmap *defs;
-
- /* REGNO x INSN y The register class for this def */
- enum reg_class *def_class;
-
- /* REGNO x INSN y 1 if insn y uses reg x */
- sbitmap *uses;
-
- /* REGNO x INSN y The register class for this use */
- enum reg_class *use_class;
- }
-def_uses;
-
-#define DU_REG_CLASS(rc,r,high_bound,i) (rc[r * high_bound + i])
-
-typedef struct ext_basic_blocks
- {
- /* n_basic_blocks x n_basic_blocks y 1 if bb y is in extended bb
- having entry x */
- sbitmap *basic_block;
-
- /* n_basic_blocks x n_basic_blocks y 1 if bb y is an exit block */
- sbitmap *exit;
- }
-ext_basic_blocks;
-
-#define UID_RUID_HIGH_BOUND 64
-#define DESTINATION 1
-#define SOURCE 2
-
-static void build_def_use PARAMS ((int, ext_basic_blocks *, HARD_REG_SET *,
- def_uses *, sbitmap *));
-static int replace_reg_in_block
- PARAMS ((def_uses *, varray_type *, int, rtx, int));
-static int consider_def PARAMS ((rtx, int, def_uses *, int));
-static int consider_available PARAMS ((rtx, int, HARD_REG_SET *, int, def_uses *, int));
-static rtx rr_replace_reg PARAMS ((rtx, rtx, rtx, int, rtx, int *));
-static int consider_use PARAMS ((rtx, int, int, int));
-static int condmove_p PARAMS ((rtx));
-static void dump_def_use_chain PARAMS ((HARD_REG_SET *, def_uses *,
- varray_type *));
-static void dump_ext_bb_info PARAMS ((int, ext_basic_blocks *));
-static void find_ext_basic_blocks PARAMS ((ext_basic_blocks *));
-static void find_one_ext_basic_block PARAMS ((int, basic_block, sbitmap *,
- ext_basic_blocks *));
-static enum reg_class get_reg_class PARAMS ((rtx, rtx, int, enum reg_class));
-static rtx regno_first_use_in PARAMS ((int, rtx));
-
-void
-regrename_optimize ()
+#include "flags.h"
+#include "toplev.h"
+#include "obstack.h"
+#include "timevar.h"
+#include "tree-pass.h"
+#include "df.h"
+
+struct du_chain
{
- int b, eb, i, inum, r, rc, replace_ok;
+ struct du_chain *next_chain;
+ struct du_chain *next_use;
+
rtx insn;
- def_uses du;
- ext_basic_blocks ebb;
+ rtx *loc;
+ ENUM_BITFIELD(reg_class) cl : 16;
+ unsigned int need_caller_save_reg:1;
+ unsigned int earlyclobber:1;
+};
+enum scan_actions
+{
+ terminate_all_read,
+ terminate_overlapping_read,
+ terminate_write,
+ terminate_dead,
+ mark_read,
+ mark_write,
+ /* mark_access is for marking the destination regs in
+ REG_FRAME_RELATED_EXPR notes (as if they were read) so that the
+ note is updated properly. */
+ mark_access
+};
+
+static const char * const scan_actions_name[] =
+{
+ "terminate_all_read",
+ "terminate_overlapping_read",
+ "terminate_write",
+ "terminate_dead",
+ "mark_read",
+ "mark_write",
+ "mark_access"
+};
+
+static struct obstack rename_obstack;
+
+static void do_replace (struct du_chain *, int);
+static void scan_rtx_reg (rtx, rtx *, enum reg_class,
+ enum scan_actions, enum op_type, int);
+static void scan_rtx_address (rtx, rtx *, enum reg_class,
+ enum scan_actions, enum machine_mode);
+static void scan_rtx (rtx, rtx *, enum reg_class, enum scan_actions,
+ enum op_type, int);
+static struct du_chain *build_def_use (basic_block);
+static void dump_def_use_chain (struct du_chain *);
+static void note_sets (rtx, const_rtx, void *);
+static void clear_dead_regs (HARD_REG_SET *, enum reg_note, rtx);
+static void merge_overlapping_regs (basic_block, HARD_REG_SET *,
+ struct du_chain *);
+
+/* Called through note_stores. Find sets of registers, and
+ record them in *DATA (which is actually a HARD_REG_SET *). */
- /* Registers used in a given class */
- HARD_REG_SET class_regs;
+static void
+note_sets (rtx x, const_rtx set ATTRIBUTE_UNUSED, void *data)
+{
+ HARD_REG_SET *pset = (HARD_REG_SET *) data;
+
+ if (GET_CODE (x) == SUBREG)
+ x = SUBREG_REG (x);
+ if (!REG_P (x))
+ return;
+ /* There must not be pseudos at this point. */
+ gcc_assert (HARD_REGISTER_P (x));
+ add_to_hard_reg_set (pset, GET_MODE (x), REGNO (x));
+}
- /* Registers available for use as renaming registers */
- HARD_REG_SET avail_regs;
+/* Clear all registers from *PSET for which a note of kind KIND can be found
+ in the list NOTES. */
- /* Registers used in the block */
- HARD_REG_SET regs_used;
+static void
+clear_dead_regs (HARD_REG_SET *pset, enum reg_note kind, rtx notes)
+{
+ rtx note;
+ for (note = notes; note; note = XEXP (note, 1))
+ if (REG_NOTE_KIND (note) == kind && REG_P (XEXP (note, 0)))
+ {
+ rtx reg = XEXP (note, 0);
+ /* There must not be pseudos at this point. */
+ gcc_assert (HARD_REGISTER_P (reg));
+ remove_from_hard_reg_set (pset, GET_MODE (reg), REGNO (reg));
+ }
+}
- /* Registers which have been used as renaming registers */
- HARD_REG_SET renamed_regs;
+/* For a def-use chain CHAIN in basic block B, find which registers overlap
+ its lifetime and set the corresponding bits in *PSET. */
- HARD_REG_SET global_live_at_end, global_live_at_start;
+static void
+merge_overlapping_regs (basic_block b, HARD_REG_SET *pset,
+ struct du_chain *chain)
+{
+ struct du_chain *t = chain;
+ rtx insn;
+ HARD_REG_SET live;
+ df_ref *def_rec;
- HARD_REG_SET null_bitmap, tmp_bitmap;
+ REG_SET_TO_HARD_REG_SET (live, df_get_live_in (b));
+ for (def_rec = df_get_artificial_defs (b->index); *def_rec; def_rec++)
+ {
+ df_ref def = *def_rec;
+ if (DF_REF_FLAGS (def) & DF_REF_AT_TOP)
+ SET_HARD_REG_BIT (live, DF_REF_REGNO (def));
+ }
+ insn = BB_HEAD (b);
+ while (t)
+ {
+ /* Search forward until the next reference to the register to be
+ renamed. */
+ while (insn != t->insn)
+ {
+ if (INSN_P (insn))
+ {
+ clear_dead_regs (&live, REG_DEAD, REG_NOTES (insn));
+ note_stores (PATTERN (insn), note_sets, (void *) &live);
+ /* Only record currently live regs if we are inside the
+ reg's live range. */
+ if (t != chain)
+ IOR_HARD_REG_SET (*pset, live);
+ clear_dead_regs (&live, REG_UNUSED, REG_NOTES (insn));
+ }
+ insn = NEXT_INSN (insn);
+ }
- /* 1 if insn y sets a register which is live at the end of the block */
- sbitmap defs_live_exit;
+ IOR_HARD_REG_SET (*pset, live);
- /* Mapping from insn y (ordinal position in block) to INSN_UID */
- varray_type uid_ruid;
+ /* For the last reference, also merge in all registers set in the
+ same insn.
+ @@@ We only have take earlyclobbered sets into account. */
+ if (! t->next_use)
+ note_stores (PATTERN (insn), note_sets, (void *) pset);
- /* Mapping from insn y (ordinal position in block) to block id */
- varray_type uid_rbid;
+ t = t->next_use;
+ }
+}
- /* Ordinal position in block of defining insn */
- int *def_idx;
+/* Perform register renaming on the current function. */
- VARRAY_RTX_INIT (uid_ruid, UID_RUID_HIGH_BOUND + 1, "uid_ruid");
- VARRAY_LONG_INIT (uid_rbid, UID_RUID_HIGH_BOUND + 1, "uid_rbid");
+static unsigned int
+regrename_optimize (void)
+{
+ int tick[FIRST_PSEUDO_REGISTER];
+ int this_tick = 0;
+ basic_block bb;
+ char *first_obj;
- ebb.basic_block =
- sbitmap_vector_alloc (n_basic_blocks, n_basic_blocks);
- sbitmap_vector_zero (ebb.basic_block, n_basic_blocks);
- ebb.exit =
- sbitmap_vector_alloc (n_basic_blocks, n_basic_blocks);
- sbitmap_vector_zero (ebb.exit, n_basic_blocks);
+ df_set_flags (DF_LR_RUN_DCE);
+ df_note_add_problem ();
+ df_analyze ();
+ df_set_flags (DF_DEFER_INSN_RESCAN);
- find_ext_basic_blocks (&ebb);
+ memset (tick, 0, sizeof tick);
- du.def_class = du.use_class = 0;
+ gcc_obstack_init (&rename_obstack);
+ first_obj = XOBNEWVAR (&rename_obstack, char, 0);
- /* Build uid_ruid and uid_rbid for this extended basic block */
- for (b = 0; b < n_basic_blocks; b++)
- if (TEST_BIT (ebb.basic_block[b], b))
- {
- for (eb = du.high_bound = 0; eb < n_basic_blocks; eb++)
- {
- if (TEST_BIT (ebb.basic_block[b], eb))
- {
- basic_block bb = BASIC_BLOCK (eb);
- /* Calculate high bound for uid_ruid and allocate if necessary */
- for (insn = bb->head;
- insn != NEXT_INSN (bb->end);
- du.high_bound++, insn = NEXT_INSN (insn))
- {
- int uid_ruid_high_bound = VARRAY_SIZE (uid_ruid);
- if (du.high_bound + 4 >= uid_ruid_high_bound)
- {
- VARRAY_GROW (uid_ruid, uid_ruid_high_bound * 2);
- VARRAY_GROW (uid_rbid, uid_ruid_high_bound * 2);
- }
- VARRAY_RTX (uid_ruid, du.high_bound) = insn;
- VARRAY_LONG (uid_rbid, du.high_bound) = eb;
- }
- }
- }
+ FOR_EACH_BB (bb)
+ {
+ struct du_chain *all_chains = 0;
+ HARD_REG_SET unavailable;
+ HARD_REG_SET regs_seen;
- CLEAR_HARD_REG_SET (null_bitmap);
- CLEAR_HARD_REG_SET (class_regs);
- CLEAR_HARD_REG_SET (regs_used);
- CLEAR_HARD_REG_SET (avail_regs);
- CLEAR_HARD_REG_SET (tmp_bitmap);
- CLEAR_HARD_REG_SET (renamed_regs);
-
- du.defs =
- sbitmap_vector_alloc (FIRST_PSEUDO_REGISTER, du.high_bound + 1);
- sbitmap_vector_zero (du.defs, FIRST_PSEUDO_REGISTER);
- du.uses =
- sbitmap_vector_alloc (FIRST_PSEUDO_REGISTER, du.high_bound + 1);
- sbitmap_vector_zero (du.uses, FIRST_PSEUDO_REGISTER);
- du.require_call_save_reg = sbitmap_alloc (du.high_bound + 1);
- sbitmap_zero (du.require_call_save_reg);
- defs_live_exit = sbitmap_alloc (du.high_bound + 1);
- sbitmap_zero (defs_live_exit);
-
- du.def_class = xrealloc
- (du.def_class,
- sizeof (enum reg_class) * FIRST_PSEUDO_REGISTER * du.high_bound);
-
- du.use_class = xrealloc
- (du.use_class,
- sizeof (enum reg_class) * FIRST_PSEUDO_REGISTER * du.high_bound);
-
- build_def_use (b, &ebb, ®s_used, &du,
- &defs_live_exit);
-
- if (rtl_dump_file)
- {
- dump_ext_bb_info (b, &ebb);
- dump_def_use_chain (&global_live_at_end, &du, &uid_ruid);
- }
+ CLEAR_HARD_REG_SET (unavailable);
- /* Available registers are not: used in the block, live at the start,
- live at the end, a register we've renamed to. */
- /* ??? The current algorithm is pessimistic for extended basic blocks
- as it just treats them as a big basic block. */
+ if (dump_file)
+ fprintf (dump_file, "\nBasic block %d:\n", bb->index);
- COPY_HARD_REG_SET (tmp_bitmap, regs_used);
- REG_SET_TO_HARD_REG_SET (global_live_at_start, BASIC_BLOCK (b)->global_live_at_start);
- IOR_HARD_REG_SET (tmp_bitmap, global_live_at_start);
- for (eb = 0; eb < n_basic_blocks; eb++)
- {
- if (TEST_BIT (ebb.basic_block[b], eb))
- {
- basic_block bb = BASIC_BLOCK (eb);
- REG_SET_TO_HARD_REG_SET (global_live_at_end, bb->global_live_at_end);
- IOR_HARD_REG_SET (tmp_bitmap, global_live_at_end);
- }
- }
+ all_chains = build_def_use (bb);
- def_idx = xcalloc (du.high_bound, sizeof (int));
+ if (dump_file)
+ dump_def_use_chain (all_chains);
- /* Only consider registers in this extended block and in this class
- that are defined more than once. Replace them if permissible. */
- for (r = 0; r < FIRST_PSEUDO_REGISTER; r++)
- {
- int avail_reg, ar_idx, def, def_cnt = 0, use_idx, call_idx;
+ CLEAR_HARD_REG_SET (unavailable);
+ /* Don't clobber traceback for noreturn functions. */
+ if (frame_pointer_needed)
+ {
+ add_to_hard_reg_set (&unavailable, Pmode, FRAME_POINTER_REGNUM);
+#if FRAME_POINTER_REGNUM != HARD_FRAME_POINTER_REGNUM
+ add_to_hard_reg_set (&unavailable, Pmode, HARD_FRAME_POINTER_REGNUM);
+#endif
+ }
- if (!TEST_HARD_REG_BIT (regs_used, r)
- || fixed_regs[r]
- || r == FRAME_POINTER_REGNUM)
- continue;
+ CLEAR_HARD_REG_SET (regs_seen);
+ while (all_chains)
+ {
+ int new_reg, best_new_reg;
+ int n_uses;
+ struct du_chain *this_du = all_chains;
+ struct du_chain *tmp, *last;
+ HARD_REG_SET this_unavailable;
+ int reg = REGNO (*this_du->loc);
+ int i;
- /* Find def_idx[N] where hbound of N is the number of
- definitions of this register in this block. and def_idx
- is the ordinal position of this insn in the block. */
- for (i = 0, def_idx[def_cnt] = 0;
- i < du.high_bound;
- i++)
- {
- if (TEST_BIT (du.defs[r], i)
- && consider_def (VARRAY_RTX (uid_ruid, i), r,
- &du, i))
- {
- int first_use = 1;
- def_idx[def_cnt] = i;
-
- /* Only consider definitions that have a use. */
- for (use_idx = i + 1; use_idx < du.high_bound;
- use_idx++)
- {
- if (TEST_BIT (du.uses[r], use_idx))
- {
- if (consider_use (VARRAY_RTX (uid_ruid, use_idx), r,
- VARRAY_LONG (uid_rbid, i),
- VARRAY_LONG (uid_rbid, use_idx)))
- {
- if (first_use)
- {
- first_use = 0;
- def_cnt++;
- }
- }
- else
- {
- /* Don't consider def if we don't want this use */
- if (!first_use)
- def_cnt--;
- break;
- }
- }
- if (TEST_BIT (du.defs[r], use_idx))
- break;
- }
- /* Scan until the next def to avoid renaming
- parameter registers. */
- /* ??? consider using CALL_INSN_FUNCTION_USAGE */
- for (call_idx = i; call_idx <= use_idx; call_idx++)
- if (VARRAY_RTX (uid_ruid, call_idx)
- && GET_CODE (VARRAY_RTX (uid_ruid, call_idx))
- == CALL_INSN)
- {
- SET_BIT (du.require_call_save_reg, i);
- }
- }
- }
- if (def_cnt < 2)
- continue;
+ all_chains = this_du->next_chain;
- /* We have more than one def so rename until we exhaust
- renaming registers. */
- /* ??? Should we continue renaming round robin when we exhaust
- renaming registers? */
- for (def = 0; def < def_cnt - 1; def++)
- {
- if (!TEST_BIT (defs_live_exit, def_idx[def])
- && (GET_RTX_CLASS
- (GET_CODE (VARRAY_RTX (uid_ruid,
- def_idx[def]))) == 'i'))
- {
- rtx reg_use = regno_first_use_in
- (r, PATTERN (VARRAY_RTX (uid_ruid, def_idx[def])));
-
- if (!reg_use)
- break;
-#ifdef STACK_REGS
- /* Don't bother with stacked float registers */
- if (GET_MODE_CLASS (GET_MODE (reg_use)) == MODE_FLOAT)
- break;
+ best_new_reg = reg;
+
+#if 0 /* This just disables optimization opportunities. */
+ /* Only rename once we've seen the reg more than once. */
+ if (! TEST_HARD_REG_BIT (regs_seen, reg))
+ {
+ SET_HARD_REG_BIT (regs_seen, reg);
+ continue;
+ }
#endif
- rc = (int) DU_REG_CLASS (du.def_class,
- r, du.high_bound, def_idx[def]);
- COPY_HARD_REG_SET (avail_regs,
- reg_class_contents[(enum reg_class) rc]);
- AND_COMPL_HARD_REG_SET (avail_regs, tmp_bitmap);
- AND_COMPL_HARD_REG_SET (avail_regs, renamed_regs);
-
- /* No available registers in this class */
- GO_IF_HARD_REG_EQUAL (avail_regs, null_bitmap,
- no_available_regs);
- for (ar_idx = 0; ar_idx < FIRST_PSEUDO_REGISTER
- && TEST_HARD_REG_BIT (avail_regs, ar_idx); ar_idx++)
- ;
- if (ar_idx == FIRST_PSEUDO_REGISTER)
- goto no_available_regs;
-
- /* Only try register renaming if there is an available
- register in this class. */
- for (ar_idx = 0;
- ar_idx < FIRST_PSEUDO_REGISTER;
- ar_idx++)
- {
-#ifdef REG_ALLOC_ORDER
- avail_reg = reg_alloc_order[ar_idx];
+
+ if (fixed_regs[reg] || global_regs[reg]
+#if FRAME_POINTER_REGNUM != HARD_FRAME_POINTER_REGNUM
+ || (frame_pointer_needed && reg == HARD_FRAME_POINTER_REGNUM)
#else
- avail_reg = ar_idx;
+ || (frame_pointer_needed && reg == FRAME_POINTER_REGNUM)
#endif
- if (consider_available (reg_use, avail_reg, &avail_regs,
- rc, &du, def_idx[def]))
- break;
- }
-
- if (ar_idx == FIRST_PSEUDO_REGISTER)
- {
- if (rtl_dump_file)
- {
- fprintf (rtl_dump_file,
- "Register %s in class %s",
- reg_names[r], reg_class_names[rc]);
- fprintf (rtl_dump_file,
- " in insn %d",
- INSN_UID (VARRAY_RTX (uid_ruid,
- def_idx[def])));
-
- if (TEST_BIT (du.require_call_save_reg,
- def_idx[def]))
- fprintf (rtl_dump_file, " crosses a call");
- fprintf (rtl_dump_file, ". No available registers\n");
- }
- goto try_next_def;
- }
-
- SET_HARD_REG_BIT (renamed_regs, avail_reg);
- CLEAR_HARD_REG_BIT (avail_regs, avail_reg);
-
- /* Replace in destination. Replace in source for
- remainder of block until new register is defined
- again */
- replace_ok = replace_reg_in_block
- (&du, &uid_ruid, def_idx[def], reg_use, avail_reg);
- /* Replace failed, so restore previous register */
- if (!replace_ok)
- {
- replace_reg_in_block (&du, &uid_ruid, def_idx[def],
- gen_rtx_REG (GET_MODE (reg_use),
- avail_reg),
- REGNO (reg_use));
- if (rtl_dump_file)
- fprintf (rtl_dump_file,
- "Register %s in class %s Renaming as %s would not satisfy constraints\n",
- reg_names[r], reg_class_names[rc],
- reg_names[avail_reg]);
- }
- else if (rtl_dump_file)
- fprintf (rtl_dump_file,
- "Register %s in class %s Renamed as %s at insn %d\n",
- reg_names[r], reg_class_names[rc],
- reg_names[avail_reg],
- INSN_UID (VARRAY_RTX (uid_ruid, def_idx[def])));
- }
- try_next_def:
- continue;
- }
- sbitmap_zero (du.defs[r]);
- no_available_regs:
+ )
continue;
- }
- free (def_idx);
- sbitmap_vector_free (du.defs);
- sbitmap_vector_free (du.uses);
- sbitmap_free (du.require_call_save_reg);
- sbitmap_free (defs_live_exit);
- CLEAR_HARD_REG_SET (regs_used);
- CLEAR_HARD_REG_SET (renamed_regs);
-
- for (inum = 0; inum < (int) VARRAY_SIZE (uid_ruid); inum++)
- VARRAY_RTX (uid_ruid, inum) = (rtx) 0;
- }
- sbitmap_vector_free (ebb.basic_block);
- sbitmap_vector_free (ebb.exit);
-}
-
-/* Build def/use chain DU for extended basic block EBB having root B.
- Also determine which regs are used, REGS_USED, and which insns define
- a live at exit def, DEFS_LIVE_EXIT */
+ COPY_HARD_REG_SET (this_unavailable, unavailable);
-static void
-build_def_use (b, ebb, regs_used, du, defs_live_exit)
- int b;
- ext_basic_blocks *ebb;
- HARD_REG_SET *regs_used;
- def_uses *du;
- sbitmap *defs_live_exit;
-{
- rtx insn;
- int eb, inum, r;
-
- inum = 0;
- for (eb = 0; eb < n_basic_blocks; eb++)
- {
- basic_block bb = BASIC_BLOCK (eb);
-
- if (!TEST_BIT (ebb->basic_block[b], eb))
- continue;
+ /* Find last entry on chain (which has the need_caller_save bit),
+ count number of uses, and narrow the set of registers we can
+ use for renaming. */
+ n_uses = 0;
+ for (last = this_du; last->next_use; last = last->next_use)
+ {
+ n_uses++;
+ IOR_COMPL_HARD_REG_SET (this_unavailable,
+ reg_class_contents[last->cl]);
+ }
+ if (n_uses < 1)
+ continue;
- for (insn = bb->head;
- insn != NEXT_INSN (bb->end);
- inum++, insn = NEXT_INSN (insn))
- {
- struct resources insn_res;
- struct resources insn_sets;
+ IOR_COMPL_HARD_REG_SET (this_unavailable,
+ reg_class_contents[last->cl]);
- if (GET_RTX_CLASS (GET_CODE (insn)) != 'i')
- continue;
+ if (this_du->need_caller_save_reg)
+ IOR_HARD_REG_SET (this_unavailable, call_used_reg_set);
- CLEAR_RESOURCE (&insn_sets);
- mark_set_resources (insn, &insn_sets, 0, MARK_DEST);
+ merge_overlapping_regs (bb, &this_unavailable, this_du);
- for (r = 0;
- r < FIRST_PSEUDO_REGISTER;
- r++)
+ /* Now potential_regs is a reasonable approximation, let's
+ have a closer look at each register still in there. */
+ for (new_reg = 0; new_reg < FIRST_PSEUDO_REGISTER; new_reg++)
{
- if (!TEST_HARD_REG_BIT (insn_sets.regs, r))
+ int nregs = hard_regno_nregs[new_reg][GET_MODE (*this_du->loc)];
+
+ for (i = nregs - 1; i >= 0; --i)
+ if (TEST_HARD_REG_BIT (this_unavailable, new_reg + i)
+ || fixed_regs[new_reg + i]
+ || global_regs[new_reg + i]
+ /* Can't use regs which aren't saved by the prologue. */
+ || (! df_regs_ever_live_p (new_reg + i)
+ && ! call_used_regs[new_reg + i])
+#ifdef LEAF_REGISTERS
+ /* We can't use a non-leaf register if we're in a
+ leaf function. */
+ || (current_function_is_leaf
+ && !LEAF_REGISTERS[new_reg + i])
+#endif
+#ifdef HARD_REGNO_RENAME_OK
+ || ! HARD_REGNO_RENAME_OK (reg + i, new_reg + i)
+#endif
+ )
+ break;
+ if (i >= 0)
continue;
- SET_HARD_REG_BIT (*regs_used, r);
- if (REGNO_REG_SET_P (bb->global_live_at_end, r))
- SET_BIT (*defs_live_exit, inum);
- if (!insn_sets.memory)
- SET_BIT (du->defs[r], inum);
- DU_REG_CLASS (du->def_class, r, du->high_bound, inum) = get_reg_class
- (insn, regno_first_use_in (r, PATTERN (insn)),
- DESTINATION, NO_REGS);
+ /* See whether it accepts all modes that occur in
+ definition and uses. */
+ for (tmp = this_du; tmp; tmp = tmp->next_use)
+ if (! HARD_REGNO_MODE_OK (new_reg, GET_MODE (*tmp->loc))
+ || (tmp->need_caller_save_reg
+ && ! (HARD_REGNO_CALL_PART_CLOBBERED
+ (reg, GET_MODE (*tmp->loc)))
+ && (HARD_REGNO_CALL_PART_CLOBBERED
+ (new_reg, GET_MODE (*tmp->loc)))))
+ break;
+ if (! tmp)
+ {
+ if (tick[best_new_reg] > tick[new_reg])
+ best_new_reg = new_reg;
+ }
}
- CLEAR_RESOURCE (&insn_res);
- mark_referenced_resources (insn, &insn_res, 0);
-
- for (r = 0;
- r < FIRST_PSEUDO_REGISTER;
- r++)
+ if (dump_file)
{
- if (!TEST_HARD_REG_BIT (insn_res.regs, r))
- continue;
+ fprintf (dump_file, "Register %s in insn %d",
+ reg_names[reg], INSN_UID (last->insn));
+ if (last->need_caller_save_reg)
+ fprintf (dump_file, " crosses a call");
+ }
- SET_HARD_REG_BIT (*regs_used, r);
- SET_BIT (du->uses[r], inum);
- DU_REG_CLASS (du->use_class, r, du->high_bound, inum) = get_reg_class
- (insn, regno_use_in (r, PATTERN (insn)),
- SOURCE, NO_REGS);
+ if (best_new_reg == reg)
+ {
+ tick[reg] = ++this_tick;
+ if (dump_file)
+ fprintf (dump_file, "; no available better choice\n");
+ continue;
}
+
+ if (dump_file)
+ fprintf (dump_file, ", renamed as %s\n", reg_names[best_new_reg]);
+
+ do_replace (this_du, best_new_reg);
+ tick[best_new_reg] = ++this_tick;
+ df_set_regs_ever_live (best_new_reg, true);
}
+
+ obstack_free (&rename_obstack, first_obj);
}
- free_resource_info ();
-}
-/* Return nonzero if regno AVAIL_REG can replace REG_DEF for insns in UID_RUID
- starting at insn DEF in def/use chain DU. */
+ obstack_free (&rename_obstack, NULL);
+
+ if (dump_file)
+ fputc ('\n', dump_file);
-static int
-replace_reg_in_block (du, uid_ruid, def, reg_def, avail_reg)
- def_uses *du;
- varray_type *uid_ruid;
- int def;
- rtx reg_def;
- int avail_reg;
+ return 0;
+}
+
+static void
+do_replace (struct du_chain *chain, int reg)
{
- int du_idx, status = 1;
- int r = REGNO (reg_def);
- rtx death_note;
- rtx new_reg = gen_rtx_REG (GET_MODE (reg_def), avail_reg);
-
-
- rr_replace_reg (PATTERN (VARRAY_RTX (*uid_ruid, def)), reg_def,
- new_reg, DESTINATION, VARRAY_RTX (*uid_ruid, def),
- &status);
- if (!status)
- return status;
-
- death_note = find_reg_note (VARRAY_RTX (*uid_ruid, def), REG_DEAD, reg_def);
- if (!death_note)
- death_note = find_reg_note (VARRAY_RTX (*uid_ruid, def), REG_UNUSED, reg_def);
- if (death_note)
- rr_replace_reg (death_note, reg_def, new_reg, 0,
- VARRAY_RTX (*uid_ruid, def), &status);
-
- for (du_idx = def + 1; du_idx < du->high_bound; du_idx++)
+ while (chain)
{
- rtx reg_use;
- rtx new_reg;
- if (GET_RTX_CLASS (GET_CODE (VARRAY_RTX (*uid_ruid, du_idx))) != 'i')
- continue;
- reg_use = regno_use_in (r, PATTERN (VARRAY_RTX (*uid_ruid, du_idx)));
-
- if (reg_use && TEST_BIT (du->uses[r], du_idx))
- {
- new_reg = gen_rtx_REG (GET_MODE (reg_use), avail_reg);
- rr_replace_reg (PATTERN (VARRAY_RTX (*uid_ruid, du_idx)), reg_use,
- new_reg, SOURCE, VARRAY_RTX (*uid_ruid, du_idx),
- &status);
- death_note = find_reg_note (VARRAY_RTX (*uid_ruid, du_idx),
- REG_DEAD, reg_use);
- if (!death_note)
- death_note = find_reg_note (VARRAY_RTX (*uid_ruid, du_idx),
- REG_UNUSED, reg_use);
- if (death_note)
- rr_replace_reg (death_note, reg_use, new_reg, 0,
- VARRAY_RTX (*uid_ruid, def), &status);
- SET_BIT (du->uses[avail_reg], du_idx);
- RESET_BIT (du->uses[r], du_idx);
- if (!status)
- return status;
- }
- if (TEST_BIT (du->defs[r], du_idx))
- break;
+ unsigned int regno = ORIGINAL_REGNO (*chain->loc);
+ struct reg_attrs * attr = REG_ATTRS (*chain->loc);
+ int reg_ptr = REG_POINTER (*chain->loc);
+
+ *chain->loc = gen_raw_REG (GET_MODE (*chain->loc), reg);
+ if (regno >= FIRST_PSEUDO_REGISTER)
+ ORIGINAL_REGNO (*chain->loc) = regno;
+ REG_ATTRS (*chain->loc) = attr;
+ REG_POINTER (*chain->loc) = reg_ptr;
+ df_insn_rescan (chain->insn);
+ chain = chain->next_use;
}
- return status;
}
-/* Try to replace REG_USE in X with REG_SUB if INSN has a REPLACE_TYPE.
- STATUS is zero if the resulting pattern is not valid. */
-
-static rtx
-rr_replace_reg (x, reg_use, reg_sub, replace_type, insn, status)
- rtx x;
- rtx reg_use;
- rtx reg_sub;
- int replace_type;
- rtx insn;
- int *status;
-{
- enum rtx_code code;
- int i;
- const char *fmt;
- int n;
- if (x == 0)
- return x;
+static struct du_chain *open_chains;
+static struct du_chain *closed_chains;
- code = GET_CODE (x);
- switch (code)
+static void
+scan_rtx_reg (rtx insn, rtx *loc, enum reg_class cl,
+ enum scan_actions action, enum op_type type, int earlyclobber)
+{
+ struct du_chain **p;
+ rtx x = *loc;
+ enum machine_mode mode = GET_MODE (x);
+ int this_regno = REGNO (x);
+ int this_nregs = hard_regno_nregs[this_regno][mode];
+
+ if (action == mark_write)
{
- case REG:
- if (REGNO (x) == REGNO (reg_use))
+ if (type == OP_OUT)
{
- if (GET_MODE (x) == GET_MODE (reg_use))
- return reg_sub;
- else
- return gen_rtx_REG (GET_MODE (x), REGNO (reg_use));
+ struct du_chain *this_du = XOBNEW (&rename_obstack, struct du_chain);
+ this_du->next_use = 0;
+ this_du->next_chain = open_chains;
+ this_du->loc = loc;
+ this_du->insn = insn;
+ this_du->cl = cl;
+ this_du->need_caller_save_reg = 0;
+ this_du->earlyclobber = earlyclobber;
+ open_chains = this_du;
}
- return x;
+ return;
+ }
- case SET:
- if (replace_type == DESTINATION)
- SET_DEST (x) = rr_replace_reg (SET_DEST (x), reg_use, reg_sub,
- replace_type, insn, status);
- else if (replace_type == SOURCE)
- {
- int dest_subregno;
+ if ((type == OP_OUT) != (action == terminate_write || action == mark_access))
+ return;
- if (GET_CODE (SET_DEST (x)) == SUBREG)
- dest_subregno = REGNO (XEXP (SET_DEST (x), 0));
- else
- dest_subregno = 0;
-
- SET_SRC (x) = rr_replace_reg (SET_SRC (x), reg_use, reg_sub,
- replace_type, insn, status);
- /* If the replacement register is not part of the source
- then it may be part of a source mem operand. */
- if (GET_CODE (SET_DEST (x)) == MEM
- || GET_CODE (SET_DEST (x)) == ZERO_EXTRACT
- || GET_CODE (SET_DEST (x)) == SIGN_EXTRACT
- || GET_CODE (SET_DEST (x)) == STRICT_LOW_PART)
- SET_DEST (x) = rr_replace_reg (SET_DEST (x), reg_use, reg_sub,
- replace_type, insn, status);
- /* shared rtl sanity check */
- if (dest_subregno
- && dest_subregno != REGNO (XEXP (SET_DEST (x), 0)))
- {
- *status = 0;
- return x;
- }
- }
+ for (p = &open_chains; *p;)
+ {
+ struct du_chain *this_du = *p;
+
+ /* Check if the chain has been terminated if it has then skip to
+ the next chain.
- n = recog_memoized (insn);
- if (n >= 0)
+ This can happen when we've already appended the location to
+ the chain in Step 3, but are trying to hide in-out operands
+ from terminate_write in Step 5. */
+
+ if (*this_du->loc == cc0_rtx)
+ p = &this_du->next_chain;
+ else
{
- int id;
- extract_insn (insn);
+ int regno = REGNO (*this_du->loc);
+ int nregs = hard_regno_nregs[regno][GET_MODE (*this_du->loc)];
+ int exact_match = (regno == this_regno && nregs == this_nregs);
- /* Any MATCH_DUP's which are REGs must still match */
- for (id = insn_data[n].n_dups - 1; id >= 0; id--)
+ if (regno + nregs <= this_regno
+ || this_regno + this_nregs <= regno)
{
- int opno = recog_data.dup_num[id];
- if (GET_CODE (*recog_data.dup_loc[id]) == REG
- && GET_CODE (*recog_data.operand_loc[opno]) == REG
- && (REGNO (*recog_data.dup_loc[id]) !=
- REGNO (*recog_data.operand_loc[opno])))
- *status = 0;
+ p = &this_du->next_chain;
+ continue;
}
- if (!constrain_operands (1))
+ if (action == mark_read || action == mark_access)
{
- *status = 0;
- validate_replace_rtx (reg_sub, reg_use, insn);
+ gcc_assert (exact_match);
+
+ /* ??? Class NO_REGS can happen if the md file makes use of
+ EXTRA_CONSTRAINTS to match registers. Which is arguably
+ wrong, but there we are. Since we know not what this may
+ be replaced with, terminate the chain. */
+ if (cl != NO_REGS)
+ {
+ this_du = XOBNEW (&rename_obstack, struct du_chain);
+ this_du->next_use = 0;
+ this_du->next_chain = (*p)->next_chain;
+ this_du->loc = loc;
+ this_du->insn = insn;
+ this_du->cl = cl;
+ this_du->need_caller_save_reg = 0;
+ while (*p)
+ p = &(*p)->next_use;
+ *p = this_du;
+ return;
+ }
}
- }
- else
- *status = 0;
- return x;
+ if (action != terminate_overlapping_read || ! exact_match)
+ {
+ struct du_chain *next = this_du->next_chain;
- default:
- break;
- }
+ /* Whether the terminated chain can be used for renaming
+ depends on the action and this being an exact match.
+ In either case, we remove this element from open_chains. */
- fmt = GET_RTX_FORMAT (code);
- for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
- {
- if (fmt[i] == 'e')
- XEXP (x, i) = rr_replace_reg (XEXP (x, i), reg_use, reg_sub,
- replace_type, insn, status);
- if (fmt[i] == 'E')
- {
- register int xv;
- for (xv = 0; xv < XVECLEN (x, i); xv++)
- {
- XVECEXP (x, i, xv) = rr_replace_reg (XVECEXP (x, i, xv), reg_use,
- reg_sub, replace_type, insn,
- status);
- n = recog_memoized (insn);
- if (n >= 0)
+ if ((action == terminate_dead || action == terminate_write)
+ && exact_match)
{
- extract_insn (insn);
- if (!constrain_operands (1))
- {
- *status = 0;
- validate_replace_rtx (reg_sub, reg_use, insn);
- }
+ this_du->next_chain = closed_chains;
+ closed_chains = this_du;
+ if (dump_file)
+ fprintf (dump_file,
+ "Closing chain %s at insn %d (%s)\n",
+ reg_names[REGNO (*this_du->loc)], INSN_UID (insn),
+ scan_actions_name[(int) action]);
}
else
- *status = 0;
+ {
+ if (dump_file)
+ fprintf (dump_file,
+ "Discarding chain %s at insn %d (%s)\n",
+ reg_names[REGNO (*this_du->loc)], INSN_UID (insn),
+ scan_actions_name[(int) action]);
+ }
+ *p = next;
}
+ else
+ p = &this_du->next_chain;
}
}
- return x;
}
-/* Can REGNO in INSN be considered for renaming, given def INUM in d/u
- chain DU? */
+/* Adapted from find_reloads_address_1. CL is INDEX_REG_CLASS or
+ BASE_REG_CLASS depending on how the register is being considered. */
-static int
-consider_def (insn, regno, du, inum)
- rtx insn;
- int regno;
- def_uses *du;
- int inum;
+static void
+scan_rtx_address (rtx insn, rtx *loc, enum reg_class cl,
+ enum scan_actions action, enum machine_mode mode)
{
- /* Don't rename windowed registers across a call */
-#ifdef INCOMING_REGNO
- if (TEST_BIT (du->require_call_save_reg, inum)
- && INCOMING_REGNO (regno) != regno)
- return 0;
-#endif
+ rtx x = *loc;
+ RTX_CODE code = GET_CODE (x);
+ const char *fmt;
+ int i, j;
- /* Don't consider conditional moves. Predicate architectures may
- use two complementary conditional moves and the regno shouldn't change */
- if (condmove_p (insn))
- return 0;
-
- /* Don't rename call used registers across a call */
- if (!(GET_CODE (insn) == CALL_INSN
- && TEST_HARD_REG_BIT (call_used_reg_set, regno)))
- return 1;
- else
- return 0;
-}
+ if (action == mark_write || action == mark_access)
+ return;
-/* Can the use of REGNO in INSN of block USE_BLOCK be considered for renaming
- for a def in def_block? */
+ switch (code)
+ {
+ case PLUS:
+ {
+ rtx orig_op0 = XEXP (x, 0);
+ rtx orig_op1 = XEXP (x, 1);
+ RTX_CODE code0 = GET_CODE (orig_op0);
+ RTX_CODE code1 = GET_CODE (orig_op1);
+ rtx op0 = orig_op0;
+ rtx op1 = orig_op1;
+ rtx *locI = NULL;
+ rtx *locB = NULL;
+ enum rtx_code index_code = SCRATCH;
+
+ if (GET_CODE (op0) == SUBREG)
+ {
+ op0 = SUBREG_REG (op0);
+ code0 = GET_CODE (op0);
+ }
-static int
-consider_use (insn, regno, def_block, use_block)
- rtx insn;
- int regno;
- int def_block;
- int use_block;
-{
- rtx reg_use;
- edge e;
- basic_block ub = BASIC_BLOCK (use_block);
+ if (GET_CODE (op1) == SUBREG)
+ {
+ op1 = SUBREG_REG (op1);
+ code1 = GET_CODE (op1);
+ }
- if (GET_RTX_CLASS (GET_CODE (insn)) != 'i')
- return 0;
+ if (code0 == MULT || code0 == SIGN_EXTEND || code0 == TRUNCATE
+ || code0 == ZERO_EXTEND || code1 == MEM)
+ {
+ locI = &XEXP (x, 0);
+ locB = &XEXP (x, 1);
+ index_code = GET_CODE (*locI);
+ }
+ else if (code1 == MULT || code1 == SIGN_EXTEND || code1 == TRUNCATE
+ || code1 == ZERO_EXTEND || code0 == MEM)
+ {
+ locI = &XEXP (x, 1);
+ locB = &XEXP (x, 0);
+ index_code = GET_CODE (*locI);
+ }
+ else if (code0 == CONST_INT || code0 == CONST
+ || code0 == SYMBOL_REF || code0 == LABEL_REF)
+ {
+ locB = &XEXP (x, 1);
+ index_code = GET_CODE (XEXP (x, 0));
+ }
+ else if (code1 == CONST_INT || code1 == CONST
+ || code1 == SYMBOL_REF || code1 == LABEL_REF)
+ {
+ locB = &XEXP (x, 0);
+ index_code = GET_CODE (XEXP (x, 1));
+ }
+ else if (code0 == REG && code1 == REG)
+ {
+ int index_op;
+ unsigned regno0 = REGNO (op0), regno1 = REGNO (op1);
+
+ if (REGNO_OK_FOR_INDEX_P (regno1)
+ && regno_ok_for_base_p (regno0, mode, PLUS, REG))
+ index_op = 1;
+ else if (REGNO_OK_FOR_INDEX_P (regno0)
+ && regno_ok_for_base_p (regno1, mode, PLUS, REG))
+ index_op = 0;
+ else if (regno_ok_for_base_p (regno0, mode, PLUS, REG)
+ || REGNO_OK_FOR_INDEX_P (regno1))
+ index_op = 1;
+ else if (regno_ok_for_base_p (regno1, mode, PLUS, REG))
+ index_op = 0;
+ else
+ index_op = 1;
+
+ locI = &XEXP (x, index_op);
+ locB = &XEXP (x, !index_op);
+ index_code = GET_CODE (*locI);
+ }
+ else if (code0 == REG)
+ {
+ locI = &XEXP (x, 0);
+ locB = &XEXP (x, 1);
+ index_code = GET_CODE (*locI);
+ }
+ else if (code1 == REG)
+ {
+ locI = &XEXP (x, 1);
+ locB = &XEXP (x, 0);
+ index_code = GET_CODE (*locI);
+ }
- /* If a use's basic block is different than the def's basic block,
- then insure another predecessor does not also define this register */
- if (def_block != use_block)
- for (e = ub->pred; e; e = e->pred_next)
- {
- if (e->src->index != def_block
- && e->src->index != -1
- && REGNO_REG_SET_P (BASIC_BLOCK (e->src->index)->global_live_at_end, regno))
- return 0;
+ if (locI)
+ scan_rtx_address (insn, locI, INDEX_REG_CLASS, action, mode);
+ if (locB)
+ scan_rtx_address (insn, locB, base_reg_class (mode, PLUS, index_code),
+ action, mode);
+
+ return;
}
- /* Don't consider conditional moves. Predicate architectures may
- use two complementary conditional moves and the regno shouldn't change */
+ case POST_INC:
+ case POST_DEC:
+ case POST_MODIFY:
+ case PRE_INC:
+ case PRE_DEC:
+ case PRE_MODIFY:
+#ifndef AUTO_INC_DEC
+ /* If the target doesn't claim to handle autoinc, this must be
+ something special, like a stack push. Kill this chain. */
+ action = terminate_all_read;
+#endif
+ break;
- if (condmove_p (insn))
- return 0;
+ case MEM:
+ scan_rtx_address (insn, &XEXP (x, 0),
+ base_reg_class (GET_MODE (x), MEM, SCRATCH), action,
+ GET_MODE (x));
+ return;
+
+ case REG:
+ scan_rtx_reg (insn, loc, cl, action, OP_IN, 0);
+ return;
+
+ default:
+ break;
+ }
- reg_use = regno_first_use_in (regno, PATTERN (insn));
- if (reg_use)
+ fmt = GET_RTX_FORMAT (code);
+ for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
{
- /* Don't consider multi-reg values. */
- if (HARD_REGNO_NREGS (regno, GET_MODE (reg_use)) != 1
- && GET_MODE (reg_use) != CCmode)
- return 0;
-
- /* Don't consider register if the only use is in a USE */
- if (reg_mentioned_p (gen_rtx_USE (VOIDmode, reg_use),
- PATTERN (insn)))
- return 0;
- else
- return 1;
+ if (fmt[i] == 'e')
+ scan_rtx_address (insn, &XEXP (x, i), cl, action, mode);
+ else if (fmt[i] == 'E')
+ for (j = XVECLEN (x, i) - 1; j >= 0; j--)
+ scan_rtx_address (insn, &XVECEXP (x, i, j), cl, action, mode);
}
- else
- return 0;
}
-/* Can REG_USE be replaced by regno AVAIL_REG if it is in AVAIL_REGS
- and it is in regclass RC, given insn INUM of def/use chain DU? */
-
-static int
-consider_available (reg_use, avail_reg, avail_regs, rc, du, inum)
- rtx reg_use;
- int avail_reg;
- HARD_REG_SET *avail_regs;
- int rc;
- def_uses *du;
- int inum;
+static void
+scan_rtx (rtx insn, rtx *loc, enum reg_class cl,
+ enum scan_actions action, enum op_type type, int earlyclobber)
{
- if (!TEST_HARD_REG_BIT (*avail_regs, avail_reg))
- return 0;
-
- if (fixed_regs[avail_reg])
- return 0;
-
-#ifdef HARD_REGNO_RENAME_OK
- if (!HARD_REGNO_RENAME_OK (REGNO (reg_use), avail_reg))
- return 0;
-#endif
-
- /* Don't consider windowed leaf registers which will be renamed by
- leaf_renumber_regs */
-#ifdef LEAF_REG_REMAP
- if (current_function_uses_only_leaf_regs)
- if (LEAF_REG_REMAP (avail_reg) < 0)
- return 0;
-#endif
-
- /* A register is considered available if it is available at the beginning of
- the basic block. We may want to refine this to when a register becomes
- available within the block. We don't consider multi-reg values. */
- /* ??? Consider a representation that would allow multi-reg support? */
- if (!TEST_HARD_REG_BIT (reg_class_contents[(enum reg_class) rc], avail_reg)
- || !HARD_REGNO_MODE_OK (avail_reg, GET_MODE (reg_use))
- || (HARD_REGNO_NREGS (avail_reg, GET_MODE (reg_use)) != 1
- && GET_MODE (reg_use) != CCmode)
- || (call_fixed_regs[avail_reg]
-#ifdef HARD_REGNO_RENAME_OK
- && !HARD_REGNO_RENAME_OK (REGNO (reg_use), avail_reg)
-#endif
- )
- || (TEST_BIT (du->require_call_save_reg, inum)
- && (call_used_regs[avail_reg] || call_used_regs[REGNO (reg_use)]
- )))
- return 0;
-
- /* If register is a callee-saved register it must be saved in the frame.
- call saved registers can not be added to regs_ever_live after reload,
- as it would invalidate most elimination offsets */
- if (regs_ever_live[avail_reg] || call_used_regs[avail_reg])
- return 1;
-
- return 0;
-}
+ const char *fmt;
+ rtx x = *loc;
+ enum rtx_code code = GET_CODE (x);
+ int i, j;
-/* Return 1 if INSN is a conditional move */
+ code = GET_CODE (x);
+ switch (code)
+ {
+ case CONST:
+ case CONST_INT:
+ case CONST_DOUBLE:
+ case CONST_FIXED:
+ case CONST_VECTOR:
+ case SYMBOL_REF:
+ case LABEL_REF:
+ case CC0:
+ case PC:
+ return;
-static int
-condmove_p (insn)
- rtx insn;
-{
- if (GET_CODE (insn) == INSN
- && GET_CODE (PATTERN (insn)) == SET
- && GET_CODE (SET_SRC (PATTERN (insn))) == IF_THEN_ELSE)
- return 1;
- return 0;
-}
+ case REG:
+ scan_rtx_reg (insn, loc, cl, action, type, earlyclobber);
+ return;
-/* Searches X for the first reference to REGNO, returning the rtx of the
- reference found if any. Otherwise, returns NULL_RTX. */
+ case MEM:
+ scan_rtx_address (insn, &XEXP (x, 0),
+ base_reg_class (GET_MODE (x), MEM, SCRATCH), action,
+ GET_MODE (x));
+ return;
-static rtx
-regno_first_use_in (regno, x)
- int regno;
- rtx x;
-{
- register const char *fmt;
- int i, j;
- rtx tem;
+ case SET:
+ scan_rtx (insn, &SET_SRC (x), cl, action, OP_IN, 0);
+ scan_rtx (insn, &SET_DEST (x), cl, action,
+ GET_CODE (PATTERN (insn)) == COND_EXEC ? OP_INOUT : OP_OUT, 0);
+ return;
+
+ case STRICT_LOW_PART:
+ scan_rtx (insn, &XEXP (x, 0), cl, action, OP_INOUT, earlyclobber);
+ return;
+
+ case ZERO_EXTRACT:
+ case SIGN_EXTRACT:
+ scan_rtx (insn, &XEXP (x, 0), cl, action,
+ type == OP_IN ? OP_IN : OP_INOUT, earlyclobber);
+ scan_rtx (insn, &XEXP (x, 1), cl, action, OP_IN, 0);
+ scan_rtx (insn, &XEXP (x, 2), cl, action, OP_IN, 0);
+ return;
+
+ case POST_INC:
+ case PRE_INC:
+ case POST_DEC:
+ case PRE_DEC:
+ case POST_MODIFY:
+ case PRE_MODIFY:
+ /* Should only happen inside MEM. */
+ gcc_unreachable ();
+
+ case CLOBBER:
+ scan_rtx (insn, &SET_DEST (x), cl, action,
+ GET_CODE (PATTERN (insn)) == COND_EXEC ? OP_INOUT : OP_OUT, 0);
+ return;
+
+ case EXPR_LIST:
+ scan_rtx (insn, &XEXP (x, 0), cl, action, type, 0);
+ if (XEXP (x, 1))
+ scan_rtx (insn, &XEXP (x, 1), cl, action, type, 0);
+ return;
- if (GET_CODE (x) == REG && REGNO (x) == regno)
- return x;
+ default:
+ break;
+ }
- fmt = GET_RTX_FORMAT (GET_CODE (x));
- for (i = 0; i <= GET_RTX_LENGTH (GET_CODE (x)) - 1; i++)
+ fmt = GET_RTX_FORMAT (code);
+ for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
{
if (fmt[i] == 'e')
- {
- if ((tem = regno_first_use_in (regno, XEXP (x, i))))
- return tem;
- }
+ scan_rtx (insn, &XEXP (x, i), cl, action, type, 0);
else if (fmt[i] == 'E')
for (j = XVECLEN (x, i) - 1; j >= 0; j--)
- if ((tem = regno_first_use_in (regno, XVECEXP (x, i, j))))
- return tem;
+ scan_rtx (insn, &XVECEXP (x, i, j), cl, action, type, 0);
}
-
- return NULL_RTX;
}
-/* Dump def/use chain DU to RTL_DUMP_FILE, given insns in UID_RUID and
- which regs are live at end, GLOBAL_LIVE_AT_END */
+/* Build def/use chain. */
-static void
-dump_def_use_chain (global_live_at_end, du, uid_ruid)
- HARD_REG_SET *global_live_at_end;
- def_uses *du;
- varray_type *uid_ruid;
+static struct du_chain *
+build_def_use (basic_block bb)
{
- int r, inum;
+ rtx insn;
+
+ open_chains = closed_chains = NULL;
- for (r = 0; r < FIRST_PSEUDO_REGISTER; r++)
+ for (insn = BB_HEAD (bb); ; insn = NEXT_INSN (insn))
{
- int set = 0;
- for (inum = 0;
- inum <= du->high_bound;
- inum++)
+ if (INSN_P (insn))
{
- rtx insn = VARRAY_RTX (*uid_ruid, inum);
-#if 0
- if (!insn
- || GET_RTX_CLASS (GET_CODE
- (insn)) != 'i')
- continue;
- reg_use = regno_first_use_in (r, PATTERN (insn));
- if (!reg_use)
- continue;
-#endif
- if (!set && (TEST_BIT (du->defs[r], inum)
- || TEST_BIT (du->uses[r], inum)))
+ int n_ops;
+ rtx note;
+ rtx old_operands[MAX_RECOG_OPERANDS];
+ rtx old_dups[MAX_DUP_OPERANDS];
+ int i, icode;
+ int alt;
+ int predicated;
+
+ /* Process the insn, determining its effect on the def-use
+ chains. We perform the following steps with the register
+ references in the insn:
+ (1) Any read that overlaps an open chain, but doesn't exactly
+ match, causes that chain to be closed. We can't deal
+ with overlaps yet.
+ (2) Any read outside an operand causes any chain it overlaps
+ with to be closed, since we can't replace it.
+ (3) Any read inside an operand is added if there's already
+ an open chain for it.
+ (4) For any REG_DEAD note we find, close open chains that
+ overlap it.
+ (5) For any write we find, close open chains that overlap it.
+ (6) For any write we find in an operand, make a new chain.
+ (7) For any REG_UNUSED, close any chains we just opened. */
+
+ icode = recog_memoized (insn);
+ extract_insn (insn);
+ if (! constrain_operands (1))
+ fatal_insn_not_found (insn);
+ preprocess_constraints ();
+ alt = which_alternative;
+ n_ops = recog_data.n_operands;
+
+ /* Simplify the code below by rewriting things to reflect
+ matching constraints. Also promote OP_OUT to OP_INOUT
+ in predicated instructions. */
+
+ predicated = GET_CODE (PATTERN (insn)) == COND_EXEC;
+ for (i = 0; i < n_ops; ++i)
{
- fprintf (rtl_dump_file, "Register %s: ", reg_names[r]);
- if (fixed_regs[r])
- fprintf (rtl_dump_file, "Fixed ");
- else if (call_fixed_regs[r])
- fprintf (rtl_dump_file, "Call Fixed ");
- if (TEST_HARD_REG_BIT (*global_live_at_end, r))
- fprintf (rtl_dump_file, "Live at Exit ");
- set = 1;
+ int matches = recog_op_alt[i][alt].matches;
+ if (matches >= 0)
+ recog_op_alt[i][alt].cl = recog_op_alt[matches][alt].cl;
+ if (matches >= 0 || recog_op_alt[i][alt].matched >= 0
+ || (predicated && recog_data.operand_type[i] == OP_OUT))
+ recog_data.operand_type[i] = OP_INOUT;
}
- if (TEST_BIT (du->defs[r], inum))
- fprintf (rtl_dump_file, "=%d ", INSN_UID (insn));
- if (TEST_BIT (du->uses[r], inum))
- fprintf (rtl_dump_file, "%d ", INSN_UID (insn));
- }
- if (set)
- fprintf (rtl_dump_file, "\n");
- }
-}
-/* Dump info for extended basic block EBB having root EB */
+ /* Step 1: Close chains for which we have overlapping reads. */
+ for (i = 0; i < n_ops; i++)
+ scan_rtx (insn, recog_data.operand_loc[i],
+ NO_REGS, terminate_overlapping_read,
+ recog_data.operand_type[i], 0);
-static void
-dump_ext_bb_info (eb, ebb)
- int eb;
- ext_basic_blocks *ebb;
-{
- int b;
+ /* Step 2: Close chains for which we have reads outside operands.
+ We do this by munging all operands into CC0, and closing
+ everything remaining. */
- {
- int have_ebb = 0;
- for (b = 0; b < n_basic_blocks; b++)
- {
- if (TEST_BIT (ebb->basic_block[eb], b))
- {
- if (!have_ebb)
+ for (i = 0; i < n_ops; i++)
+ {
+ old_operands[i] = recog_data.operand[i];
+ /* Don't squash match_operator or match_parallel here, since
+ we don't know that all of the contained registers are
+ reachable by proper operands. */
+ if (recog_data.constraints[i][0] == '\0')
+ continue;
+ *recog_data.operand_loc[i] = cc0_rtx;
+ }
+ for (i = 0; i < recog_data.n_dups; i++)
+ {
+ old_dups[i] = *recog_data.dup_loc[i];
+ *recog_data.dup_loc[i] = cc0_rtx;
+ }
+
+ scan_rtx (insn, &PATTERN (insn), NO_REGS, terminate_all_read,
+ OP_IN, 0);
+
+ for (i = 0; i < recog_data.n_dups; i++)
+ *recog_data.dup_loc[i] = old_dups[i];
+ for (i = 0; i < n_ops; i++)
+ *recog_data.operand_loc[i] = old_operands[i];
+ if (recog_data.n_dups)
+ df_insn_rescan (insn);
+
+ /* Step 2B: Can't rename function call argument registers. */
+ if (CALL_P (insn) && CALL_INSN_FUNCTION_USAGE (insn))
+ scan_rtx (insn, &CALL_INSN_FUNCTION_USAGE (insn),
+ NO_REGS, terminate_all_read, OP_IN, 0);
+
+ /* Step 2C: Can't rename asm operands that were originally
+ hard registers. */
+ if (asm_noperands (PATTERN (insn)) > 0)
+ for (i = 0; i < n_ops; i++)
{
-#ifndef RENAME_EXTENDED_BLOCKS
- fprintf (rtl_dump_file, "\nBasic block %d: ", b);
-#else
- fprintf (rtl_dump_file, "\nExtended basic block %d: ", b);
-#endif
- have_ebb = 1;
+ rtx *loc = recog_data.operand_loc[i];
+ rtx op = *loc;
+
+ if (REG_P (op)
+ && REGNO (op) == ORIGINAL_REGNO (op)
+ && (recog_data.operand_type[i] == OP_IN
+ || recog_data.operand_type[i] == OP_INOUT))
+ scan_rtx (insn, loc, NO_REGS, terminate_all_read, OP_IN, 0);
}
- fprintf (rtl_dump_file, "%d ", b);
- }
- if (TEST_BIT (ebb->exit[eb], b))
- fprintf (rtl_dump_file, "(exit) ");
- }
- if (have_ebb)
- fprintf (rtl_dump_file, "\n");
- }
-}
-/* Initialize EBB with extended basic block info if RENAME_EXTENDED_BLOCKS is
- defined. Otherwise just use basic blocks */
+ /* Step 3: Append to chains for reads inside operands. */
+ for (i = 0; i < n_ops + recog_data.n_dups; i++)
+ {
+ int opn = i < n_ops ? i : recog_data.dup_num[i - n_ops];
+ rtx *loc = (i < n_ops
+ ? recog_data.operand_loc[opn]
+ : recog_data.dup_loc[i - n_ops]);
+ enum reg_class cl = recog_op_alt[opn][alt].cl;
+ enum op_type type = recog_data.operand_type[opn];
+
+ /* Don't scan match_operand here, since we've no reg class
+ information to pass down. Any operands that we could
+ substitute in will be represented elsewhere. */
+ if (recog_data.constraints[opn][0] == '\0')
+ continue;
-static void
-find_ext_basic_blocks (ebb)
- ext_basic_blocks *ebb;
-{
- sbitmap bb_processed;
- int b;
+ if (recog_op_alt[opn][alt].is_address)
+ scan_rtx_address (insn, loc, cl, mark_read, VOIDmode);
+ else
+ scan_rtx (insn, loc, cl, mark_read, type, 0);
+ }
+
+ /* Step 3B: Record updates for regs in REG_INC notes, and
+ source regs in REG_FRAME_RELATED_EXPR notes. */
+ for (note = REG_NOTES (insn); note; note = XEXP (note, 1))
+ if (REG_NOTE_KIND (note) == REG_INC
+ || REG_NOTE_KIND (note) == REG_FRAME_RELATED_EXPR)
+ scan_rtx (insn, &XEXP (note, 0), ALL_REGS, mark_read,
+ OP_INOUT, 0);
+
+ /* Step 4: Close chains for registers that die here. */
+ for (note = REG_NOTES (insn); note; note = XEXP (note, 1))
+ if (REG_NOTE_KIND (note) == REG_DEAD)
+ scan_rtx (insn, &XEXP (note, 0), NO_REGS, terminate_dead,
+ OP_IN, 0);
+
+ /* Step 4B: If this is a call, any chain live at this point
+ requires a caller-saved reg. */
+ if (CALL_P (insn))
+ {
+ struct du_chain *p;
+ for (p = open_chains; p; p = p->next_chain)
+ p->need_caller_save_reg = 1;
+ }
- bb_processed = sbitmap_alloc (n_basic_blocks);
- sbitmap_zero (bb_processed);
+ /* Step 5: Close open chains that overlap writes. Similar to
+ step 2, we hide in-out operands, since we do not want to
+ close these chains. */
-#ifndef RENAME_EXTENDED_BLOCKS
- for (b = 0; b < n_basic_blocks; b++)
- {
- basic_block bb = BASIC_BLOCK (b);
- SET_BIT (ebb->basic_block[bb->index], bb->index);
- }
-#else
- for (b = 0; b < n_basic_blocks; b++)
- {
- basic_block bb = BASIC_BLOCK (b);
- if (!TEST_BIT (bb_processed, b))
- {
- find_one_ext_basic_block (bb->index, bb, &bb_processed, ebb);
+ for (i = 0; i < n_ops; i++)
+ {
+ old_operands[i] = recog_data.operand[i];
+ if (recog_data.operand_type[i] == OP_INOUT)
+ *recog_data.operand_loc[i] = cc0_rtx;
+ }
+ for (i = 0; i < recog_data.n_dups; i++)
+ {
+ int opn = recog_data.dup_num[i];
+ old_dups[i] = *recog_data.dup_loc[i];
+ if (recog_data.operand_type[opn] == OP_INOUT)
+ *recog_data.dup_loc[i] = cc0_rtx;
+ }
+
+ scan_rtx (insn, &PATTERN (insn), NO_REGS, terminate_write, OP_IN, 0);
+
+ for (i = 0; i < recog_data.n_dups; i++)
+ *recog_data.dup_loc[i] = old_dups[i];
+ for (i = 0; i < n_ops; i++)
+ *recog_data.operand_loc[i] = old_operands[i];
+
+ /* Step 6: Begin new chains for writes inside operands. */
+ /* ??? Many targets have output constraints on the SET_DEST
+ of a call insn, which is stupid, since these are certainly
+ ABI defined hard registers. Don't change calls at all.
+ Similarly take special care for asm statement that originally
+ referenced hard registers. */
+ if (asm_noperands (PATTERN (insn)) > 0)
+ {
+ for (i = 0; i < n_ops; i++)
+ if (recog_data.operand_type[i] == OP_OUT)
+ {
+ rtx *loc = recog_data.operand_loc[i];
+ rtx op = *loc;
+ enum reg_class cl = recog_op_alt[i][alt].cl;
+
+ if (REG_P (op)
+ && REGNO (op) == ORIGINAL_REGNO (op))
+ continue;
+
+ scan_rtx (insn, loc, cl, mark_write, OP_OUT,
+ recog_op_alt[i][alt].earlyclobber);
+ }
+ }
+ else if (!CALL_P (insn))
+ for (i = 0; i < n_ops + recog_data.n_dups; i++)
+ {
+ int opn = i < n_ops ? i : recog_data.dup_num[i - n_ops];
+ rtx *loc = (i < n_ops
+ ? recog_data.operand_loc[opn]
+ : recog_data.dup_loc[i - n_ops]);
+ enum reg_class cl = recog_op_alt[opn][alt].cl;
+
+ if (recog_data.operand_type[opn] == OP_OUT)
+ scan_rtx (insn, loc, cl, mark_write, OP_OUT,
+ recog_op_alt[opn][alt].earlyclobber);
+ }
+
+ /* Step 6B: Record destination regs in REG_FRAME_RELATED_EXPR
+ notes for update. */
+ for (note = REG_NOTES (insn); note; note = XEXP (note, 1))
+ if (REG_NOTE_KIND (note) == REG_FRAME_RELATED_EXPR)
+ scan_rtx (insn, &XEXP (note, 0), ALL_REGS, mark_access,
+ OP_INOUT, 0);
+
+ /* Step 7: Close chains for registers that were never
+ really used here. */
+ for (note = REG_NOTES (insn); note; note = XEXP (note, 1))
+ if (REG_NOTE_KIND (note) == REG_UNUSED)
+ scan_rtx (insn, &XEXP (note, 0), NO_REGS, terminate_dead,
+ OP_IN, 0);
}
+ if (insn == BB_END (bb))
+ break;
}
-#endif
- sbitmap_free (bb_processed);
+
+ /* Since we close every chain when we find a REG_DEAD note, anything that
+ is still open lives past the basic block, so it can't be renamed. */
+ return closed_chains;
}
-/* Find one extended basic block EBB having root ENTRY containing block
- BB */
+/* Dump all def/use chains in CHAINS to DUMP_FILE. They are
+ printed in reverse order as that's how we build them. */
static void
-find_one_ext_basic_block (entry, bb, bb_processed, ebb)
- int entry;
- basic_block bb;
- sbitmap *bb_processed;
- ext_basic_blocks *ebb;
+dump_def_use_chain (struct du_chain *chains)
{
- edge e;
-
- if (!TEST_BIT (*bb_processed, bb->index))
+ while (chains)
{
- SET_BIT (ebb->basic_block[entry], bb->index);
- SET_BIT (*bb_processed, bb->index);
+ struct du_chain *this_du = chains;
+ int r = REGNO (*this_du->loc);
+ int nregs = hard_regno_nregs[r][GET_MODE (*this_du->loc)];
+ fprintf (dump_file, "Register %s (%d):", reg_names[r], nregs);
+ while (this_du)
+ {
+ fprintf (dump_file, " %d [%s]", INSN_UID (this_du->insn),
+ reg_class_names[this_du->cl]);
+ this_du = this_du->next_use;
+ }
+ fprintf (dump_file, "\n");
+ chains = chains->next_chain;
}
-
- for (e = bb->succ; e; e = e->succ_next)
- if (!TEST_BIT (*bb_processed, e->dest->index))
- {
- if (!e->dest->pred->pred_next
- && (!TEST_BIT (*bb_processed, e->dest->index)))
- {
- find_one_ext_basic_block (entry, e->dest, bb_processed, ebb);
- }
- else
- {
- SET_BIT (ebb->exit[entry], bb->index);
- }
- }
}
-/* Find the register class for register REG_USE having TYPE (DESTINATION or
- SOURCE) in INSN. Use DEFAULT_CLASS if we cannot determine a class. */
-
-static enum reg_class
-get_reg_class (insn, reg_use, type, default_class)
- rtx insn;
- rtx reg_use;
- int type;
- enum reg_class default_class;
+\f
+static bool
+gate_handle_regrename (void)
{
- int alt, id = 0;
-
- extract_insn (insn);
- constrain_operands (1);
- alt = which_alternative;
-
- preprocess_constraints ();
-
- if (type == DESTINATION)
- for (id = 0; id < recog_data.n_operands; id++)
- {
- if (rtx_equal_p (recog_data.operand[id], reg_use))
- break;
- }
- else if (type == SOURCE)
- for (id = recog_data.n_operands - 1; id >= 0; id--)
- {
- if (rtx_equal_p (recog_data.operand[id], reg_use))
- break;
- }
+ return (optimize > 0 && (flag_rename_registers));
+}
- if (id == -1 || id == recog_data.n_operands)
- return default_class;
+struct rtl_opt_pass pass_regrename =
+{
+ {
+ RTL_PASS,
+ "rnreg", /* name */
+ gate_handle_regrename, /* gate */
+ regrename_optimize, /* execute */
+ NULL, /* sub */
+ NULL, /* next */
+ 0, /* static_pass_number */
+ TV_RENAME_REGISTERS, /* tv_id */
+ 0, /* properties_required */
+ 0, /* properties_provided */
+ 0, /* properties_destroyed */
+ 0, /* todo_flags_start */
+ TODO_df_finish | TODO_verify_rtl_sharing |
+ TODO_dump_func /* todo_flags_finish */
+ }
+};
- return recog_op_alt[id][alt].class;
-}