/* Compute different info about registers.
- Copyright (C) 1987, 1988, 1991, 1992, 1993, 1994, 1995, 1996
- 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008,
- 2009, 2010, 2011 Free Software Foundation, Inc.
+ Copyright (C) 1987-2015 Free Software Foundation, Inc.
This file is part of GCC.
#include "coretypes.h"
#include "tm.h"
#include "hard-reg-set.h"
+#include "alias.h"
+#include "symtab.h"
+#include "tree.h"
#include "rtl.h"
+#include "function.h"
+#include "flags.h"
+#include "insn-config.h"
+#include "expmed.h"
+#include "dojump.h"
+#include "explow.h"
+#include "calls.h"
+#include "emit-rtl.h"
+#include "varasm.h"
+#include "stmt.h"
#include "expr.h"
#include "tm_p.h"
-#include "flags.h"
+#include "predict.h"
+#include "dominance.h"
+#include "cfg.h"
#include "basic-block.h"
#include "regs.h"
#include "addresses.h"
-#include "function.h"
-#include "insn-config.h"
#include "recog.h"
#include "reload.h"
#include "diagnostic-core.h"
#include "output.h"
-#include "timevar.h"
-#include "hashtab.h"
#include "target.h"
#include "tree-pass.h"
#include "df.h"
int max_regno;
+/* Used to cache the results of simplifiable_subregs. SHAPE is the input
+ parameter and SIMPLIFIABLE_REGS is the result. */
+struct simplifiable_subreg
+{
+ simplifiable_subreg (const subreg_shape &);
+
+ subreg_shape shape;
+ HARD_REG_SET simplifiable_regs;
+};
\f
struct target_hard_regs default_target_hard_regs;
struct target_regs default_target_regs;
char global_regs[FIRST_PSEUDO_REGISTER];
/* Declaration for the global register. */
-static tree GTY(()) global_regs_decl[FIRST_PSEUDO_REGISTER];
+tree global_regs_decl[FIRST_PSEUDO_REGISTER];
/* Same information as REGS_INVALIDATED_BY_CALL but in regset form to be used
in dataflow more conveniently. */
init_reg_sets_1 (void)
{
unsigned int i, j;
- unsigned int /* enum machine_mode */ m;
+ unsigned int /* machine_mode */ m;
restore_register_info ();
}
else if (i == FRAME_POINTER_REGNUM)
;
-#if !HARD_FRAME_POINTER_IS_FRAME_POINTER
- else if (i == HARD_FRAME_POINTER_REGNUM)
+ else if (!HARD_FRAME_POINTER_IS_FRAME_POINTER
+ && i == HARD_FRAME_POINTER_REGNUM)
;
-#endif
-#if ARG_POINTER_REGNUM != FRAME_POINTER_REGNUM
- else if (i == ARG_POINTER_REGNUM && fixed_regs[i])
+ else if (FRAME_POINTER_REGNUM != ARG_POINTER_REGNUM
+ && i == ARG_POINTER_REGNUM && fixed_regs[i])
;
-#endif
else if (!PIC_OFFSET_TABLE_REG_CALL_CLOBBERED
&& i == (unsigned) PIC_OFFSET_TABLE_REGNUM && fixed_regs[i])
;
}
}
- COPY_HARD_REG_SET(call_fixed_reg_set, fixed_reg_set);
+ COPY_HARD_REG_SET (call_fixed_reg_set, fixed_reg_set);
/* Preserve global registers if called more than once. */
for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
HARD_REG_SET ok_regs;
CLEAR_HARD_REG_SET (ok_regs);
for (j = 0; j < FIRST_PSEUDO_REGISTER; j++)
- if (!fixed_regs [j] && HARD_REGNO_MODE_OK (j, (enum machine_mode) m))
+ if (!fixed_regs [j] && HARD_REGNO_MODE_OK (j, (machine_mode) m))
SET_HARD_REG_BIT (ok_regs, j);
for (i = 0; i < N_REG_CLASSES; i++)
- if ((targetm.class_max_nregs ((reg_class_t) i, (enum machine_mode) m)
+ if ((targetm.class_max_nregs ((reg_class_t) i, (machine_mode) m)
<= reg_class_size[i])
&& hard_reg_set_intersect_p (ok_regs, reg_class_contents[i]))
{
for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
for (j = 0; j < MAX_MACHINE_MODE; j++)
- hard_regno_nregs[i][j] = HARD_REGNO_NREGS(i, (enum machine_mode)j);
+ hard_regno_nregs[i][j] = HARD_REGNO_NREGS (i, (machine_mode)j);
for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
{
init_regs ();
/* caller_save needs to be re-initialized. */
caller_save_initialized_p = false;
- ira_init ();
+ if (this_target_rtl->target_specific_initialized)
+ {
+ ira_init ();
+ recog_init ();
+ }
}
/* Initialize some fake stack-frame MEM references for use in
int i;
for (i = 0; i < MAX_MACHINE_MODE; i++)
- top_of_stack[i] = gen_rtx_MEM ((enum machine_mode) i, stack_pointer_rtx);
+ top_of_stack[i] = gen_rtx_MEM ((machine_mode) i, stack_pointer_rtx);
}
TO, using MODE. */
int
-register_move_cost (enum machine_mode mode, reg_class_t from, reg_class_t to)
+register_move_cost (machine_mode mode, reg_class_t from, reg_class_t to)
{
return targetm.register_move_cost (mode, from, to);
}
/* Compute cost of moving registers to/from memory. */
int
-memory_move_cost (enum machine_mode mode, reg_class_t rclass, bool in)
+memory_move_cost (machine_mode mode, reg_class_t rclass, bool in)
{
return targetm.memory_move_cost (mode, rclass, in);
}
/* Compute extra cost of moving registers to/from memory due to reloads.
Only needed if secondary reloads are required for memory moves. */
int
-memory_move_secondary_cost (enum machine_mode mode, reg_class_t rclass,
+memory_move_secondary_cost (machine_mode mode, reg_class_t rclass,
bool in)
{
reg_class_t altclass;
/* Return a machine mode that is legitimate for hard reg REGNO and large
enough to save nregs. If we can't find one, return VOIDmode.
If CALL_SAVED is true, only consider modes that are call saved. */
-enum machine_mode
+machine_mode
choose_hard_reg_mode (unsigned int regno ATTRIBUTE_UNUSED,
unsigned int nregs, bool call_saved)
{
- unsigned int /* enum machine_mode */ m;
- enum machine_mode found_mode = VOIDmode, mode;
+ unsigned int /* machine_mode */ m;
+ machine_mode found_mode = VOIDmode, mode;
/* We first look for the largest integer mode that can be validly
held in REGNO. If none, we look for the largest floating-point mode.
mode = GET_MODE_WIDER_MODE (mode))
if ((unsigned) hard_regno_nregs[regno][mode] == nregs
&& HARD_REGNO_MODE_OK (regno, mode)
- && (! call_saved || ! HARD_REGNO_CALL_PART_CLOBBERED (regno, mode)))
+ && (! call_saved || ! HARD_REGNO_CALL_PART_CLOBBERED (regno, mode))
+ && GET_MODE_SIZE (mode) > GET_MODE_SIZE (found_mode))
found_mode = mode;
- if (found_mode != VOIDmode)
- return found_mode;
-
for (mode = GET_CLASS_NARROWEST_MODE (MODE_FLOAT);
mode != VOIDmode;
mode = GET_MODE_WIDER_MODE (mode))
if ((unsigned) hard_regno_nregs[regno][mode] == nregs
&& HARD_REGNO_MODE_OK (regno, mode)
- && (! call_saved || ! HARD_REGNO_CALL_PART_CLOBBERED (regno, mode)))
+ && (! call_saved || ! HARD_REGNO_CALL_PART_CLOBBERED (regno, mode))
+ && GET_MODE_SIZE (mode) > GET_MODE_SIZE (found_mode))
found_mode = mode;
- if (found_mode != VOIDmode)
- return found_mode;
-
for (mode = GET_CLASS_NARROWEST_MODE (MODE_VECTOR_FLOAT);
mode != VOIDmode;
mode = GET_MODE_WIDER_MODE (mode))
if ((unsigned) hard_regno_nregs[regno][mode] == nregs
&& HARD_REGNO_MODE_OK (regno, mode)
- && (! call_saved || ! HARD_REGNO_CALL_PART_CLOBBERED (regno, mode)))
+ && (! call_saved || ! HARD_REGNO_CALL_PART_CLOBBERED (regno, mode))
+ && GET_MODE_SIZE (mode) > GET_MODE_SIZE (found_mode))
found_mode = mode;
- if (found_mode != VOIDmode)
- return found_mode;
-
for (mode = GET_CLASS_NARROWEST_MODE (MODE_VECTOR_INT);
mode != VOIDmode;
mode = GET_MODE_WIDER_MODE (mode))
if ((unsigned) hard_regno_nregs[regno][mode] == nregs
&& HARD_REGNO_MODE_OK (regno, mode)
- && (! call_saved || ! HARD_REGNO_CALL_PART_CLOBBERED (regno, mode)))
+ && (! call_saved || ! HARD_REGNO_CALL_PART_CLOBBERED (regno, mode))
+ && GET_MODE_SIZE (mode) > GET_MODE_SIZE (found_mode))
found_mode = mode;
if (found_mode != VOIDmode)
/* Iterate over all of the CCmodes. */
for (m = (unsigned int) CCmode; m < (unsigned int) NUM_MACHINE_MODES; ++m)
{
- mode = (enum machine_mode) m;
+ mode = (machine_mode) m;
if ((unsigned) hard_regno_nregs[regno][mode] == nregs
&& HARD_REGNO_MODE_OK (regno, mode)
&& (! call_saved || ! HARD_REGNO_CALL_PART_CLOBBERED (regno, mode)))
/* Current size of reg_info. */
static int reg_info_size;
+/* Max_reg_num still last resize_reg_info call. */
+static int max_regno_since_last_resize;
/* Return the reg_class in which pseudo reg number REGNO is best allocated.
This function is sometimes called before the info has been computed.
if (reg_pref == 0)
return GENERAL_REGS;
+ gcc_assert (regno < reg_info_size);
return (enum reg_class) reg_pref[regno].prefclass;
}
if (reg_pref == 0)
return ALL_REGS;
+ gcc_assert (regno < reg_info_size);
return (enum reg_class) reg_pref[regno].altclass;
}
if (reg_pref == 0)
return NO_REGS;
+ gcc_assert (regno < reg_info_size);
return (enum reg_class) reg_pref[regno].allocnoclass;
}
\f
-/* Allocate space for reg info. */
+/* Allocate space for reg info and initilize it. */
static void
allocate_reg_info (void)
{
- reg_info_size = max_reg_num ();
+ int i;
+
+ max_regno_since_last_resize = max_reg_num ();
+ reg_info_size = max_regno_since_last_resize * 3 / 2 + 1;
gcc_assert (! reg_pref && ! reg_renumber);
reg_renumber = XNEWVEC (short, reg_info_size);
reg_pref = XCNEWVEC (struct reg_pref, reg_info_size);
memset (reg_renumber, -1, reg_info_size * sizeof (short));
+ for (i = 0; i < reg_info_size; i++)
+ {
+ reg_pref[i].prefclass = GENERAL_REGS;
+ reg_pref[i].altclass = ALL_REGS;
+ reg_pref[i].allocnoclass = GENERAL_REGS;
+ }
}
-/* Resize reg info. The new elements will be uninitialized. Return
- TRUE if new elements (for new pseudos) were added. */
+/* Resize reg info. The new elements will be initialized. Return TRUE
+ if new pseudos were added since the last call. */
bool
resize_reg_info (void)
{
- int old;
+ int old, i;
+ bool change_p;
if (reg_pref == NULL)
{
allocate_reg_info ();
return true;
}
- if (reg_info_size == max_reg_num ())
- return false;
+ change_p = max_regno_since_last_resize != max_reg_num ();
+ max_regno_since_last_resize = max_reg_num ();
+ if (reg_info_size >= max_reg_num ())
+ return change_p;
old = reg_info_size;
- reg_info_size = max_reg_num ();
+ reg_info_size = max_reg_num () * 3 / 2 + 1;
gcc_assert (reg_pref && reg_renumber);
reg_renumber = XRESIZEVEC (short, reg_renumber, reg_info_size);
reg_pref = XRESIZEVEC (struct reg_pref, reg_pref, reg_info_size);
memset (reg_pref + old, -1,
(reg_info_size - old) * sizeof (struct reg_pref));
memset (reg_renumber + old, -1, (reg_info_size - old) * sizeof (short));
+ for (i = old; i < reg_info_size; i++)
+ {
+ reg_pref[i].prefclass = GENERAL_REGS;
+ reg_pref[i].altclass = ALL_REGS;
+ reg_pref[i].allocnoclass = GENERAL_REGS;
+ }
return true;
}
if (df)
df_compute_regs_ever_live (true);
- /* This prevents dump_flow_info from losing if called
+ /* This prevents dump_reg_info from losing if called
before reginfo is run. */
reg_pref = NULL;
+ reg_info_size = max_regno_since_last_resize = 0;
/* No more global register variables may be declared. */
no_global_reg_vars = 1;
return 1;
}
-struct rtl_opt_pass pass_reginfo_init =
+namespace {
+
+const pass_data pass_data_reginfo_init =
{
- {
- RTL_PASS,
- "reginfo", /* name */
- NULL, /* gate */
- reginfo_init, /* execute */
- NULL, /* sub */
- NULL, /* next */
- 0, /* static_pass_number */
- TV_NONE, /* tv_id */
- 0, /* properties_required */
- 0, /* properties_provided */
- 0, /* properties_destroyed */
- 0, /* todo_flags_start */
- 0 /* todo_flags_finish */
- }
+ RTL_PASS, /* type */
+ "reginfo", /* name */
+ OPTGROUP_NONE, /* optinfo_flags */
+ TV_NONE, /* tv_id */
+ 0, /* properties_required */
+ 0, /* properties_provided */
+ 0, /* properties_destroyed */
+ 0, /* todo_flags_start */
+ 0, /* todo_flags_finish */
};
+class pass_reginfo_init : public rtl_opt_pass
+{
+public:
+ pass_reginfo_init (gcc::context *ctxt)
+ : rtl_opt_pass (pass_data_reginfo_init, ctxt)
+ {}
+
+ /* opt_pass methods: */
+ virtual unsigned int execute (function *) { return reginfo_init (); }
+
+}; // class pass_reginfo_init
+
+} // anon namespace
+
+rtl_opt_pass *
+make_pass_reginfo_init (gcc::context *ctxt)
+{
+ return new pass_reginfo_init (ctxt);
+}
+
\f
-/* Set up preferred, alternate, and cover classes for REGNO as
+/* Set up preferred, alternate, and allocno classes for REGNO as
PREFCLASS, ALTCLASS, and ALLOCNOCLASS. */
void
setup_reg_classes (int regno,
{
if (reg_pref == NULL)
return;
- gcc_assert (reg_info_size == max_reg_num ());
+ gcc_assert (reg_info_size >= max_reg_num ());
reg_pref[regno].prefclass = prefclass;
reg_pref[regno].altclass = altclass;
reg_pref[regno].allocnoclass = allocnoclass;
again just before loop. It finds the first and last use of each
pseudo-register. */
-static void reg_scan_mark_refs (rtx, rtx);
+static void reg_scan_mark_refs (rtx, rtx_insn *);
void
-reg_scan (rtx f, unsigned int nregs ATTRIBUTE_UNUSED)
+reg_scan (rtx_insn *f, unsigned int nregs ATTRIBUTE_UNUSED)
{
- rtx insn;
+ rtx_insn *insn;
timevar_push (TV_REG_SCAN);
We should only record information for REGs with numbers
greater than or equal to MIN_REGNO. */
static void
-reg_scan_mark_refs (rtx x, rtx insn)
+reg_scan_mark_refs (rtx x, rtx_insn *insn)
{
enum rtx_code code;
rtx dest;
switch (code)
{
case CONST:
- case CONST_INT:
- case CONST_DOUBLE:
- case CONST_FIXED:
- case CONST_VECTOR:
+ CASE_CONST_ANY:
case CC0:
case PC:
case SYMBOL_REF:
break;
case INSN_LIST:
+ case INT_LIST:
if (XEXP (x, 1))
reg_scan_mark_refs (XEXP (x, 1), insn);
break;
/* Count a set of the destination if it is a register. */
for (dest = SET_DEST (x);
GET_CODE (dest) == SUBREG || GET_CODE (dest) == STRICT_LOW_PART
- || GET_CODE (dest) == ZERO_EXTEND;
+ || GET_CODE (dest) == ZERO_EXTRACT;
dest = XEXP (dest, 0))
;
}
\f
+inline hashval_t
+simplifiable_subregs_hasher::hash (const simplifiable_subreg *value)
+{
+ return value->shape.unique_id ();
+}
+
+inline bool
+simplifiable_subregs_hasher::equal (const simplifiable_subreg *value,
+ const subreg_shape *compare)
+{
+ return value->shape == *compare;
+}
+
+inline simplifiable_subreg::simplifiable_subreg (const subreg_shape &shape_in)
+ : shape (shape_in)
+{
+ CLEAR_HARD_REG_SET (simplifiable_regs);
+}
+
+/* Return the set of hard registers that are able to form the subreg
+ described by SHAPE. */
+
+const HARD_REG_SET &
+simplifiable_subregs (const subreg_shape &shape)
+{
+ if (!this_target_hard_regs->x_simplifiable_subregs)
+ this_target_hard_regs->x_simplifiable_subregs
+ = new hash_table <simplifiable_subregs_hasher> (30);
+ simplifiable_subreg **slot
+ = (this_target_hard_regs->x_simplifiable_subregs
+ ->find_slot_with_hash (&shape, shape.unique_id (), INSERT));
+
+ if (!*slot)
+ {
+ simplifiable_subreg *info = new simplifiable_subreg (shape);
+ for (unsigned int i = 0; i < FIRST_PSEUDO_REGISTER; ++i)
+ if (HARD_REGNO_MODE_OK (i, shape.inner_mode)
+ && simplify_subreg_regno (i, shape.inner_mode, shape.offset,
+ shape.outer_mode) >= 0)
+ SET_HARD_REG_BIT (info->simplifiable_regs, i);
+ *slot = info;
+ }
+ return (*slot)->simplifiable_regs;
+}
/* Passes for keeping and updating info about modes of registers
inside subregisters. */
-#ifdef CANNOT_CHANGE_MODE_CLASS
-
-static bitmap invalid_mode_changes;
+static HARD_REG_SET **valid_mode_changes;
+static obstack valid_mode_changes_obstack;
static void
-record_subregs_of_mode (rtx subreg, bitmap subregs_of_mode)
+record_subregs_of_mode (rtx subreg)
{
- enum machine_mode mode;
unsigned int regno;
if (!REG_P (SUBREG_REG (subreg)))
return;
regno = REGNO (SUBREG_REG (subreg));
- mode = GET_MODE (subreg);
-
if (regno < FIRST_PSEUDO_REGISTER)
return;
- if (bitmap_set_bit (subregs_of_mode,
- regno * NUM_MACHINE_MODES + (unsigned int) mode))
+ if (valid_mode_changes[regno])
+ AND_HARD_REG_SET (*valid_mode_changes[regno],
+ simplifiable_subregs (shape_of_subreg (subreg)));
+ else
{
- unsigned int rclass;
- for (rclass = 0; rclass < N_REG_CLASSES; rclass++)
- if (!bitmap_bit_p (invalid_mode_changes,
- regno * N_REG_CLASSES + rclass)
- && CANNOT_CHANGE_MODE_CLASS (PSEUDO_REGNO_MODE (regno),
- mode, (enum reg_class) rclass))
- bitmap_set_bit (invalid_mode_changes,
- regno * N_REG_CLASSES + rclass);
+ valid_mode_changes[regno]
+ = XOBNEW (&valid_mode_changes_obstack, HARD_REG_SET);
+ COPY_HARD_REG_SET (*valid_mode_changes[regno],
+ simplifiable_subregs (shape_of_subreg (subreg)));
}
}
/* Call record_subregs_of_mode for all the subregs in X. */
static void
-find_subregs_of_mode (rtx x, bitmap subregs_of_mode)
+find_subregs_of_mode (rtx x)
{
enum rtx_code code = GET_CODE (x);
const char * const fmt = GET_RTX_FORMAT (code);
int i;
if (code == SUBREG)
- record_subregs_of_mode (x, subregs_of_mode);
+ record_subregs_of_mode (x);
/* Time for some deep diving. */
for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
{
if (fmt[i] == 'e')
- find_subregs_of_mode (XEXP (x, i), subregs_of_mode);
+ find_subregs_of_mode (XEXP (x, i));
else if (fmt[i] == 'E')
{
int j;
for (j = XVECLEN (x, i) - 1; j >= 0; j--)
- find_subregs_of_mode (XVECEXP (x, i, j), subregs_of_mode);
+ find_subregs_of_mode (XVECEXP (x, i, j));
}
}
}
init_subregs_of_mode (void)
{
basic_block bb;
- rtx insn;
- bitmap_obstack srom_obstack;
- bitmap subregs_of_mode;
+ rtx_insn *insn;
- gcc_assert (invalid_mode_changes == NULL);
- invalid_mode_changes = BITMAP_ALLOC (NULL);
- bitmap_obstack_initialize (&srom_obstack);
- subregs_of_mode = BITMAP_ALLOC (&srom_obstack);
+ gcc_obstack_init (&valid_mode_changes_obstack);
+ valid_mode_changes = XCNEWVEC (HARD_REG_SET *, max_reg_num ());
- FOR_EACH_BB (bb)
+ FOR_EACH_BB_FN (bb, cfun)
FOR_BB_INSNS (bb, insn)
if (NONDEBUG_INSN_P (insn))
- find_subregs_of_mode (PATTERN (insn), subregs_of_mode);
-
- BITMAP_FREE (subregs_of_mode);
- bitmap_obstack_release (&srom_obstack);
+ find_subregs_of_mode (PATTERN (insn));
}
-/* Return 1 if REGNO has had an invalid mode change in CLASS from FROM
- mode. */
-bool
-invalid_mode_change_p (unsigned int regno,
- enum reg_class rclass)
+const HARD_REG_SET *
+valid_mode_changes_for_regno (unsigned int regno)
{
- return bitmap_bit_p (invalid_mode_changes,
- regno * N_REG_CLASSES + (unsigned) rclass);
+ return valid_mode_changes[regno];
}
void
finish_subregs_of_mode (void)
{
- BITMAP_FREE (invalid_mode_changes);
-}
-#else
-void
-init_subregs_of_mode (void)
-{
+ XDELETEVEC (valid_mode_changes);
+ obstack_free (&valid_mode_changes_obstack, NULL);
}
+
+/* Free all data attached to the structure. This isn't a destructor because
+ we don't want to run on exit. */
+
void
-finish_subregs_of_mode (void)
+target_hard_regs::finalize ()
{
+ delete x_simplifiable_subregs;
}
-
-#endif /* CANNOT_CHANGE_MODE_CLASS */