/* 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 Free Software Foundation, Inc.
+ Copyright (C) 1987-2019 Free Software Foundation, Inc.
This file is part of GCC.
#include "config.h"
#include "system.h"
#include "coretypes.h"
-#include "tm.h"
-#include "hard-reg-set.h"
+#include "backend.h"
+#include "target.h"
#include "rtl.h"
-#include "expr.h"
+#include "tree.h"
+#include "df.h"
+#include "memmodel.h"
#include "tm_p.h"
-#include "flags.h"
-#include "basic-block.h"
-#include "regs.h"
-#include "addresses.h"
-#include "function.h"
#include "insn-config.h"
+#include "regs.h"
+#include "ira.h"
#include "recog.h"
-#include "reload.h"
-#include "toplev.h"
#include "diagnostic-core.h"
+#include "reload.h"
#include "output.h"
-#include "ggc.h"
-#include "timevar.h"
-#include "hashtab.h"
-#include "target.h"
#include "tree-pass.h"
-#include "df.h"
-#include "ira.h"
/* Maximum register number used in this function, plus one. */
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;
and are also considered fixed. */
char global_regs[FIRST_PSEUDO_REGISTER];
+/* Declaration for the global 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. */
regset regs_invalidated_by_call_regset;
+/* Same information as FIXED_REG_SET but in regset form. */
+regset fixed_reg_set_regset;
+
/* The bitmap_obstack is used to hold some static variables that
should not be reset after each function is compiled. */
static bitmap_obstack persistent_obstack;
/* Array containing all of the register class names. */
const char * reg_class_names[] = REG_CLASS_NAMES;
-#define last_mode_for_init_move_cost \
- (this_target_regs->x_last_mode_for_init_move_cost)
-
-/* Sample MEM values for use by memory_move_secondary_cost. */
-static GTY(()) rtx top_of_stack[MAX_MACHINE_MODE];
-
/* No more global register variables may be declared; true once
reginfo has been initialized. */
static int no_global_reg_vars = 0;
}
}
-/* Function called only once to initialize the above data on reg usage.
- Once this is done, various switches may override. */
+/* Function called only once per target_globals to initialize the
+ target_hard_regs structure. Once this is done, various switches
+ may override. */
void
init_reg_sets (void)
{
memcpy (reg_alloc_order, initial_reg_alloc_order, sizeof reg_alloc_order);
#endif
memcpy (reg_names, initial_reg_names, sizeof reg_names);
- memset (global_regs, 0, sizeof global_regs);
-}
-/* Initialize may_move_cost and friends for mode M. */
-void
-init_move_cost (enum machine_mode m)
-{
- static unsigned short last_move_cost[N_REG_CLASSES][N_REG_CLASSES];
- bool all_match = true;
- unsigned int i, j;
-
- gcc_assert (have_regs_of_mode[m]);
- for (i = 0; i < N_REG_CLASSES; i++)
- if (contains_reg_of_mode[i][m])
- for (j = 0; j < N_REG_CLASSES; j++)
- {
- int cost;
- if (!contains_reg_of_mode[j][m])
- cost = 65535;
- else
- {
- cost = register_move_cost (m, (enum reg_class) i,
- (enum reg_class) j);
- gcc_assert (cost < 65535);
- }
- all_match &= (last_move_cost[i][j] == cost);
- last_move_cost[i][j] = cost;
- }
- if (all_match && last_mode_for_init_move_cost != -1)
- {
- move_cost[m] = move_cost[last_mode_for_init_move_cost];
- may_move_in_cost[m] = may_move_in_cost[last_mode_for_init_move_cost];
- may_move_out_cost[m] = may_move_out_cost[last_mode_for_init_move_cost];
- return;
- }
- last_mode_for_init_move_cost = m;
- move_cost[m] = (move_table *)xmalloc (sizeof (move_table)
- * N_REG_CLASSES);
- may_move_in_cost[m] = (move_table *)xmalloc (sizeof (move_table)
- * N_REG_CLASSES);
- may_move_out_cost[m] = (move_table *)xmalloc (sizeof (move_table)
- * N_REG_CLASSES);
- for (i = 0; i < N_REG_CLASSES; i++)
- if (contains_reg_of_mode[i][m])
- for (j = 0; j < N_REG_CLASSES; j++)
- {
- int cost;
- enum reg_class *p1, *p2;
-
- if (last_move_cost[i][j] == 65535)
- {
- move_cost[m][i][j] = 65535;
- may_move_in_cost[m][i][j] = 65535;
- may_move_out_cost[m][i][j] = 65535;
- }
- else
- {
- cost = last_move_cost[i][j];
-
- for (p2 = ®_class_subclasses[j][0];
- *p2 != LIM_REG_CLASSES; p2++)
- if (*p2 != i && contains_reg_of_mode[*p2][m])
- cost = MAX (cost, move_cost[m][i][*p2]);
-
- for (p1 = ®_class_subclasses[i][0];
- *p1 != LIM_REG_CLASSES; p1++)
- if (*p1 != j && contains_reg_of_mode[*p1][m])
- cost = MAX (cost, move_cost[m][*p1][j]);
-
- gcc_assert (cost <= 65535);
- move_cost[m][i][j] = cost;
-
- if (reg_class_subset_p ((enum reg_class) i, (enum reg_class) j))
- may_move_in_cost[m][i][j] = 0;
- else
- may_move_in_cost[m][i][j] = cost;
-
- if (reg_class_subset_p ((enum reg_class) j, (enum reg_class) i))
- may_move_out_cost[m][i][j] = 0;
- else
- may_move_out_cost[m][i][j] = cost;
- }
- }
- else
- for (j = 0; j < N_REG_CLASSES; j++)
- {
- move_cost[m][i][j] = 65535;
- may_move_in_cost[m][i][j] = 65535;
- may_move_out_cost[m][i][j] = 65535;
- }
+ SET_HARD_REG_SET (accessible_reg_set);
+ SET_HARD_REG_SET (operand_reg_set);
}
/* We need to save copies of some of the register information which
static char saved_call_really_used_regs[FIRST_PSEUDO_REGISTER];
#endif
static const char *saved_reg_names[FIRST_PSEUDO_REGISTER];
+static HARD_REG_SET saved_accessible_reg_set;
+static HARD_REG_SET saved_operand_reg_set;
/* Save the register information. */
void
/* And similarly for reg_names. */
gcc_assert (sizeof reg_names == sizeof saved_reg_names);
memcpy (saved_reg_names, reg_names, sizeof reg_names);
+ COPY_HARD_REG_SET (saved_accessible_reg_set, accessible_reg_set);
+ COPY_HARD_REG_SET (saved_operand_reg_set, operand_reg_set);
}
/* Restore the register information. */
#endif
memcpy (reg_names, saved_reg_names, sizeof reg_names);
+ COPY_HARD_REG_SET (accessible_reg_set, saved_accessible_reg_set);
+ COPY_HARD_REG_SET (operand_reg_set, saved_operand_reg_set);
}
/* After switches have been processed, which perhaps alter
init_reg_sets_1 (void)
{
unsigned int i, j;
- unsigned int /* enum machine_mode */ m;
+ unsigned int /* machine_mode */ m;
restore_register_info ();
inv_reg_alloc_order[reg_alloc_order[i]] = i;
#endif
- /* This macro allows the fixed or call-used registers
- and the register classes to depend on target flags. */
+ /* Let the target tweak things if necessary. */
-#ifdef CONDITIONAL_REGISTER_USAGE
- CONDITIONAL_REGISTER_USAGE;
-#endif
+ targetm.conditional_register_usage ();
/* Compute number of hard regs in each class. */
}
else
CLEAR_REG_SET (regs_invalidated_by_call_regset);
+ if (!fixed_reg_set_regset)
+ fixed_reg_set_regset = ALLOC_REG_SET (&persistent_obstack);
+ else
+ CLEAR_REG_SET (fixed_reg_set_regset);
+ AND_HARD_REG_SET (operand_reg_set, accessible_reg_set);
for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
{
+ /* As a special exception, registers whose class is NO_REGS are
+ not accepted by `register_operand'. The reason for this change
+ is to allow the representation of special architecture artifacts
+ (such as a condition code register) without extending the rtl
+ definitions. Since registers of class NO_REGS cannot be used
+ as registers in any case where register classes are examined,
+ it is better to apply this exception in a target-independent way. */
+ if (REGNO_REG_CLASS (i) == NO_REGS)
+ CLEAR_HARD_REG_BIT (operand_reg_set, i);
+
+ /* If a register is too limited to be treated as a register operand,
+ then it should never be allocated to a pseudo. */
+ if (!TEST_HARD_REG_BIT (operand_reg_set, i))
+ {
+ fixed_regs[i] = 1;
+ call_used_regs[i] = 1;
+ }
+
/* call_used_regs must include fixed_regs. */
gcc_assert (!fixed_regs[i] || call_used_regs[i]);
#ifdef CALL_REALLY_USED_REGISTERS
#endif
if (fixed_regs[i])
- SET_HARD_REG_BIT (fixed_reg_set, i);
+ {
+ SET_HARD_REG_BIT (fixed_reg_set, i);
+ SET_REGNO_REG_SET (fixed_reg_set_regset, i);
+ }
if (call_used_regs[i])
SET_HARD_REG_BIT (call_used_reg_set, i);
}
else if (i == FRAME_POINTER_REGNUM)
;
-#if HARD_FRAME_POINTER_REGNUM != FRAME_POINTER_REGNUM
- 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
-#ifndef PIC_OFFSET_TABLE_REG_CALL_CLOBBERED
- else if (i == (unsigned) PIC_OFFSET_TABLE_REGNUM && fixed_regs[i])
+ else if (!PIC_OFFSET_TABLE_REG_CALL_CLOBBERED
+ && i == (unsigned) PIC_OFFSET_TABLE_REGNUM && fixed_regs[i])
;
-#endif
else if (CALL_REALLY_USED_REGNO_P (i))
{
SET_HARD_REG_BIT (regs_invalidated_by_call, i);
}
}
- COPY_HARD_REG_SET(call_fixed_reg_set, fixed_reg_set);
+ COPY_HARD_REG_SET (call_fixed_reg_set, fixed_reg_set);
+ COPY_HARD_REG_SET (fixed_nonglobal_reg_set, fixed_reg_set);
/* Preserve global registers if called more than once. */
for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
memset (contains_reg_of_mode, 0, sizeof (contains_reg_of_mode));
for (m = 0; m < (unsigned int) MAX_MACHINE_MODE; m++)
{
- HARD_REG_SET ok_regs;
+ HARD_REG_SET ok_regs, ok_regs2;
CLEAR_HARD_REG_SET (ok_regs);
+ CLEAR_HARD_REG_SET (ok_regs2);
for (j = 0; j < FIRST_PSEUDO_REGISTER; j++)
- if (!fixed_regs [j] && HARD_REGNO_MODE_OK (j, (enum machine_mode) m))
- SET_HARD_REG_BIT (ok_regs, j);
+ if (!TEST_HARD_REG_BIT (fixed_nonglobal_reg_set, j)
+ && targetm.hard_regno_mode_ok (j, (machine_mode) m))
+ {
+ SET_HARD_REG_BIT (ok_regs, j);
+ if (!fixed_regs[j])
+ SET_HARD_REG_BIT (ok_regs2, j);
+ }
for (i = 0; i < N_REG_CLASSES; i++)
- if (((unsigned) CLASS_MAX_NREGS ((enum reg_class) 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]))
{
- contains_reg_of_mode [i][m] = 1;
- have_regs_of_mode [m] = 1;
+ contains_reg_of_mode[i][m] = 1;
+ if (hard_reg_set_intersect_p (ok_regs2, reg_class_contents[i]))
+ {
+ have_regs_of_mode[m] = 1;
+ contains_allocatable_reg_of_mode[i][m] = 1;
+ }
}
}
-
- /* Reset move_cost and friends, making sure we only free shared
- table entries once. */
- for (i = 0; i < MAX_MACHINE_MODE; i++)
- if (move_cost[i])
- {
- for (j = 0; j < i && move_cost[i] != move_cost[j]; j++)
- ;
- if (i == j)
- {
- free (move_cost[i]);
- free (may_move_in_cost[i]);
- free (may_move_out_cost[i]);
- }
- }
- memset (move_cost, 0, sizeof move_cost);
- memset (may_move_in_cost, 0, sizeof may_move_in_cost);
- memset (may_move_out_cost, 0, sizeof may_move_out_cost);
- last_mode_for_init_move_cost = -1;
}
/* Compute the table of register modes.
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);
+ this_target_regs->x_hard_regno_nregs[i][j]
+ = targetm.hard_regno_nregs (i, (machine_mode) j);
for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
{
reg_raw_mode[i] = choose_hard_reg_mode (i, 1, false);
- /* If we couldn't find a valid mode, just use the previous mode.
- ??? One situation in which we need to do this is on the mips where
- HARD_REGNO_NREGS (fpreg, [SD]Fmode) returns 2. Ideally we'd like
- to use DF mode for the even registers and VOIDmode for the odd
- (for the cpu models where the odd ones are inaccessible). */
+ /* If we couldn't find a valid mode, just use the previous mode
+ if it is suitable, otherwise fall back on word_mode. */
if (reg_raw_mode[i] == VOIDmode)
- reg_raw_mode[i] = i == 0 ? word_mode : reg_raw_mode[i-1];
+ {
+ if (i > 0 && hard_regno_nregs (i, reg_raw_mode[i - 1]) == 1)
+ reg_raw_mode[i] = reg_raw_mode[i - 1];
+ else
+ reg_raw_mode[i] = word_mode;
+ }
}
}
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, enum reg_class from,
- enum reg_class 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, enum reg_class 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, enum reg_class rclass,
+memory_move_secondary_cost (machine_mode mode, reg_class_t rclass,
bool in)
{
- enum reg_class altclass;
+ reg_class_t altclass;
int partial_cost = 0;
/* We need a memory reference to feed to SECONDARY... macros. */
/* mem may be unused even if the SECONDARY_ macros are defined. */
/* 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.
- If we still didn't find a valid mode, try CCmode. */
-
- for (mode = GET_CLASS_NARROWEST_MODE (MODE_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)))
+ If we still didn't find a valid mode, try CCmode.
+
+ The tests use maybe_gt rather than known_gt because we want (for example)
+ N V4SFs to win over plain V4SF even though N might be 1. */
+ FOR_EACH_MODE_IN_CLASS (mode, MODE_INT)
+ if (hard_regno_nregs (regno, mode) == nregs
+ && targetm.hard_regno_mode_ok (regno, mode)
+ && (!call_saved
+ || !targetm.hard_regno_call_part_clobbered (NULL, regno, mode))
+ && maybe_gt (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)))
+ FOR_EACH_MODE_IN_CLASS (mode, MODE_FLOAT)
+ if (hard_regno_nregs (regno, mode) == nregs
+ && targetm.hard_regno_mode_ok (regno, mode)
+ && (!call_saved
+ || !targetm.hard_regno_call_part_clobbered (NULL, regno, mode))
+ && maybe_gt (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)))
+ FOR_EACH_MODE_IN_CLASS (mode, MODE_VECTOR_FLOAT)
+ if (hard_regno_nregs (regno, mode) == nregs
+ && targetm.hard_regno_mode_ok (regno, mode)
+ && (!call_saved
+ || !targetm.hard_regno_call_part_clobbered (NULL, regno, mode))
+ && maybe_gt (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)))
+ FOR_EACH_MODE_IN_CLASS (mode, MODE_VECTOR_INT)
+ if (hard_regno_nregs (regno, mode) == nregs
+ && targetm.hard_regno_mode_ok (regno, mode)
+ && (!call_saved
+ || !targetm.hard_regno_call_part_clobbered (NULL, regno, mode))
+ && maybe_gt (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;
- if ((unsigned) hard_regno_nregs[regno][mode] == nregs
- && HARD_REGNO_MODE_OK (regno, mode)
- && (! call_saved || ! HARD_REGNO_CALL_PART_CLOBBERED (regno, mode)))
+ mode = (machine_mode) m;
+ if (hard_regno_nregs (regno, mode) == nregs
+ && targetm.hard_regno_mode_ok (regno, mode)
+ && (!call_saved
+ || !targetm.hard_regno_call_part_clobbered (NULL, regno, mode)))
return mode;
}
fix_register (const char *name, int fixed, int call_used)
{
int i;
+ int reg, nregs;
/* Decode the name and update the primary form of
the register info. */
- if ((i = decode_reg_name (name)) >= 0)
+ if ((reg = decode_reg_name_and_count (name, &nregs)) >= 0)
{
- if ((i == STACK_POINTER_REGNUM
+ gcc_assert (nregs >= 1);
+ for (i = reg; i < reg + nregs; i++)
+ {
+ if ((i == STACK_POINTER_REGNUM
#ifdef HARD_FRAME_POINTER_REGNUM
- || i == HARD_FRAME_POINTER_REGNUM
+ || i == HARD_FRAME_POINTER_REGNUM
#else
- || i == FRAME_POINTER_REGNUM
+ || i == FRAME_POINTER_REGNUM
#endif
- )
- && (fixed == 0 || call_used == 0))
- {
- static const char * const what_option[2][2] = {
- { "call-saved", "call-used" },
- { "no-such-option", "fixed" }};
-
- error ("can't use '%s' as a %s register", name,
- what_option[fixed][call_used]);
- }
- else
- {
- fixed_regs[i] = fixed;
- call_used_regs[i] = call_used;
+ )
+ && (fixed == 0 || call_used == 0))
+ {
+ switch (fixed)
+ {
+ case 0:
+ switch (call_used)
+ {
+ case 0:
+ error ("can%'t use %qs as a call-saved register", name);
+ break;
+
+ case 1:
+ error ("can%'t use %qs as a call-used register", name);
+ break;
+
+ default:
+ gcc_unreachable ();
+ }
+ break;
+
+ case 1:
+ switch (call_used)
+ {
+ case 1:
+ error ("can%'t use %qs as a fixed register", name);
+ break;
+
+ case 0:
+ default:
+ gcc_unreachable ();
+ }
+ break;
+
+ default:
+ gcc_unreachable ();
+ }
+ }
+ else
+ {
+ fixed_regs[i] = fixed;
+ call_used_regs[i] = call_used;
#ifdef CALL_REALLY_USED_REGISTERS
- if (fixed == 0)
- call_really_used_regs[i] = call_used;
+ if (fixed == 0)
+ call_really_used_regs[i] = call_used;
#endif
+ }
}
}
else
/* Mark register number I as global. */
void
-globalize_reg (int i)
+globalize_reg (tree decl, int i)
{
+ location_t loc = DECL_SOURCE_LOCATION (decl);
+
+#ifdef STACK_REGS
+ if (IN_RANGE (i, FIRST_STACK_REG, LAST_STACK_REG))
+ {
+ error ("stack register used for global register variable");
+ return;
+ }
+#endif
+
if (fixed_regs[i] == 0 && no_global_reg_vars)
- error ("global register variable follows a function definition");
+ error_at (loc, "global register variable follows a function definition");
if (global_regs[i])
{
- warning (0, "register used for two global register variables");
+ auto_diagnostic_group d;
+ warning_at (loc, 0,
+ "register of %qD used for multiple global register variables",
+ decl);
+ inform (DECL_SOURCE_LOCATION (global_regs_decl[i]),
+ "conflicts with %qD", global_regs_decl[i]);
return;
}
if (call_used_regs[i] && ! fixed_regs[i])
- warning (0, "call-clobbered register used for global register variable");
+ warning_at (loc, 0, "call-clobbered register used for global register variable");
global_regs[i] = 1;
+ global_regs_decl[i] = decl;
/* If we're globalizing the frame pointer, we need to set the
appropriate regs_invalidated_by_call bit, even if it's already
union of most major pair of classes, that generality is not required. */
char altclass;
- /* coverclass is a register class that IRA uses for allocating
+ /* allocnoclass is a register class that IRA uses for allocating
the pseudo. */
- char coverclass;
+ char allocnoclass;
};
/* Record preferences of each pseudo. This is available after RA is
/* 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;
}
/* Return the reg_class which is used by IRA for its allocation. */
enum reg_class
-reg_cover_class (int regno)
+reg_allocno_class (int regno)
{
if (reg_pref == 0)
return NO_REGS;
- return (enum reg_class) reg_pref[regno].coverclass;
+ 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
- PREFCLASS, ALTCLASS, and COVERCLASS. */
+/* Set up preferred, alternate, and allocno classes for REGNO as
+ PREFCLASS, ALTCLASS, and ALLOCNOCLASS. */
void
setup_reg_classes (int regno,
enum reg_class prefclass, enum reg_class altclass,
- enum reg_class coverclass)
+ enum reg_class allocnoclass)
{
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].coverclass = coverclass;
+ reg_pref[regno].allocnoclass = allocnoclass;
}
\f
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;
reg_scan_mark_refs (XEXP (XEXP (x, 0), 0), insn);
break;
+ case CLOBBER_HIGH:
+ gcc_assert (!(MEM_P (XEXP (x, 0))));
+ break;
+
case SET:
/* 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))
;
/* If this is setting a register from a register or from a simple
conversion of a register, propagate REG_EXPR. */
if (REG_P (dest) && !REG_ATTRS (dest))
- {
- rtx src = SET_SRC (x);
-
- while (GET_CODE (src) == SIGN_EXTEND
- || GET_CODE (src) == ZERO_EXTEND
- || GET_CODE (src) == TRUNCATE
- || (GET_CODE (src) == SUBREG && subreg_lowpart_p (src)))
- src = XEXP (src, 0);
-
- set_reg_attrs_from_value (dest, src);
- }
+ set_reg_attrs_from_value (dest, SET_SRC (x));
- /* ... fall through ... */
+ /* fall through */
default:
{
/* Return nonzero if C1 is a subset of C2, i.e., if every register in C1
is also in C2. */
int
-reg_class_subset_p (enum reg_class c1, enum reg_class c2)
+reg_class_subset_p (reg_class_t c1, reg_class_t c2)
{
return (c1 == c2
|| c2 == ALL_REGS
}
\f
-
-/* Passes for keeping and updating info about modes of registers
- inside subregisters. */
-
-#ifdef CANNOT_CHANGE_MODE_CLASS
-
-struct subregs_of_mode_node
+inline hashval_t
+simplifiable_subregs_hasher::hash (const simplifiable_subreg *value)
{
- unsigned int block;
- unsigned char modes[MAX_MACHINE_MODE];
-};
+ inchash::hash h;
+ h.add_hwi (value->shape.unique_id ());
+ return h.end ();
+}
-static htab_t subregs_of_mode;
+inline bool
+simplifiable_subregs_hasher::equal (const simplifiable_subreg *value,
+ const subreg_shape *compare)
+{
+ return value->shape == *compare;
+}
-static hashval_t
-som_hash (const void *x)
+inline simplifiable_subreg::simplifiable_subreg (const subreg_shape &shape_in)
+ : shape (shape_in)
{
- const struct subregs_of_mode_node *const a =
- (const struct subregs_of_mode_node *) x;
- return a->block;
+ CLEAR_HARD_REG_SET (simplifiable_regs);
}
-static int
-som_eq (const void *x, const void *y)
+/* 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)
{
- const struct subregs_of_mode_node *const a =
- (const struct subregs_of_mode_node *) x;
- const struct subregs_of_mode_node *const b =
- (const struct subregs_of_mode_node *) y;
- return a->block == b->block;
+ if (!this_target_hard_regs->x_simplifiable_subregs)
+ this_target_hard_regs->x_simplifiable_subregs
+ = new hash_table <simplifiable_subregs_hasher> (30);
+ inchash::hash h;
+ h.add_hwi (shape.unique_id ());
+ simplifiable_subreg **slot
+ = (this_target_hard_regs->x_simplifiable_subregs
+ ->find_slot_with_hash (&shape, h.end (), INSERT));
+
+ if (!*slot)
+ {
+ simplifiable_subreg *info = new simplifiable_subreg (shape);
+ for (unsigned int i = 0; i < FIRST_PSEUDO_REGISTER; ++i)
+ if (targetm.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. */
+
+static HARD_REG_SET **valid_mode_changes;
+static obstack valid_mode_changes_obstack;
+
+/* Restrict the choice of register for SUBREG_REG (SUBREG) based
+ on information about SUBREG.
+
+ If PARTIAL_DEF, SUBREG is a partial definition of a multipart inner
+ register and we want to ensure that the other parts of the inner
+ register are correctly preserved. If !PARTIAL_DEF we need to
+ ensure that SUBREG itself can be formed. */
+
static void
-record_subregs_of_mode (rtx subreg)
+record_subregs_of_mode (rtx subreg, bool partial_def)
{
- struct subregs_of_mode_node dummy, *node;
- enum machine_mode mode;
unsigned int regno;
- void **slot;
if (!REG_P (SUBREG_REG (subreg)))
return;
regno = REGNO (SUBREG_REG (subreg));
- mode = GET_MODE (subreg);
-
if (regno < FIRST_PSEUDO_REGISTER)
return;
- dummy.block = regno & -8;
- slot = htab_find_slot_with_hash (subregs_of_mode, &dummy,
- dummy.block, INSERT);
- node = (struct subregs_of_mode_node *) *slot;
- if (node == NULL)
+ subreg_shape shape (shape_of_subreg (subreg));
+ if (partial_def)
{
- node = XCNEW (struct subregs_of_mode_node);
- node->block = regno & -8;
- *slot = node;
+ /* The number of independently-accessible SHAPE.outer_mode values
+ in SHAPE.inner_mode is GET_MODE_SIZE (SHAPE.inner_mode) / SIZE.
+ We need to check that the assignment will preserve all the other
+ SIZE-byte chunks in the inner register besides the one that
+ includes SUBREG.
+
+ In practice it is enough to check whether an equivalent
+ SHAPE.inner_mode value in an adjacent SIZE-byte chunk can be formed.
+ If the underlying registers are small enough, both subregs will
+ be valid. If the underlying registers are too large, one of the
+ subregs will be invalid.
+
+ This relies on the fact that we've already been passed
+ SUBREG with PARTIAL_DEF set to false.
+
+ The size of the outer mode must ordered wrt the size of the
+ inner mode's registers, since otherwise we wouldn't know at
+ compile time how many registers the outer mode occupies. */
+ poly_uint64 size = ordered_max (REGMODE_NATURAL_SIZE (shape.inner_mode),
+ GET_MODE_SIZE (shape.outer_mode));
+ gcc_checking_assert (known_lt (size, GET_MODE_SIZE (shape.inner_mode)));
+ if (known_ge (shape.offset, size))
+ shape.offset -= size;
+ else
+ shape.offset += size;
}
- node->modes[mode] |= 1 << (regno & 7);
+ if (valid_mode_changes[regno])
+ AND_HARD_REG_SET (*valid_mode_changes[regno],
+ simplifiable_subregs (shape));
+ else
+ {
+ valid_mode_changes[regno]
+ = XOBNEW (&valid_mode_changes_obstack, HARD_REG_SET);
+ COPY_HARD_REG_SET (*valid_mode_changes[regno],
+ simplifiable_subregs (shape));
+ }
}
/* Call record_subregs_of_mode for all the subregs in X. */
int i;
if (code == SUBREG)
- record_subregs_of_mode (x);
+ record_subregs_of_mode (x, false);
/* Time for some deep diving. */
for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
init_subregs_of_mode (void)
{
basic_block bb;
- rtx insn;
+ rtx_insn *insn;
- if (subregs_of_mode)
- htab_empty (subregs_of_mode);
- else
- subregs_of_mode = htab_create (100, som_hash, som_eq, free);
+ 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 (INSN_P (insn))
- find_subregs_of_mode (PATTERN (insn));
+ if (NONDEBUG_INSN_P (insn))
+ {
+ find_subregs_of_mode (PATTERN (insn));
+ df_ref def;
+ FOR_EACH_INSN_DEF (def, insn)
+ if (DF_REF_FLAGS_IS_SET (def, DF_REF_PARTIAL)
+ && read_modify_subreg_p (DF_REF_REG (def)))
+ record_subregs_of_mode (DF_REF_REG (def), true);
+ }
}
-/* 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 ATTRIBUTE_UNUSED,
- enum machine_mode from)
+const HARD_REG_SET *
+valid_mode_changes_for_regno (unsigned int regno)
{
- struct subregs_of_mode_node dummy, *node;
- unsigned int to;
- unsigned char mask;
-
- gcc_assert (subregs_of_mode);
- dummy.block = regno & -8;
- node = (struct subregs_of_mode_node *)
- htab_find_with_hash (subregs_of_mode, &dummy, dummy.block);
- if (node == NULL)
- return false;
-
- mask = 1 << (regno & 7);
- for (to = VOIDmode; to < NUM_MACHINE_MODES; to++)
- if (node->modes[to] & mask)
- if (CANNOT_CHANGE_MODE_CLASS (from, (enum machine_mode) to, rclass))
- return true;
-
- return false;
+ return valid_mode_changes[regno];
}
void
finish_subregs_of_mode (void)
{
- htab_delete (subregs_of_mode);
- subregs_of_mode = 0;
-}
-#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 */
-
-#include "gt-reginfo.h"