x86: Update message for target_clones and unsupported ISAs
[gcc.git] / gcc / reginfo.c
index f17084c46ef58d97daf2a7ce69e37496958a3742..315c5ecabe6d7bb220f3accc8b01674876606605 100644 (file)
@@ -1,7 +1,5 @@
 /* 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.
 
@@ -30,34 +28,35 @@ along with GCC; see the file COPYING3.  If not see
 #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;
@@ -89,10 +88,16 @@ static const char initial_call_really_used_regs[] = CALL_REALLY_USED_REGISTERS;
    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;
@@ -118,12 +123,6 @@ static const char *const initial_reg_names[] = REGISTER_NAMES;
 /* 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;
@@ -146,8 +145,9 @@ reg_set_to_hard_reg_set (HARD_REG_SET *to, const_bitmap from)
     }
 }
 
-/* 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)
 {
@@ -190,96 +190,9 @@ 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 = &reg_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 = &reg_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
@@ -291,6 +204,8 @@ static char saved_call_used_regs[FIRST_PSEUDO_REGISTER];
 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
@@ -314,6 +229,8 @@ save_register_info (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.  */
@@ -329,6 +246,8 @@ restore_register_info (void)
 #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
@@ -337,7 +256,7 @@ static void
 init_reg_sets_1 (void)
 {
   unsigned int i, j;
-  unsigned int /* enum machine_mode */ m;
+  unsigned int /* machine_mode */ m;
 
   restore_register_info ();
 
@@ -346,12 +265,9 @@ init_reg_sets_1 (void)
     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.  */
 
@@ -456,9 +372,32 @@ init_reg_sets_1 (void)
     }
   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
@@ -467,7 +406,10 @@ init_reg_sets_1 (void)
 #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);
@@ -491,18 +433,15 @@ init_reg_sets_1 (void)
        }
       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);
@@ -510,7 +449,8 @@ init_reg_sets_1 (void)
         }
     }
 
-  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++)
@@ -528,41 +468,31 @@ init_reg_sets_1 (void)
   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.
@@ -578,19 +508,22 @@ init_reg_modes_target (void)
 
   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;
+       }
     }
 }
 
@@ -613,7 +546,11 @@ reinit_regs (void)
   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
@@ -624,7 +561,7 @@ init_fake_stack_mems (void)
   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);
 }
 
 
@@ -632,15 +569,15 @@ init_fake_stack_mems (void)
    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);
 }
@@ -648,10 +585,10 @@ memory_move_cost (enum machine_mode mode, enum reg_class rclass, bool 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.  */
@@ -685,56 +622,49 @@ memory_move_secondary_cost (enum machine_mode mode, enum reg_class rclass,
 /* 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)
@@ -743,10 +673,11 @@ choose_hard_reg_mode (unsigned int regno ATTRIBUTE_UNUSED,
   /* 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;
     }
 
@@ -761,36 +692,69 @@ void
 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
@@ -801,21 +765,37 @@ fix_register (const char *name, int fixed, int call_used)
 
 /* 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
@@ -859,9 +839,9 @@ struct reg_pref
      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
@@ -870,6 +850,8 @@ static struct reg_pref *reg_pref;
 
 /* 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.
@@ -880,6 +862,7 @@ reg_preferred_class (int regno)
   if (reg_pref == 0)
     return GENERAL_REGS;
 
+  gcc_assert (regno < reg_info_size);
   return (enum reg_class) reg_pref[regno].prefclass;
 }
 
@@ -889,55 +872,75 @@ reg_alternate_class (int regno)
   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;
 }
 
@@ -966,48 +969,65 @@ reginfo_init (void)
   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
@@ -1015,12 +1035,12 @@ setup_reg_classes (int regno,
    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);
 
@@ -1041,7 +1061,7 @@ reg_scan (rtx f, unsigned int nregs ATTRIBUTE_UNUSED)
    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;
@@ -1053,10 +1073,7 @@ reg_scan_mark_refs (rtx x, rtx insn)
   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:
@@ -1074,6 +1091,7 @@ reg_scan_mark_refs (rtx x, rtx insn)
       break;
 
     case INSN_LIST:
+    case INT_LIST:
       if (XEXP (x, 1))
        reg_scan_mark_refs (XEXP (x, 1), insn);
       break;
@@ -1083,11 +1101,15 @@ reg_scan_mark_refs (rtx x, rtx insn)
        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))
        ;
 
@@ -1141,19 +1163,9 @@ reg_scan_mark_refs (rtx x, rtx insn)
       /* 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:
       {
@@ -1178,7 +1190,7 @@ reg_scan_mark_refs (rtx x, rtx insn)
 /* 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
@@ -1198,67 +1210,121 @@ reg_classes_intersect_p (reg_class_t c1, reg_class_t c2)
 }
 
 \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.  */
@@ -1270,7 +1336,7 @@ find_subregs_of_mode (rtx 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--)
@@ -1290,62 +1356,42 @@ void
 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"