OSDN Git Service

PR rtl-optimization/21299
[pf3gnuchains/gcc-fork.git] / gcc / reload1.c
index 4dd1153..58f77cb 100644 (file)
@@ -1,6 +1,7 @@
 /* Reload pseudo regs into hard regs for insns that require hard regs.
    Copyright (C) 1987, 1988, 1989, 1992, 1993, 1994, 1995, 1996, 1997, 1998,
-   1999, 2000, 2001, 2002, 2003, 2004 Free Software Foundation, Inc.
+   1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006 Free Software Foundation,
+   Inc.
 
 This file is part of GCC.
 
@@ -16,8 +17,8 @@ for more details.
 
 You should have received a copy of the GNU General Public License
 along with GCC; see the file COPYING.  If not, write to the Free
-Software Foundation, 59 Temple Place - Suite 330, Boston, MA
-02111-1307, USA.  */
+Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301, USA.  */
 
 #include "config.h"
 #include "system.h"
@@ -35,6 +36,7 @@ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
 #include "expr.h"
 #include "optabs.h"
 #include "regs.h"
+#include "addresses.h"
 #include "basic-block.h"
 #include "reload.h"
 #include "recog.h"
@@ -43,6 +45,7 @@ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
 #include "toplev.h"
 #include "except.h"
 #include "tree.h"
+#include "target.h"
 
 /* This file contains the reload pass of the compiler, which is
    run after register allocation has been done.  It checks that
@@ -84,7 +87,7 @@ static rtx *reg_last_reload_reg;
 
 /* Elt N nonzero if reg_last_reload_reg[N] has been set in this insn
    for an output reload that stores into reg N.  */
-static char *reg_has_output_reload;
+static regset_head reg_has_output_reload;
 
 /* Indicates which hard regs are reload-registers for an output reload
    in the current insn.  */
@@ -96,6 +99,11 @@ static HARD_REG_SET reg_is_output_reload;
    with the constant it stands for.  */
 rtx *reg_equiv_constant;
 
+/* Element N is an invariant value to which pseudo reg N is equivalent.
+   eliminate_regs_in_insn uses this to replace pseudos in particular
+   contexts.  */
+rtx *reg_equiv_invariant;
+
 /* Element N is a memory location to which pseudo reg N is equivalent,
    prior to any register elimination (such as frame pointer to stack
    pointer).  Depending on whether or not it is a valid address, this value
@@ -104,7 +112,7 @@ rtx *reg_equiv_memory_loc;
 
 /* We allocate reg_equiv_memory_loc inside a varray so that the garbage
    collector can keep track of what is inside.  */
-varray_type reg_equiv_memory_loc_varray;
+VEC(rtx,gc) *reg_equiv_memory_loc_vec;
 
 /* Element N is the address of stack slot to which pseudo reg N is equivalent.
    This is used when the address is not valid as a memory address
@@ -115,12 +123,17 @@ rtx *reg_equiv_address;
    or zero if pseudo reg N is not equivalent to a memory slot.  */
 rtx *reg_equiv_mem;
 
+/* Element N is an EXPR_LIST of REG_EQUIVs containing MEMs with
+   alternate representations of the location of pseudo reg N.  */
+rtx *reg_equiv_alt_mem_list;
+
 /* Widest width in which each pseudo reg is referred to (via subreg).  */
 static unsigned int *reg_max_ref_width;
 
 /* Element N is the list of insns that initialized reg N from its equivalent
    constant or memory slot.  */
-static rtx *reg_equiv_init;
+rtx *reg_equiv_init;
+int reg_equiv_init_size;
 
 /* Vector to remember old contents of reg_renumber before spilling.  */
 static short *reg_old_renumber;
@@ -268,15 +281,15 @@ enum insn_code reload_out_optab[NUM_MACHINE_MODES];
 /* This obstack is used for allocation of rtl during register elimination.
    The allocated storage can be freed once find_reloads has processed the
    insn.  */
-struct obstack reload_obstack;
+static struct obstack reload_obstack;
 
 /* Points to the beginning of the reload_obstack.  All insn_chain structures
    are allocated first.  */
-char *reload_startobj;
+static char *reload_startobj;
 
 /* The point after all insn_chain structures.  Used to quickly deallocate
    memory allocated in copy_reloads during calculate_needs_all_insns.  */
-char *reload_firstobj;
+static char *reload_firstobj;
 
 /* This points before all local rtl generated by register elimination.
    Used to quickly free all memory after processing one insn.  */
@@ -383,19 +396,19 @@ static int eliminate_regs_in_insn (rtx, int);
 static void update_eliminable_offsets (void);
 static void mark_not_eliminable (rtx, rtx, void *);
 static void set_initial_elim_offsets (void);
-static void verify_initial_elim_offsets (void);
+static bool verify_initial_elim_offsets (void);
 static void set_initial_label_offsets (void);
 static void set_offsets_for_label (rtx);
 static void init_elim_table (void);
 static void update_eliminables (HARD_REG_SET *);
 static void spill_hard_reg (unsigned int, int);
 static int finish_spills (int);
-static void ior_hard_reg_set (HARD_REG_SET *, HARD_REG_SET *);
 static void scan_paradoxical_subregs (rtx);
 static void count_pseudo (int);
 static void order_regs_for_reload (struct insn_chain *);
 static void reload_as_needed (int);
 static void forget_old_reloads_1 (rtx, rtx, void *);
+static void forget_marked_reloads (regset);
 static int reload_reg_class_lower (const void *, const void *);
 static void mark_reload_reg_in_use (unsigned int, int, enum reload_type,
                                    enum machine_mode);
@@ -406,7 +419,6 @@ static int reload_reg_free_for_value_p (int, int, int, enum reload_type,
                                        rtx, rtx, int, int);
 static int free_for_value_p (int, enum machine_mode, int, enum reload_type,
                             rtx, rtx, int, int);
-static int function_invariant_p (rtx);
 static int reload_reg_reaches_end_p (unsigned int, int, enum reload_type);
 static int allocate_reload_reg (struct insn_chain *, int, int);
 static int conflicts_with_override (rtx);
@@ -431,6 +443,9 @@ static rtx inc_for_reload (rtx, rtx, rtx, int);
 static void add_auto_inc_notes (rtx, rtx);
 #endif
 static void copy_eh_notes (rtx, rtx);
+static int reloads_conflict (int, int);
+static rtx gen_reload (rtx, rtx, int, enum reload_type);
+static rtx emit_insn_if_valid_for_reload (rtx);
 \f
 /* Initialize the reload pass once per compilation.  */
 
@@ -486,7 +501,6 @@ init_reload (void)
 
   INIT_REG_SET (&spilled_pseudos);
   INIT_REG_SET (&pseudos_counted);
-  VARRAY_RTX_INIT (reg_equiv_memory_loc_varray, 0, "reg_equiv_memory_loc");
 }
 
 /* List of insn chains that are currently unused.  */
@@ -523,29 +537,28 @@ void
 compute_use_by_pseudos (HARD_REG_SET *to, regset from)
 {
   unsigned int regno;
+  reg_set_iterator rsi;
 
-  EXECUTE_IF_SET_IN_REG_SET
-    (from, FIRST_PSEUDO_REGISTER, regno,
-     {
-       int r = reg_renumber[regno];
-       int nregs;
-
-       if (r < 0)
-        {
-          /* reload_combine uses the information from
-             BASIC_BLOCK->global_live_at_start, which might still
-             contain registers that have not actually been allocated
-             since they have an equivalence.  */
-          if (! reload_completed)
-            abort ();
-        }
-       else
-        {
-          nregs = hard_regno_nregs[r][PSEUDO_REGNO_MODE (regno)];
-          while (nregs-- > 0)
-            SET_HARD_REG_BIT (*to, r + nregs);
-        }
-     });
+  EXECUTE_IF_SET_IN_REG_SET (from, FIRST_PSEUDO_REGISTER, regno, rsi)
+    {
+      int r = reg_renumber[regno];
+      int nregs;
+
+      if (r < 0)
+       {
+         /* reload_combine uses the information from
+            BASIC_BLOCK->global_live_at_start, which might still
+            contain registers that have not actually been allocated
+            since they have an equivalence.  */
+         gcc_assert (reload_completed);
+       }
+      else
+       {
+         nregs = hard_regno_nregs[r][PSEUDO_REGNO_MODE (regno)];
+         while (nregs-- > 0)
+           SET_HARD_REG_BIT (*to, r + nregs);
+       }
+    }
 }
 
 /* Replace all pseudos found in LOC with their corresponding
@@ -584,11 +597,12 @@ replace_pseudos_in (rtx *loc, enum machine_mode mem_mode, rtx usage)
        *loc = reg_equiv_mem[regno];
       else if (reg_equiv_address[regno])
        *loc = gen_rtx_MEM (GET_MODE (x), reg_equiv_address[regno]);
-      else if (!REG_P (regno_reg_rtx[regno])
-              || REGNO (regno_reg_rtx[regno]) != regno)
-       *loc = regno_reg_rtx[regno];
       else
-       abort ();
+       {
+         gcc_assert (!REG_P (regno_reg_rtx[regno])
+                     || REGNO (regno_reg_rtx[regno]) != regno);
+         *loc = regno_reg_rtx[regno];
+       }
 
       return;
     }
@@ -614,7 +628,7 @@ replace_pseudos_in (rtx *loc, enum machine_mode mem_mode, rtx usage)
 /* Set during calculate_needs if an insn needs register elimination.  */
 static int something_needs_elimination;
 /* Set during calculate_needs if an insn needs an operand changed.  */
-int something_needs_operands_changed;
+static int something_needs_operands_changed;
 
 /* Nonzero means we couldn't get enough spill regs.  */
 static int failure;
@@ -681,17 +695,6 @@ reload (rtx first, int global)
       if (! call_used_regs[i] && ! fixed_regs[i] && ! LOCAL_REGNO (i))
        regs_ever_live[i] = 1;
 
-#ifdef NON_SAVING_SETJMP
-  /* A function that calls setjmp should save and restore all the
-     call-saved registers on a system where longjmp clobbers them.  */
-  if (NON_SAVING_SETJMP && current_function_calls_setjmp)
-    {
-      for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
-       if (! call_used_regs[i])
-         regs_ever_live[i] = 1;
-    }
-#endif
-
   /* Find all the pseudo registers that didn't get hard regs
      but do have known equivalent constants or memory slots.
      These include parameters (known equivalent to parameter slots)
@@ -702,15 +705,16 @@ reload (rtx first, int global)
      Record memory equivalents in reg_mem_equiv so they can
      be substituted eventually by altering the REG-rtx's.  */
 
-  reg_equiv_constant = xcalloc (max_regno, sizeof (rtx));
-  reg_equiv_mem = xcalloc (max_regno, sizeof (rtx));
-  reg_equiv_init = xcalloc (max_regno, sizeof (rtx));
-  reg_equiv_address = xcalloc (max_regno, sizeof (rtx));
-  reg_max_ref_width = xcalloc (max_regno, sizeof (int));
-  reg_old_renumber = xcalloc (max_regno, sizeof (short));
+  reg_equiv_constant = XCNEWVEC (rtx, max_regno);
+  reg_equiv_invariant = XCNEWVEC (rtx, max_regno);
+  reg_equiv_mem = XCNEWVEC (rtx, max_regno);
+  reg_equiv_alt_mem_list = XCNEWVEC (rtx, max_regno);
+  reg_equiv_address = XCNEWVEC (rtx, max_regno);
+  reg_max_ref_width = XCNEWVEC (unsigned int, max_regno);
+  reg_old_renumber = XCNEWVEC (short, max_regno);
   memcpy (reg_old_renumber, reg_renumber, max_regno * sizeof (short));
-  pseudo_forbidden_regs = xmalloc (max_regno * sizeof (HARD_REG_SET));
-  pseudo_previous_regs = xcalloc (max_regno, sizeof (HARD_REG_SET));
+  pseudo_forbidden_regs = XNEWVEC (HARD_REG_SET, max_regno);
+  pseudo_previous_regs = XCNEWVEC (HARD_REG_SET, max_regno);
 
   CLEAR_HARD_REG_SET (bad_spill_regs_global);
 
@@ -730,89 +734,87 @@ reload (rtx first, int global)
          && GET_MODE (insn) != VOIDmode)
        PUT_MODE (insn, VOIDmode);
 
+      if (INSN_P (insn))
+       scan_paradoxical_subregs (PATTERN (insn));
+
       if (set != 0 && REG_P (SET_DEST (set)))
        {
          rtx note = find_reg_note (insn, REG_EQUIV, NULL_RTX);
-         if (note
-             && (! function_invariant_p (XEXP (note, 0))
-                 || ! flag_pic
-                 /* A function invariant is often CONSTANT_P but may
-                    include a register.  We promise to only pass
-                    CONSTANT_P objects to LEGITIMATE_PIC_OPERAND_P.  */
-                 || (CONSTANT_P (XEXP (note, 0))
-                     && LEGITIMATE_PIC_OPERAND_P (XEXP (note, 0)))))
+         rtx x;
+
+         if (! note)
+           continue;
+
+         i = REGNO (SET_DEST (set));
+         x = XEXP (note, 0);
+
+         if (i <= LAST_VIRTUAL_REGISTER)
+           continue;
+
+         if (! function_invariant_p (x)
+             || ! flag_pic
+             /* A function invariant is often CONSTANT_P but may
+                include a register.  We promise to only pass
+                CONSTANT_P objects to LEGITIMATE_PIC_OPERAND_P.  */
+             || (CONSTANT_P (x)
+                 && LEGITIMATE_PIC_OPERAND_P (x)))
            {
-             rtx x = XEXP (note, 0);
-             i = REGNO (SET_DEST (set));
-             if (i > LAST_VIRTUAL_REGISTER)
+             /* It can happen that a REG_EQUIV note contains a MEM
+                that is not a legitimate memory operand.  As later
+                stages of reload assume that all addresses found
+                in the reg_equiv_* arrays were originally legitimate,
+                we ignore such REG_EQUIV notes.  */
+             if (memory_operand (x, VOIDmode))
                {
-                 /* It can happen that a REG_EQUIV note contains a MEM
-                    that is not a legitimate memory operand.  As later
-                    stages of reload assume that all addresses found
-                    in the reg_equiv_* arrays were originally legitimate,
-                    we ignore such REG_EQUIV notes.  */
-                 if (memory_operand (x, VOIDmode))
+                 /* Always unshare the equivalence, so we can
+                    substitute into this insn without touching the
+                      equivalence.  */
+                 reg_equiv_memory_loc[i] = copy_rtx (x);
+               }
+             else if (function_invariant_p (x))
+               {
+                 if (GET_CODE (x) == PLUS)
                    {
-                     /* Always unshare the equivalence, so we can
-                        substitute into this insn without touching the
-                        equivalence.  */
-                     reg_equiv_memory_loc[i] = copy_rtx (x);
+                     /* This is PLUS of frame pointer and a constant,
+                        and might be shared.  Unshare it.  */
+                     reg_equiv_invariant[i] = copy_rtx (x);
+                     num_eliminable_invariants++;
                    }
-                 else if (function_invariant_p (x))
+                 else if (x == frame_pointer_rtx || x == arg_pointer_rtx)
                    {
-                     if (GET_CODE (x) == PLUS)
-                       {
-                         /* This is PLUS of frame pointer and a constant,
-                            and might be shared.  Unshare it.  */
-                         reg_equiv_constant[i] = copy_rtx (x);
-                         num_eliminable_invariants++;
-                       }
-                     else if (x == frame_pointer_rtx
-                              || x == arg_pointer_rtx)
-                       {
-                         reg_equiv_constant[i] = x;
-                         num_eliminable_invariants++;
-                       }
-                     else if (LEGITIMATE_CONSTANT_P (x))
-                       reg_equiv_constant[i] = x;
-                     else
-                       {
-                         reg_equiv_memory_loc[i]
-                           = force_const_mem (GET_MODE (SET_DEST (set)), x);
-                         if (!reg_equiv_memory_loc[i])
-                           continue;
-                       }
+                     reg_equiv_invariant[i] = x;
+                     num_eliminable_invariants++;
                    }
+                 else if (LEGITIMATE_CONSTANT_P (x))
+                   reg_equiv_constant[i] = x;
                  else
-                   continue;
-
-                 /* If this register is being made equivalent to a MEM
-                    and the MEM is not SET_SRC, the equivalencing insn
-                    is one with the MEM as a SET_DEST and it occurs later.
-                    So don't mark this insn now.  */
-                 if (!MEM_P (x)
-                     || rtx_equal_p (SET_SRC (set), x))
-                   reg_equiv_init[i]
-                     = gen_rtx_INSN_LIST (VOIDmode, insn, reg_equiv_init[i]);
+                   {
+                     reg_equiv_memory_loc[i]
+                       = force_const_mem (GET_MODE (SET_DEST (set)), x);
+                     if (! reg_equiv_memory_loc[i])
+                       reg_equiv_init[i] = NULL_RTX;
+                   }
+               }
+             else
+               {
+                 reg_equiv_init[i] = NULL_RTX;
+                 continue;
                }
            }
+         else
+           reg_equiv_init[i] = NULL_RTX;
        }
-
-      /* If this insn is setting a MEM from a register equivalent to it,
-        this is the equivalencing insn.  */
-      else if (set && MEM_P (SET_DEST (set))
-              && REG_P (SET_SRC (set))
-              && reg_equiv_memory_loc[REGNO (SET_SRC (set))]
-              && rtx_equal_p (SET_DEST (set),
-                              reg_equiv_memory_loc[REGNO (SET_SRC (set))]))
-       reg_equiv_init[REGNO (SET_SRC (set))]
-         = gen_rtx_INSN_LIST (VOIDmode, insn,
-                              reg_equiv_init[REGNO (SET_SRC (set))]);
-
-      if (INSN_P (insn))
-       scan_paradoxical_subregs (PATTERN (insn));
     }
 
+  if (dump_file)
+    for (i = FIRST_PSEUDO_REGISTER; i < max_regno; i++)
+      if (reg_equiv_init[i])
+       {
+         fprintf (dump_file, "init_insns for %u: ", i);
+         print_inline_rtx (dump_file, reg_equiv_init[i], 20);
+         fprintf (dump_file, "\n");
+       }
+
   init_elim_table ();
 
   first_label_num = get_first_label_num ();
@@ -822,8 +824,8 @@ reload (rtx first, int global)
   /* We used to use alloca here, but the size of what it would try to
      allocate would occasionally cause it to exceed the stack limit and
      cause a core dump.  */
-  offsets_known_at = xmalloc (num_labels);
-  offsets_at = xmalloc (num_labels * NUM_ELIMINABLE_REGS * sizeof (HOST_WIDE_INT));
+  offsets_known_at = XNEWVEC (char, num_labels);
+  offsets_at = (HOST_WIDE_INT (*)[NUM_ELIMINABLE_REGS]) xmalloc (num_labels * NUM_ELIMINABLE_REGS * sizeof (HOST_WIDE_INT));
 
   /* Alter each pseudo-reg rtx to contain its hard reg number.
      Assign stack slots to the pseudos that lack hard regs or equivalents.
@@ -982,10 +984,19 @@ reload (rtx first, int global)
       if (starting_frame_size != get_frame_size ())
        something_changed = 1;
 
+      /* Even if the frame size remained the same, we might still have
+        changed elimination offsets, e.g. if find_reloads called 
+        force_const_mem requiring the back end to allocate a constant
+        pool base register that needs to be saved on the stack.  */
+      else if (!verify_initial_elim_offsets ())
+       something_changed = 1;
+
       {
        HARD_REG_SET to_spill;
        CLEAR_HARD_REG_SET (to_spill);
        update_eliminables (&to_spill);
+       AND_COMPL_HARD_REG_SET(used_spill_regs, to_spill);
+
        for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
          if (TEST_HARD_REG_BIT (to_spill, i))
            {
@@ -1047,7 +1058,7 @@ reload (rtx first, int global)
              /* If we already deleted the insn or if it may trap, we can't
                 delete it.  The latter case shouldn't happen, but can
                 if an insn has a variable address, gets a REG_EH_REGION
-                note added to it, and then gets converted into an load
+                note added to it, and then gets converted into a load
                 from a constant address.  */
              if (NOTE_P (equiv_insn)
                  || can_throw_internal (equiv_insn))
@@ -1071,11 +1082,9 @@ reload (rtx first, int global)
 
       reload_as_needed (global);
 
-      if (old_frame_size != get_frame_size ())
-       abort ();
+      gcc_assert (old_frame_size == get_frame_size ());
 
-      if (num_eliminable)
-       verify_initial_elim_offsets ();
+      gcc_assert (verify_initial_elim_offsets ());
     }
 
   /* If we were able to eliminate the frame pointer, show that it is no
@@ -1086,11 +1095,11 @@ reload (rtx first, int global)
 
   if (! frame_pointer_needed)
     FOR_EACH_BB (bb)
-      CLEAR_REGNO_REG_SET (bb->global_live_at_start,
+      CLEAR_REGNO_REG_SET (bb->il.rtl->global_live_at_start,
                           HARD_FRAME_POINTER_REGNUM);
 
-  /* Come here (with failure set nonzero) if we can't get enough spill regs
-     and we decide not to abort about it.  */
+  /* Come here (with failure set nonzero) if we can't get enough spill
+     regs.  */
  failed:
 
   CLEAR_REG_SET (&spilled_pseudos);
@@ -1132,6 +1141,7 @@ reload (rtx first, int global)
                  MEM_IN_STRUCT_P (reg) = MEM_SCALAR_P (reg) = 0;
                  MEM_ATTRS (reg) = 0;
                }
+             MEM_NOTRAP_P (reg) = 1;
            }
          else if (reg_equiv_mem[i])
            XEXP (reg_equiv_mem[i], 0) = addr;
@@ -1157,6 +1167,20 @@ reload (rtx first, int global)
       {
        rtx *pnote;
 
+       /* Clean up invalid ASMs so that they don't confuse later passes.
+          See PR 21299.  */
+       if (asm_noperands (PATTERN (insn)) >= 0)
+         {
+           extract_insn (insn);
+           if (!constrain_operands (1))
+             {
+               error_for_asm (insn,
+                              "%<asm%> operand has impossible constraints");
+               delete_insn (insn);
+               continue;
+             }
+         }
+
        if (CALL_P (insn))
          replace_pseudos_in (& CALL_INSN_FUNCTION_USAGE (insn),
                              VOIDmode, CALL_INSN_FUNCTION_USAGE (insn));
@@ -1185,6 +1209,19 @@ reload (rtx first, int global)
          replace_pseudos_in (& XEXP (PATTERN (insn), 0),
                              VOIDmode, PATTERN (insn));
 
+       /* Discard obvious no-ops, even without -O.  This optimization
+          is fast and doesn't interfere with debugging.  */
+       if (NONJUMP_INSN_P (insn)
+           && GET_CODE (PATTERN (insn)) == SET
+           && REG_P (SET_SRC (PATTERN (insn)))
+           && REG_P (SET_DEST (PATTERN (insn)))
+           && (REGNO (SET_SRC (PATTERN (insn)))
+               == REGNO (SET_DEST (PATTERN (insn)))))
+         {
+           delete_insn (insn);
+           continue;
+         }
+
        pnote = &REG_NOTES (insn);
        while (*pnote != 0)
          {
@@ -1219,10 +1256,10 @@ reload (rtx first, int global)
 
       if (size > STACK_CHECK_MAX_FRAME_SIZE)
        {
-         warning ("frame size too large for reliable stack checking");
+         warning (0, "frame size too large for reliable stack checking");
          if (! verbose_warned)
            {
-             warning ("try reducing the number of local variables");
+             warning (0, "try reducing the number of local variables");
              verbose_warned = 1;
            }
        }
@@ -1231,8 +1268,11 @@ reload (rtx first, int global)
   /* Indicate that we no longer have known memory locations or constants.  */
   if (reg_equiv_constant)
     free (reg_equiv_constant);
+  if (reg_equiv_invariant)
+    free (reg_equiv_invariant);
   reg_equiv_constant = 0;
-  VARRAY_GROW (reg_equiv_memory_loc_varray, 0);
+  reg_equiv_invariant = 0;
+  VEC_free (rtx, gc, reg_equiv_memory_loc_vec);
   reg_equiv_memory_loc = 0;
 
   if (offsets_known_at)
@@ -1240,8 +1280,13 @@ reload (rtx first, int global)
   if (offsets_at)
     free (offsets_at);
 
+  for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+    if (reg_equiv_alt_mem_list[i])
+      free_EXPR_LIST_list (&reg_equiv_alt_mem_list[i]);
+  free (reg_equiv_alt_mem_list);
+
   free (reg_equiv_mem);
-  free (reg_equiv_init);
+  reg_equiv_init = 0;
   free (reg_equiv_address);
   free (reg_max_ref_width);
   free (reg_old_renumber);
@@ -1357,7 +1402,7 @@ maybe_fix_stack_asms (void)
 
                case 'p':
                  cls = (int) reg_class_subunion[cls]
-                   [(int) MODE_BASE_REG_CLASS (VOIDmode)];
+                     [(int) base_reg_class (VOIDmode, ADDRESS, SCRATCH)];
                  break;
 
                case 'g':
@@ -1368,7 +1413,7 @@ maybe_fix_stack_asms (void)
                default:
                  if (EXTRA_ADDRESS_CONSTRAINT (c, p))
                    cls = (int) reg_class_subunion[cls]
-                     [(int) MODE_BASE_REG_CLASS (VOIDmode)];
+                     [(int) base_reg_class (VOIDmode, ADDRESS, SCRATCH)];
                  else
                    cls = (int) reg_class_subunion[cls]
                      [(int) REG_CLASS_FROM_CONSTRAINT (c, p)];
@@ -1447,7 +1492,9 @@ calculate_needs_all_insns (int global)
          /* Skip insns that only set an equivalence.  */
          if (set && REG_P (SET_DEST (set))
              && reg_renumber[REGNO (SET_DEST (set))] < 0
-             && reg_equiv_constant[REGNO (SET_DEST (set))])
+             && (reg_equiv_constant[REGNO (SET_DEST (set))]
+                 || (reg_equiv_invariant[REGNO (SET_DEST (set))]))
+                     && reg_equiv_init[REGNO (SET_DEST (set))])
            continue;
 
          /* If needed, eliminate any eliminable registers.  */
@@ -1576,8 +1623,7 @@ count_pseudo (int reg)
 
   SET_REGNO_REG_SET (&pseudos_counted, reg);
 
-  if (r < 0)
-    abort ();
+  gcc_assert (r >= 0);
 
   spill_add_cost[r] += freq;
 
@@ -1592,9 +1638,10 @@ count_pseudo (int reg)
 static void
 order_regs_for_reload (struct insn_chain *chain)
 {
-  int i;
+  unsigned i;
   HARD_REG_SET used_by_pseudos;
   HARD_REG_SET used_by_pseudos2;
+  reg_set_iterator rsi;
 
   COPY_HARD_REG_SET (bad_spill_regs, fixed_reg_set);
 
@@ -1615,15 +1662,15 @@ order_regs_for_reload (struct insn_chain *chain)
   CLEAR_REG_SET (&pseudos_counted);
 
   EXECUTE_IF_SET_IN_REG_SET
-    (&chain->live_throughout, FIRST_PSEUDO_REGISTER, i,
-     {
-       count_pseudo (i);
-     });
+    (&chain->live_throughout, FIRST_PSEUDO_REGISTER, i, rsi)
+    {
+      count_pseudo (i);
+    }
   EXECUTE_IF_SET_IN_REG_SET
-    (&chain->dead_or_set, FIRST_PSEUDO_REGISTER, i,
-     {
-       count_pseudo (i);
-     });
+    (&chain->dead_or_set, FIRST_PSEUDO_REGISTER, i, rsi)
+    {
+      count_pseudo (i);
+    }
   CLEAR_REG_SET (&pseudos_counted);
 }
 \f
@@ -1669,6 +1716,7 @@ find_reg (struct insn_chain *chain, int order)
   int k;
   HARD_REG_SET not_usable;
   HARD_REG_SET used_by_other_reload;
+  reg_set_iterator rsi;
 
   COPY_HARD_REG_SET (not_usable, bad_spill_regs);
   IOR_HARD_REG_SET (not_usable, bad_spill_regs_global);
@@ -1737,22 +1785,21 @@ find_reg (struct insn_chain *chain, int order)
   rl->regno = best_reg;
 
   EXECUTE_IF_SET_IN_REG_SET
-    (&chain->live_throughout, FIRST_PSEUDO_REGISTER, j,
-     {
-       count_spilled_pseudo (best_reg, rl->nregs, j);
-     });
+    (&chain->live_throughout, FIRST_PSEUDO_REGISTER, j, rsi)
+    {
+      count_spilled_pseudo (best_reg, rl->nregs, j);
+    }
 
   EXECUTE_IF_SET_IN_REG_SET
-    (&chain->dead_or_set, FIRST_PSEUDO_REGISTER, j,
-     {
-       count_spilled_pseudo (best_reg, rl->nregs, j);
-     });
+    (&chain->dead_or_set, FIRST_PSEUDO_REGISTER, j, rsi)
+    {
+      count_spilled_pseudo (best_reg, rl->nregs, j);
+    }
 
   for (i = 0; i < rl->nregs; i++)
     {
-      if (spill_cost[best_reg + i] != 0
-         || spill_add_cost[best_reg + i] != 0)
-       abort ();
+      gcc_assert (spill_cost[best_reg + i] == 0);
+      gcc_assert (spill_add_cost[best_reg + i] == 0);
       SET_HARD_REG_BIT (used_spill_regs_local, best_reg + i);
     }
   return 1;
@@ -1812,6 +1859,8 @@ find_reload_regs (struct insn_chain *chain)
          && rld[r].regno == -1)
        if (! find_reg (chain, i))
          {
+           if (dump_file)
+             fprintf(dump_file, "reload failure for reload %d\n", r);
            spill_failure (chain->insn, rld[r].class);
            failure = 1;
            return;
@@ -1872,14 +1921,20 @@ delete_caller_save_insns (void)
 static void
 spill_failure (rtx insn, enum reg_class class)
 {
-  static const char *const reg_class_names[] = REG_CLASS_NAMES;
   if (asm_noperands (PATTERN (insn)) >= 0)
-    error_for_asm (insn, "can't find a register in class `%s' while reloading `asm'",
+    error_for_asm (insn, "can't find a register in class %qs while "
+                  "reloading %<asm%>",
                   reg_class_names[class]);
   else
     {
-      error ("unable to find a register to spill in class `%s'",
+      error ("unable to find a register to spill in class %qs",
             reg_class_names[class]);
+
+      if (dump_file)
+       {
+         fprintf (dump_file, "\nReloads for insn # %d\n", INSN_UID (insn));
+         debug_reload_to_stream (dump_file);
+       }
       fatal_insn ("this is the insn:", insn);
     }
 }
@@ -1937,6 +1992,7 @@ alter_reg (int i, int from_reg)
   if (reg_renumber[i] < 0
       && REG_N_REFS (i) > 0
       && reg_equiv_constant[i] == 0
+      && (reg_equiv_invariant[i] == 0 || reg_equiv_init[i] == 0)
       && reg_equiv_memory_loc[i] == 0)
     {
       rtx x;
@@ -2034,7 +2090,7 @@ alter_reg (int i, int from_reg)
       /* If we have a decl for the original register, set it for the
         memory.  If this is a shared MEM, make a copy.  */
       if (REG_EXPR (regno_reg_rtx[i])
-         && TREE_CODE_CLASS (TREE_CODE (REG_EXPR (regno_reg_rtx[i]))) == 'd')
+         && DECL_P (REG_EXPR (regno_reg_rtx[i])))
        {
          rtx decl = DECL_RTL_IF_SET (REG_EXPR (regno_reg_rtx[i]));
 
@@ -2187,7 +2243,7 @@ set_label_offsets (rtx x, rtx insn, int initial_p)
          return;
 
        case LABEL_REF:
-         set_label_offsets (XEXP (SET_SRC (x), 0), insn, initial_p);
+         set_label_offsets (SET_SRC (x), insn, initial_p);
          return;
 
        case IF_THEN_ELSE:
@@ -2245,8 +2301,9 @@ set_label_offsets (rtx x, rtx insn, int initial_p)
    encounter, return the actual location so that find_reloads will do
    the proper thing.  */
 
-rtx
-eliminate_regs (rtx x, enum machine_mode mem_mode, rtx insn)
+static rtx
+eliminate_regs_1 (rtx x, enum machine_mode mem_mode, rtx insn,
+                 bool may_use_invariant)
 {
   enum rtx_code code = GET_CODE (x);
   struct elim_table *ep;
@@ -2289,10 +2346,16 @@ eliminate_regs (rtx x, enum machine_mode mem_mode, rtx insn)
 
        }
       else if (reg_renumber && reg_renumber[regno] < 0
-              && reg_equiv_constant && reg_equiv_constant[regno]
-              && ! CONSTANT_P (reg_equiv_constant[regno]))
-       return eliminate_regs (copy_rtx (reg_equiv_constant[regno]),
-                              mem_mode, insn);
+              && reg_equiv_invariant && reg_equiv_invariant[regno])
+       {
+         if (may_use_invariant)
+           return eliminate_regs_1 (copy_rtx (reg_equiv_invariant[regno]),
+                                    mem_mode, insn, true);
+         /* There exists at least one use of REGNO that cannot be
+            eliminated.  Prevent the defining insn from being deleted.  */
+         reg_equiv_init[regno] = NULL_RTX;
+         alter_reg (regno, -1);
+       }
       return x;
 
     /* You might think handling MINUS in a manner similar to PLUS is a
@@ -2352,8 +2415,8 @@ eliminate_regs (rtx x, enum machine_mode mem_mode, rtx insn)
         operand of a load-address insn.  */
 
       {
-       rtx new0 = eliminate_regs (XEXP (x, 0), mem_mode, insn);
-       rtx new1 = eliminate_regs (XEXP (x, 1), mem_mode, insn);
+       rtx new0 = eliminate_regs_1 (XEXP (x, 0), mem_mode, insn, true);
+       rtx new1 = eliminate_regs_1 (XEXP (x, 1), mem_mode, insn, true);
 
        if (reg_renumber && (new0 != XEXP (x, 0) || new1 != XEXP (x, 1)))
          {
@@ -2425,9 +2488,9 @@ eliminate_regs (rtx x, enum machine_mode mem_mode, rtx insn)
     case GE:       case GT:       case GEU:    case GTU:
     case LE:       case LT:       case LEU:    case LTU:
       {
-       rtx new0 = eliminate_regs (XEXP (x, 0), mem_mode, insn);
-       rtx new1
-         = XEXP (x, 1) ? eliminate_regs (XEXP (x, 1), mem_mode, insn) : 0;
+       rtx new0 = eliminate_regs_1 (XEXP (x, 0), mem_mode, insn, false);
+       rtx new1 = XEXP (x, 1)
+                  ? eliminate_regs_1 (XEXP (x, 1), mem_mode, insn, false) : 0;
 
        if (new0 != XEXP (x, 0) || new1 != XEXP (x, 1))
          return gen_rtx_fmt_ee (code, GET_MODE (x), new0, new1);
@@ -2438,7 +2501,7 @@ eliminate_regs (rtx x, enum machine_mode mem_mode, rtx insn)
       /* If we have something in XEXP (x, 0), the usual case, eliminate it.  */
       if (XEXP (x, 0))
        {
-         new = eliminate_regs (XEXP (x, 0), mem_mode, insn);
+         new = eliminate_regs_1 (XEXP (x, 0), mem_mode, insn, true);
          if (new != XEXP (x, 0))
            {
              /* If this is a REG_DEAD note, it is not valid anymore.
@@ -2446,7 +2509,7 @@ eliminate_regs (rtx x, enum machine_mode mem_mode, rtx insn)
                 REG_DEAD note for the stack or frame pointer.  */
              if (GET_MODE (x) == REG_DEAD)
                return (XEXP (x, 1)
-                       ? eliminate_regs (XEXP (x, 1), mem_mode, insn)
+                       ? eliminate_regs_1 (XEXP (x, 1), mem_mode, insn, true)
                        : NULL_RTX);
 
              x = gen_rtx_EXPR_LIST (REG_NOTE_KIND (x), new, XEXP (x, 1));
@@ -2461,7 +2524,7 @@ eliminate_regs (rtx x, enum machine_mode mem_mode, rtx insn)
         strictly needed, but it simplifies the code.  */
       if (XEXP (x, 1))
        {
-         new = eliminate_regs (XEXP (x, 1), mem_mode, insn);
+         new = eliminate_regs_1 (XEXP (x, 1), mem_mode, insn, true);
          if (new != XEXP (x, 1))
            return
              gen_rtx_fmt_ee (GET_CODE (x), GET_MODE (x), XEXP (x, 0), new);
@@ -2485,7 +2548,7 @@ eliminate_regs (rtx x, enum machine_mode mem_mode, rtx insn)
     case CTZ:
     case POPCOUNT:
     case PARITY:
-      new = eliminate_regs (XEXP (x, 0), mem_mode, insn);
+      new = eliminate_regs_1 (XEXP (x, 0), mem_mode, insn, false);
       if (new != XEXP (x, 0))
        return gen_rtx_fmt_e (code, GET_MODE (x), new);
       return x;
@@ -2506,7 +2569,7 @@ eliminate_regs (rtx x, enum machine_mode mem_mode, rtx insn)
          new = SUBREG_REG (x);
        }
       else
-       new = eliminate_regs (SUBREG_REG (x), mem_mode, insn);
+       new = eliminate_regs_1 (SUBREG_REG (x), mem_mode, insn, false);
 
       if (new != SUBREG_REG (x))
        {
@@ -2542,12 +2605,12 @@ eliminate_regs (rtx x, enum machine_mode mem_mode, rtx insn)
         case more efficiently.  */
       return
        replace_equiv_address_nv (x,
-                                 eliminate_regs (XEXP (x, 0),
-                                                 GET_MODE (x), insn));
+                                 eliminate_regs_1 (XEXP (x, 0), GET_MODE (x),
+                                                   insn, true));
 
     case USE:
       /* Handle insn_list USE that a call to a pure function may generate.  */
-      new = eliminate_regs (XEXP (x, 0), 0, insn);
+      new = eliminate_regs_1 (XEXP (x, 0), 0, insn, false);
       if (new != XEXP (x, 0))
        return gen_rtx_USE (GET_MODE (x), new);
       return x;
@@ -2555,7 +2618,7 @@ eliminate_regs (rtx x, enum machine_mode mem_mode, rtx insn)
     case CLOBBER:
     case ASM_OPERANDS:
     case SET:
-      abort ();
+      gcc_unreachable ();
 
     default:
       break;
@@ -2568,12 +2631,10 @@ eliminate_regs (rtx x, enum machine_mode mem_mode, rtx insn)
     {
       if (*fmt == 'e')
        {
-         new = eliminate_regs (XEXP (x, i), mem_mode, insn);
+         new = eliminate_regs_1 (XEXP (x, i), mem_mode, insn, false);
          if (new != XEXP (x, i) && ! copied)
            {
-             rtx new_x = rtx_alloc (code);
-             memcpy (new_x, x, RTX_SIZE (code));
-             x = new_x;
+             x = shallow_copy_rtx (x);
              copied = 1;
            }
          XEXP (x, i) = new;
@@ -2583,16 +2644,14 @@ eliminate_regs (rtx x, enum machine_mode mem_mode, rtx insn)
          int copied_vec = 0;
          for (j = 0; j < XVECLEN (x, i); j++)
            {
-             new = eliminate_regs (XVECEXP (x, i, j), mem_mode, insn);
+             new = eliminate_regs_1 (XVECEXP (x, i, j), mem_mode, insn, false);
              if (new != XVECEXP (x, i, j) && ! copied_vec)
                {
                  rtvec new_v = gen_rtvec_v (XVECLEN (x, i),
                                             XVEC (x, i)->elem);
                  if (! copied)
                    {
-                     rtx new_x = rtx_alloc (code);
-                     memcpy (new_x, x, RTX_SIZE (code));
-                     x = new_x;
+                     x = shallow_copy_rtx (x);
                      copied = 1;
                    }
                  XVEC (x, i) = new_v;
@@ -2606,6 +2665,12 @@ eliminate_regs (rtx x, enum machine_mode mem_mode, rtx insn)
   return x;
 }
 
+rtx
+eliminate_regs (rtx x, enum machine_mode mem_mode, rtx insn)
+{
+  return eliminate_regs_1 (x, mem_mode, insn, false);
+}
+
 /* Scan rtx X for modifications of elimination target registers.  Update
    the table of eliminables to reflect the changed state.  MEM_MODE is
    the mode of an enclosing MEM rtx, or VOIDmode if not within a MEM.  */
@@ -2863,17 +2928,16 @@ eliminate_regs_in_insn (rtx insn, int replace)
   rtx substed_operand[MAX_RECOG_OPERANDS];
   rtx orig_operand[MAX_RECOG_OPERANDS];
   struct elim_table *ep;
-  rtx plus_src;
+  rtx plus_src, plus_cst_src;
 
   if (! insn_is_asm && icode < 0)
     {
-      if (GET_CODE (PATTERN (insn)) == USE
-         || GET_CODE (PATTERN (insn)) == CLOBBER
-         || GET_CODE (PATTERN (insn)) == ADDR_VEC
-         || GET_CODE (PATTERN (insn)) == ADDR_DIFF_VEC
-         || GET_CODE (PATTERN (insn)) == ASM_INPUT)
-       return 0;
-      abort ();
+      gcc_assert (GET_CODE (PATTERN (insn)) == USE
+                 || GET_CODE (PATTERN (insn)) == CLOBBER
+                 || GET_CODE (PATTERN (insn)) == ADDR_VEC
+                 || GET_CODE (PATTERN (insn)) == ADDR_DIFF_VEC
+                 || GET_CODE (PATTERN (insn)) == ASM_INPUT);
+      return 0;
     }
 
   if (old_set != 0 && REG_P (SET_DEST (old_set))
@@ -2969,44 +3033,62 @@ eliminate_regs_in_insn (rtx insn, int replace)
   /* We allow one special case which happens to work on all machines we
      currently support: a single set with the source or a REG_EQUAL
      note being a PLUS of an eliminable register and a constant.  */
-  plus_src = 0;
+  plus_src = plus_cst_src = 0;
   if (old_set && REG_P (SET_DEST (old_set)))
     {
-      /* First see if the source is of the form (plus (reg) CST).  */
-      if (GET_CODE (SET_SRC (old_set)) == PLUS
-         && REG_P (XEXP (SET_SRC (old_set), 0))
-         && GET_CODE (XEXP (SET_SRC (old_set), 1)) == CONST_INT
-         && REGNO (XEXP (SET_SRC (old_set), 0)) < FIRST_PSEUDO_REGISTER)
+      if (GET_CODE (SET_SRC (old_set)) == PLUS)
        plus_src = SET_SRC (old_set);
-      else if (REG_P (SET_SRC (old_set)))
+      /* First see if the source is of the form (plus (...) CST).  */
+      if (plus_src
+         && GET_CODE (XEXP (plus_src, 1)) == CONST_INT)
+       plus_cst_src = plus_src;
+      else if (REG_P (SET_SRC (old_set))
+              || plus_src)
        {
          /* Otherwise, see if we have a REG_EQUAL note of the form
-            (plus (reg) CST).  */
+            (plus (...) CST).  */
          rtx links;
          for (links = REG_NOTES (insn); links; links = XEXP (links, 1))
            {
              if (REG_NOTE_KIND (links) == REG_EQUAL
                  && GET_CODE (XEXP (links, 0)) == PLUS
-                 && REG_P (XEXP (XEXP (links, 0), 0))
-                 && GET_CODE (XEXP (XEXP (links, 0), 1)) == CONST_INT
-                 && REGNO (XEXP (XEXP (links, 0), 0)) < FIRST_PSEUDO_REGISTER)
+                 && GET_CODE (XEXP (XEXP (links, 0), 1)) == CONST_INT)
                {
-                 plus_src = XEXP (links, 0);
+                 plus_cst_src = XEXP (links, 0);
                  break;
                }
            }
        }
+
+      /* Check that the first operand of the PLUS is a hard reg or
+        the lowpart subreg of one.  */
+      if (plus_cst_src)
+       {
+         rtx reg = XEXP (plus_cst_src, 0);
+         if (GET_CODE (reg) == SUBREG && subreg_lowpart_p (reg))
+           reg = SUBREG_REG (reg);
+
+         if (!REG_P (reg) || REGNO (reg) >= FIRST_PSEUDO_REGISTER)
+           plus_cst_src = 0;
+       }
     }
-  if (plus_src)
+  if (plus_cst_src)
     {
-      rtx reg = XEXP (plus_src, 0);
-      HOST_WIDE_INT offset = INTVAL (XEXP (plus_src, 1));
+      rtx reg = XEXP (plus_cst_src, 0);
+      HOST_WIDE_INT offset = INTVAL (XEXP (plus_cst_src, 1));
+
+      if (GET_CODE (reg) == SUBREG)
+       reg = SUBREG_REG (reg);
 
       for (ep = reg_eliminate; ep < &reg_eliminate[NUM_ELIMINABLE_REGS]; ep++)
        if (ep->from_rtx == reg && ep->can_eliminate)
          {
+           rtx to_rtx = ep->to_rtx;
            offset += ep->offset;
 
+           if (GET_CODE (XEXP (plus_cst_src, 0)) == SUBREG)
+             to_rtx = gen_lowpart (GET_MODE (XEXP (plus_cst_src, 0)),
+                                   to_rtx);
            if (offset == 0)
              {
                int num_clobbers;
@@ -3016,7 +3098,7 @@ eliminate_regs_in_insn (rtx insn, int replace)
                   There's not much we can do if that doesn't work.  */
                PATTERN (insn) = gen_rtx_SET (VOIDmode,
                                              SET_DEST (old_set),
-                                             ep->to_rtx);
+                                             to_rtx);
                num_clobbers = 0;
                INSN_CODE (insn) = recog (PATTERN (insn), insn, &num_clobbers);
                if (num_clobbers)
@@ -3027,15 +3109,14 @@ eliminate_regs_in_insn (rtx insn, int replace)
                    PATTERN (insn) = gen_rtx_PARALLEL (VOIDmode, vec);
                    add_clobbers (PATTERN (insn), INSN_CODE (insn));
                  }
-               if (INSN_CODE (insn) < 0)
-                 abort ();
+               gcc_assert (INSN_CODE (insn) >= 0);
              }
            /* If we have a nonzero offset, and the source is already
               a simple REG, the following transformation would
               increase the cost of the insn by replacing a simple REG
-              with (plus (reg sp) CST).  So try only when plus_src
-              comes from old_set proper, not REG_NOTES.  */
-           else if (SET_SRC (old_set) == plus_src)
+              with (plus (reg sp) CST).  So try only when we already
+              had a PLUS before.  */
+           else if (plus_src)
              {
                new_body = old_body;
                if (! replace)
@@ -3047,7 +3128,7 @@ eliminate_regs_in_insn (rtx insn, int replace)
                PATTERN (insn) = new_body;
                old_set = single_set (insn);
 
-               XEXP (SET_SRC (old_set), 0) = ep->to_rtx;
+               XEXP (SET_SRC (old_set), 0) = to_rtx;
                XEXP (SET_SRC (old_set), 1) = GEN_INT (offset);
              }
            else
@@ -3074,6 +3155,8 @@ eliminate_regs_in_insn (rtx insn, int replace)
       /* For an asm statement, every operand is eliminable.  */
       if (insn_is_asm || insn_data[icode].operand[i].eliminable)
        {
+         bool is_set_src, in_plus;
+
          /* Check for setting a register that we know about.  */
          if (recog_data.operand_type[i] != OP_IN
              && REG_P (orig_operand[i]))
@@ -3088,8 +3171,21 @@ eliminate_regs_in_insn (rtx insn, int replace)
                  ep->can_eliminate = 0;
            }
 
-         substed_operand[i] = eliminate_regs (recog_data.operand[i], 0,
-                                              replace ? insn : NULL_RTX);
+         /* Companion to the above plus substitution, we can allow
+            invariants as the source of a plain move.  */
+         is_set_src = false;
+         if (old_set && recog_data.operand_loc[i] == &SET_SRC (old_set))
+           is_set_src = true;
+         in_plus = false;
+         if (plus_src
+             && (recog_data.operand_loc[i] == &XEXP (plus_src, 0)
+                 || recog_data.operand_loc[i] == &XEXP (plus_src, 1)))
+           in_plus = true;
+
+         substed_operand[i]
+           = eliminate_regs_1 (recog_data.operand[i], 0,
+                               replace ? insn : NULL_RTX,
+                               is_set_src || in_plus);
          if (substed_operand[i] != orig_operand[i])
            val = 1;
          /* Terminate the search in check_eliminable_occurrences at
@@ -3169,8 +3265,8 @@ eliminate_regs_in_insn (rtx insn, int replace)
              || GET_CODE (SET_SRC (old_set)) == PLUS))
        {
          int new_icode = recog (PATTERN (insn), insn, 0);
-         if (new_icode < 0)
-           INSN_CODE (insn) = icode;
+         if (new_icode >= 0)
+           INSN_CODE (insn) = new_icode;
        }
     }
 
@@ -3217,7 +3313,8 @@ eliminate_regs_in_insn (rtx insn, int replace)
      of spill registers to be needed in the final reload pass than in
      the pre-passes.  */
   if (val && REG_NOTES (insn) != 0)
-    REG_NOTES (insn) = eliminate_regs (REG_NOTES (insn), 0, REG_NOTES (insn));
+    REG_NOTES (insn)
+      = eliminate_regs_1 (REG_NOTES (insn), 0, REG_NOTES (insn), true);
 
   return val;
 }
@@ -3288,25 +3385,32 @@ mark_not_eliminable (rtx dest, rtx x, void *data ATTRIBUTE_UNUSED)
    where something illegal happened during reload_as_needed that could
    cause incorrect code to be generated if we did not check for it.  */
 
-static void
+static bool
 verify_initial_elim_offsets (void)
 {
   HOST_WIDE_INT t;
 
+  if (!num_eliminable)
+    return true;
+
 #ifdef ELIMINABLE_REGS
-  struct elim_table *ep;
+  {
+   struct elim_table *ep;
 
-  for (ep = reg_eliminate; ep < &reg_eliminate[NUM_ELIMINABLE_REGS]; ep++)
-    {
-      INITIAL_ELIMINATION_OFFSET (ep->from, ep->to, t);
-      if (t != ep->initial_offset)
-       abort ();
-    }
+   for (ep = reg_eliminate; ep < &reg_eliminate[NUM_ELIMINABLE_REGS]; ep++)
+     {
+       INITIAL_ELIMINATION_OFFSET (ep->from, ep->to, t);
+       if (t != ep->initial_offset)
+        return false;
+     }
+  }
 #else
   INITIAL_FRAME_POINTER_OFFSET (t);
   if (t != reg_eliminate[0].initial_offset)
-    abort ();
+    return false;
 #endif
+
+  return true;
 }
 
 /* Reset all offsets on eliminable registers to their initial values.  */
@@ -3330,6 +3434,14 @@ set_initial_elim_offsets (void)
   num_not_at_initial_offset = 0;
 }
 
+/* Subroutine of set_initial_label_offsets called via for_each_eh_label.  */
+
+static void
+set_initial_eh_label_offset (rtx label)
+{
+  set_label_offsets (label, NULL_RTX, 1);
+}
+
 /* Initialize the known label offsets.
    Set a known offset for each forced label to be at the initial offset
    of each elimination.  We do this because we assume that all
@@ -3346,6 +3458,8 @@ set_initial_label_offsets (void)
   for (x = forced_labels; x; x = XEXP (x, 1))
     if (XEXP (x, 0))
       set_label_offsets (XEXP (x, 0), NULL_RTX, 1);
+
+  for_each_eh_label (set_initial_eh_label_offset);
 }
 
 /* Set all elimination offsets to the known values for the code label given
@@ -3471,6 +3585,7 @@ init_elim_table (void)
                             sp-adjusting insns for this case.  */
                          || (current_function_calls_alloca
                              && EXIT_IGNORE_STACK)
+                         || current_function_accesses_prior_frames
                          || FRAME_POINTER_REQUIRED);
 
   num_eliminable = 0;
@@ -3538,15 +3653,6 @@ spill_hard_reg (unsigned int regno, int cant_eliminate)
       SET_REGNO_REG_SET (&spilled_pseudos, i);
 }
 
-/* I'm getting weird preprocessor errors if I use IOR_HARD_REG_SET
-   from within EXECUTE_IF_SET_IN_REG_SET.  Hence this awkwardness.  */
-
-static void
-ior_hard_reg_set (HARD_REG_SET *set1, HARD_REG_SET *set2)
-{
-  IOR_HARD_REG_SET (*set1, *set2);
-}
-
 /* After find_reload_regs has been run for all insn that need reloads,
    and/or spill_hard_regs was called, this function is used to actually
    spill pseudo registers and try to reallocate them.  It also sets up the
@@ -3557,7 +3663,8 @@ finish_spills (int global)
 {
   struct insn_chain *chain;
   int something_changed = 0;
-  int i;
+  unsigned i;
+  reg_set_iterator rsi;
 
   /* Build the spill_regs array for the function.  */
   /* If there are some registers still to eliminate and one of the spill regs
@@ -3584,21 +3691,19 @@ finish_spills (int global)
     else
       spill_reg_order[i] = -1;
 
-  EXECUTE_IF_SET_IN_REG_SET
-    (&spilled_pseudos, FIRST_PSEUDO_REGISTER, i,
-     {
-       /* Record the current hard register the pseudo is allocated to in
-         pseudo_previous_regs so we avoid reallocating it to the same
-         hard reg in a later pass.  */
-       if (reg_renumber[i] < 0)
-        abort ();
-
-       SET_HARD_REG_BIT (pseudo_previous_regs[i], reg_renumber[i]);
-       /* Mark it as no longer having a hard register home.  */
-       reg_renumber[i] = -1;
-       /* We will need to scan everything again.  */
-       something_changed = 1;
-     });
+  EXECUTE_IF_SET_IN_REG_SET (&spilled_pseudos, FIRST_PSEUDO_REGISTER, i, rsi)
+    {
+      /* Record the current hard register the pseudo is allocated to in
+        pseudo_previous_regs so we avoid reallocating it to the same
+        hard reg in a later pass.  */
+      gcc_assert (reg_renumber[i] >= 0);
+
+      SET_HARD_REG_BIT (pseudo_previous_regs[i], reg_renumber[i]);
+      /* Mark it as no longer having a hard register home.  */
+      reg_renumber[i] = -1;
+      /* We will need to scan everything again.  */
+      something_changed = 1;
+    }
 
   /* Retry global register allocation if possible.  */
   if (global)
@@ -3610,17 +3715,17 @@ finish_spills (int global)
       for (chain = insns_need_reload; chain; chain = chain->next_need_reload)
        {
          EXECUTE_IF_SET_IN_REG_SET
-           (&chain->live_throughout, FIRST_PSEUDO_REGISTER, i,
-            {
-              ior_hard_reg_set (pseudo_forbidden_regs + i,
-                                &chain->used_spill_regs);
-            });
+           (&chain->live_throughout, FIRST_PSEUDO_REGISTER, i, rsi)
+           {
+             IOR_HARD_REG_SET (pseudo_forbidden_regs[i],
+                               chain->used_spill_regs);
+           }
          EXECUTE_IF_SET_IN_REG_SET
-           (&chain->dead_or_set, FIRST_PSEUDO_REGISTER, i,
-            {
-              ior_hard_reg_set (pseudo_forbidden_regs + i,
-                                &chain->used_spill_regs);
-            });
+           (&chain->dead_or_set, FIRST_PSEUDO_REGISTER, i, rsi)
+           {
+             IOR_HARD_REG_SET (pseudo_forbidden_regs[i],
+                               chain->used_spill_regs);
+           }
        }
 
       /* Retry allocating the spilled pseudos.  For each reg, merge the
@@ -3628,7 +3733,7 @@ finish_spills (int global)
         and call retry_global_alloc.
         We change spill_pseudos here to only contain pseudos that did not
         get a new hard register.  */
-      for (i = FIRST_PSEUDO_REGISTER; i < max_regno; i++)
+      for (i = FIRST_PSEUDO_REGISTER; i < (unsigned)max_regno; i++)
        if (reg_old_renumber[i] != reg_renumber[i])
          {
            HARD_REG_SET forbidden;
@@ -3670,13 +3775,13 @@ finish_spills (int global)
 
          /* Make sure we only enlarge the set.  */
          GO_IF_HARD_REG_SUBSET (used_by_pseudos2, chain->used_spill_regs, ok);
-         abort ();
+         gcc_unreachable ();
        ok:;
        }
     }
 
   /* Let alter_reg modify the reg rtx's for the modified pseudos.  */
-  for (i = FIRST_PSEUDO_REGISTER; i < max_regno; i++)
+  for (i = FIRST_PSEUDO_REGISTER; i < (unsigned)max_regno; i++)
     {
       int regno = reg_renumber[i];
       if (reg_old_renumber[i] == regno)
@@ -3746,6 +3851,37 @@ scan_paradoxical_subregs (rtx x)
     }
 }
 \f
+/* A subroutine of reload_as_needed.  If INSN has a REG_EH_REGION note,
+   examine all of the reload insns between PREV and NEXT exclusive, and
+   annotate all that may trap.  */
+
+static void
+fixup_eh_region_note (rtx insn, rtx prev, rtx next)
+{
+  rtx note = find_reg_note (insn, REG_EH_REGION, NULL_RTX);
+  unsigned int trap_count;
+  rtx i;
+
+  if (note == NULL)
+    return;
+
+  if (may_trap_p (PATTERN (insn)))
+    trap_count = 1;
+  else
+    {
+      remove_note (insn, note);
+      trap_count = 0;
+    }
+
+  for (i = NEXT_INSN (prev); i != next; i = NEXT_INSN (i))
+    if (INSN_P (i) && i != insn && may_trap_p (PATTERN (i)))
+      {
+       trap_count++;
+       REG_NOTES (i)
+         = gen_rtx_EXPR_LIST (REG_EH_REGION, XEXP (note, 0), REG_NOTES (i));
+      }
+}
+
 /* Reload pseudo-registers into hard regs around each insn as needed.
    Additional register load insns are output before the insn that needs it
    and perhaps store insns after insns that modify the reloaded pseudo reg.
@@ -3766,8 +3902,8 @@ reload_as_needed (int live_known)
 
   memset (spill_reg_rtx, 0, sizeof spill_reg_rtx);
   memset (spill_reg_store, 0, sizeof spill_reg_store);
-  reg_last_reload_reg = xcalloc (max_regno, sizeof (rtx));
-  reg_has_output_reload = xmalloc (max_regno);
+  reg_last_reload_reg = XCNEWVEC (rtx, max_regno);
+  INIT_REG_SET (&reg_has_output_reload);
   CLEAR_HARD_REG_SET (reg_reloaded_valid);
   CLEAR_HARD_REG_SET (reg_reloaded_call_part_clobbered);
 
@@ -3786,7 +3922,9 @@ reload_as_needed (int live_known)
 
       else if (INSN_P (insn))
        {
-         rtx oldpat = copy_rtx (PATTERN (insn));
+         regset_head regs_to_forget;
+         INIT_REG_SET (&regs_to_forget);
+         note_stores (PATTERN (insn), forget_old_reloads_1, &regs_to_forget);
 
          /* If this is a USE and CLOBBER of a MEM, ensure that any
             references to eliminable registers have been removed.  */
@@ -3807,6 +3945,7 @@ reload_as_needed (int live_known)
              if (NOTE_P (insn))
                {
                  update_eliminable_offsets ();
+                 CLEAR_REG_SET (&regs_to_forget);
                  continue;
                }
            }
@@ -3827,7 +3966,7 @@ reload_as_needed (int live_known)
             rtx's for those pseudo regs.  */
          else
            {
-             memset (reg_has_output_reload, 0, max_regno);
+             CLEAR_REG_SET (&reg_has_output_reload);
              CLEAR_HARD_REG_SET (reg_is_output_reload);
 
              find_reloads (insn, 1, spill_indirect_levels, live_known,
@@ -3863,10 +4002,13 @@ reload_as_needed (int live_known)
                 and that we moved the structure into).  */
              subst_reloads (insn);
 
+             /* Adjust the exception region notes for loads and stores.  */
+             if (flag_non_call_exceptions && !CALL_P (insn))
+               fixup_eh_region_note (insn, prev, next);
+
              /* If this was an ASM, make sure that all the reload insns
                 we have generated are valid.  If not, give an error
                 and delete them.  */
-
              if (asm_noperands (PATTERN (insn)) >= 0)
                for (p = NEXT_INSN (prev); p != next; p = NEXT_INSN (p))
                  if (p != insn && INSN_P (p)
@@ -3875,7 +4017,8 @@ reload_as_needed (int live_known)
                          || (extract_insn (p), ! constrain_operands (1))))
                    {
                      error_for_asm (insn,
-                                    "`asm' operand requires impossible reload");
+                                    "%<asm%> operand requires "
+                                    "impossible reload");
                      delete_insn (p);
                    }
            }
@@ -3889,7 +4032,8 @@ reload_as_needed (int live_known)
             for this insn in order to be stored in
             (obeying register constraints).  That is correct; such reload
             registers ARE still valid.  */
-         note_stores (oldpat, forget_old_reloads_1, NULL);
+         forget_marked_reloads (&regs_to_forget);
+         CLEAR_REG_SET (&regs_to_forget);
 
          /* There may have been CLOBBER insns placed after INSN.  So scan
             between INSN and NEXT and use them to forget old reloads.  */
@@ -3976,7 +4120,8 @@ reload_as_needed (int live_known)
                             the reload for inheritance.  */
                          SET_HARD_REG_BIT (reg_is_output_reload,
                                            REGNO (reload_reg));
-                         reg_has_output_reload[REGNO (XEXP (in_reg, 0))] = 1;
+                         SET_REGNO_REG_SET (&reg_has_output_reload,
+                                            REGNO (XEXP (in_reg, 0)));
                        }
                      else
                        forget_old_reloads_1 (XEXP (in_reg, 0), NULL_RTX,
@@ -3992,7 +4137,8 @@ reload_as_needed (int live_known)
                    {
                      SET_HARD_REG_BIT (reg_is_output_reload,
                                        REGNO (rld[i].reg_rtx));
-                     reg_has_output_reload[REGNO (XEXP (in_reg, 0))] = 1;
+                     SET_REGNO_REG_SET (&reg_has_output_reload,
+                                        REGNO (XEXP (in_reg, 0)));
                    }
                }
            }
@@ -4030,7 +4176,7 @@ reload_as_needed (int live_known)
 
   /* Clean up.  */
   free (reg_last_reload_reg);
-  free (reg_has_output_reload);
+  CLEAR_REG_SET (&reg_has_output_reload);
 }
 
 /* Discard all record of any value reloaded from X,
@@ -4038,17 +4184,21 @@ reload_as_needed (int live_known)
    unless X is an output reload reg of the current insn.
 
    X may be a hard reg (the reload reg)
-   or it may be a pseudo reg that was reloaded from.  */
+   or it may be a pseudo reg that was reloaded from.  
+
+   When DATA is non-NULL just mark the registers in regset
+   to be forgotten later.  */
 
 static void
 forget_old_reloads_1 (rtx x, rtx ignored ATTRIBUTE_UNUSED,
-                     void *data ATTRIBUTE_UNUSED)
+                     void *data)
 {
   unsigned int regno;
   unsigned int nr;
+  regset regs = (regset) data;
 
   /* note_stores does give us subregs of hard regs,
-     subreg_regno_offset will abort if it is not a hard reg.  */
+     subreg_regno_offset requires a hard reg.  */
   while (GET_CODE (x) == SUBREG)
     {
       /* We ignore the subreg offset when calculating the regno,
@@ -4073,26 +4223,58 @@ forget_old_reloads_1 (rtx x, rtx ignored ATTRIBUTE_UNUSED,
         This can happen if a block-local pseudo is allocated to that reg
         and it wasn't spilled because this block's total need is 0.
         Then some insn might have an optional reload and use this reg.  */
-      for (i = 0; i < nr; i++)
-       /* But don't do this if the reg actually serves as an output
-          reload reg in the current instruction.  */
+      if (!regs)
+       for (i = 0; i < nr; i++)
+         /* But don't do this if the reg actually serves as an output
+            reload reg in the current instruction.  */
+         if (n_reloads == 0
+             || ! TEST_HARD_REG_BIT (reg_is_output_reload, regno + i))
+           {
+             CLEAR_HARD_REG_BIT (reg_reloaded_valid, regno + i);
+             CLEAR_HARD_REG_BIT (reg_reloaded_call_part_clobbered, regno + i);
+             spill_reg_store[regno + i] = 0;
+           }
+    }
+
+  if (regs)
+    while (nr-- > 0)
+      SET_REGNO_REG_SET (regs, regno + nr);
+  else
+    {
+      /* Since value of X has changed,
+        forget any value previously copied from it.  */
+
+      while (nr-- > 0)
+       /* But don't forget a copy if this is the output reload
+          that establishes the copy's validity.  */
        if (n_reloads == 0
-           || ! TEST_HARD_REG_BIT (reg_is_output_reload, regno + i))
+           || !REGNO_REG_SET_P (&reg_has_output_reload, regno + nr))
+         reg_last_reload_reg[regno + nr] = 0;
+     }
+}
+
+/* Forget the reloads marked in regset by previous function.  */
+static void
+forget_marked_reloads (regset regs)
+{
+  unsigned int reg;
+  reg_set_iterator rsi;
+  EXECUTE_IF_SET_IN_REG_SET (regs, 0, reg, rsi)
+    {
+      if (reg < FIRST_PSEUDO_REGISTER
+         /* But don't do this if the reg actually serves as an output
+            reload reg in the current instruction.  */
+         && (n_reloads == 0
+             || ! TEST_HARD_REG_BIT (reg_is_output_reload, reg)))
          {
-           CLEAR_HARD_REG_BIT (reg_reloaded_valid, regno + i);
-           CLEAR_HARD_REG_BIT (reg_reloaded_call_part_clobbered, regno + i);
-           spill_reg_store[regno + i] = 0;
+           CLEAR_HARD_REG_BIT (reg_reloaded_valid, reg);
+           CLEAR_HARD_REG_BIT (reg_reloaded_call_part_clobbered, reg);
+           spill_reg_store[reg] = 0;
          }
+      if (n_reloads == 0
+         || !REGNO_REG_SET_P (&reg_has_output_reload, reg))
+       reg_last_reload_reg[reg] = 0;
     }
-
-  /* Since value of X has changed,
-     forget any value previously copied from it.  */
-
-  while (nr-- > 0)
-    /* But don't forget a copy if this is the output reload
-       that establishes the copy's validity.  */
-    if (n_reloads == 0 || reg_has_output_reload[regno + nr] == 0)
-      reg_last_reload_reg[regno + nr] = 0;
 }
 \f
 /* The following HARD_REG_SETs indicate when each hard register is
@@ -4265,7 +4447,7 @@ clear_reload_reg_in_use (unsigned int regno, int opnum,
       used_in_set = &reload_reg_used_in_insn;
       break;
     default:
-      abort ();
+      gcc_unreachable ();
     }
   /* We resolve conflicts with remaining reloads of the same type by
      excluding the intervals of reload registers by them from the
@@ -4461,8 +4643,10 @@ reload_reg_free_p (unsigned int regno, int opnum, enum reload_type type)
 
     case RELOAD_FOR_OTHER_ADDRESS:
       return ! TEST_HARD_REG_BIT (reload_reg_used_in_other_addr, regno);
+
+    default:
+      gcc_unreachable ();
     }
-  abort ();
 }
 
 /* Return 1 if the value in reload reg REGNO, as used by a reload
@@ -4594,9 +4778,10 @@ reload_reg_reaches_end_p (unsigned int regno, int opnum, enum reload_type type)
          return 0;
 
       return 1;
-    }
 
-  abort ();
+    default:
+      gcc_unreachable ();
+    }
 }
 \f
 /* Return 1 if the reloads denoted by R1 and R2 cannot share a register.
@@ -4604,7 +4789,7 @@ reload_reg_reaches_end_p (unsigned int regno, int opnum, enum reload_type type)
 
    This function uses the same algorithm as reload_reg_free_p above.  */
 
-int
+static int
 reloads_conflict (int r1, int r2)
 {
   enum reload_type r1_type = rld[r1].when_needed;
@@ -4671,25 +4856,25 @@ reloads_conflict (int r1, int r2)
       return 1;
 
     default:
-      abort ();
+      gcc_unreachable ();
     }
 }
 \f
 /* Indexed by reload number, 1 if incoming value
    inherited from previous insns.  */
-char reload_inherited[MAX_RELOADS];
+static char reload_inherited[MAX_RELOADS];
 
 /* For an inherited reload, this is the insn the reload was inherited from,
    if we know it.  Otherwise, this is 0.  */
-rtx reload_inheritance_insn[MAX_RELOADS];
+static rtx reload_inheritance_insn[MAX_RELOADS];
 
 /* If nonzero, this is a place to get the value of the reload,
    rather than using reload_in.  */
-rtx reload_override_in[MAX_RELOADS];
+static rtx reload_override_in[MAX_RELOADS];
 
 /* For each reload, the hard register number of the register used,
    or -1 if we did not need a register for this reload.  */
-int reload_spill_index[MAX_RELOADS];
+static int reload_spill_index[MAX_RELOADS];
 
 /* Subroutine of free_for_value_p, used to check a single register.
    START_REGNO is the starting regno of the full reload register
@@ -4949,13 +5134,13 @@ free_for_value_p (int regno, enum machine_mode mode, int opnum,
 }
 
 /* Return nonzero if the rtx X is invariant over the current function.  */
-/* ??? Actually, the places where we use this expect exactly what
* is tested here, and not everything that is function invariant.  In
* particular, the frame pointer and arg pointer are special cased;
- * pic_offset_table_rtx is not, and this will cause aborts when we
*             go to spill these things to memory.  */
+/* ??? Actually, the places where we use this expect exactly what is
  tested here, and not everything that is function invariant.  In
  particular, the frame pointer and arg pointer are special cased;
+   pic_offset_table_rtx is not, and we must not spill these things to
  memory.  */
 
-static int
+int
 function_invariant_p (rtx x)
 {
   if (CONSTANT_P (x))
@@ -4995,7 +5180,7 @@ failed_reload (rtx insn, int r)
   /* It's the user's fault; the operand's mode and constraint
      don't match.  Disable this reload so we don't crash in final.  */
   error_for_asm (insn,
-                "`asm' operand constraint incompatible with operand size");
+                "%<asm%> operand constraint incompatible with operand size");
   rld[r].in = 0;
   rld[r].out = 0;
   rld[r].reg_rtx = 0;
@@ -5388,10 +5573,7 @@ choose_reload_regs (struct insn_chain *chain)
                  mode = GET_MODE (rld[r].in_reg);
                }
 #ifdef AUTO_INC_DEC
-             else if ((GET_CODE (rld[r].in_reg) == PRE_INC
-                       || GET_CODE (rld[r].in_reg) == PRE_DEC
-                       || GET_CODE (rld[r].in_reg) == POST_INC
-                       || GET_CODE (rld[r].in_reg) == POST_DEC)
+             else if (GET_RTX_CLASS (GET_CODE (rld[r].in_reg)) == RTX_AUTOINC
                       && REG_P (XEXP (rld[r].in_reg, 0)))
                {
                  regno = REGNO (XEXP (rld[r].in_reg, 0));
@@ -5422,19 +5604,18 @@ choose_reload_regs (struct insn_chain *chain)
                    need_mode = mode;
                  else
                    need_mode
-                     = smallest_mode_for_size (GET_MODE_SIZE (mode) + byte,
+                     = smallest_mode_for_size (GET_MODE_BITSIZE (mode)
+                                               + byte * BITS_PER_UNIT,
                                                GET_MODE_CLASS (mode));
 
-                 if (
-#ifdef CANNOT_CHANGE_MODE_CLASS
-                     (!REG_CANNOT_CHANGE_MODE_P (i, GET_MODE (last_reg),
-                                                 need_mode)
-                      &&
-#endif
-                     (GET_MODE_SIZE (GET_MODE (last_reg))
+                 if ((GET_MODE_SIZE (GET_MODE (last_reg))
                       >= GET_MODE_SIZE (need_mode))
 #ifdef CANNOT_CHANGE_MODE_CLASS
-                     )
+                     /* Verify that the register in "i" can be obtained
+                        from LAST_REG.  */
+                     && !REG_CANNOT_CHANGE_MODE_P (REGNO (last_reg),
+                                                   GET_MODE (last_reg),
+                                                   mode)
 #endif
                      && reg_reloaded_contents[i] == regno
                      && TEST_HARD_REG_BIT (reg_reloaded_valid, i)
@@ -5446,11 +5627,9 @@ choose_reload_regs (struct insn_chain *chain)
                             enough.  */
                          || ((REGISTER_MOVE_COST (mode, last_class, class)
                               < MEMORY_MOVE_COST (mode, class, 1))
-#ifdef SECONDARY_INPUT_RELOAD_CLASS
-                             && (SECONDARY_INPUT_RELOAD_CLASS (class, mode,
-                                                               last_reg)
+                             && (secondary_reload_class (1, class, mode,
+                                                         last_reg)
                                  == NO_REGS)
-#endif
 #ifdef SECONDARY_MEMORY_NEEDED
                              && ! SECONDARY_MEMORY_NEEDED (last_class, class,
                                                            mode)
@@ -5591,17 +5770,25 @@ choose_reload_regs (struct insn_chain *chain)
                {
                  if (REG_P (equiv))
                    regno = REGNO (equiv);
-                 else if (GET_CODE (equiv) == SUBREG)
+                 else
                    {
                      /* This must be a SUBREG of a hard register.
                         Make a new REG since this might be used in an
                         address and not all machines support SUBREGs
                         there.  */
+                     gcc_assert (GET_CODE (equiv) == SUBREG);
                      regno = subreg_regno (equiv);
                      equiv = gen_rtx_REG (rld[r].mode, regno);
+                     /* If we choose EQUIV as the reload register, but the
+                        loop below decides to cancel the inheritance, we'll
+                        end up reloading EQUIV in rld[r].mode, not the mode
+                        it had originally.  That isn't safe when EQUIV isn't
+                        available as a spill register since its value might
+                        still be live at this point.  */
+                     for (i = regno; i < regno + (int) rld[r].nregs; i++)
+                       if (TEST_HARD_REG_BIT (reload_reg_unavailable, i))
+                         equiv = 0;
                    }
-                 else
-                   abort ();
                }
 
              /* If we found a spill reg, reject it unless it is free
@@ -5655,7 +5842,7 @@ choose_reload_regs (struct insn_chain *chain)
 
              if (equiv != 0)
                {
-                 if (regno_clobbered_p (regno, insn, rld[r].mode, 0))
+                 if (regno_clobbered_p (regno, insn, rld[r].mode, 2))
                    switch (rld[r].when_needed)
                      {
                      case RELOAD_FOR_OTHER_ADDRESS:
@@ -5805,15 +5992,13 @@ choose_reload_regs (struct insn_chain *chain)
 
       /* Some sanity tests to verify that the reloads found in the first
         pass are identical to the ones we have now.  */
-      if (chain->n_reloads != n_reloads)
-       abort ();
+      gcc_assert (chain->n_reloads == n_reloads);
 
       for (i = 0; i < n_reloads; i++)
        {
          if (chain->rld[i].regno < 0 || chain->rld[i].reg_rtx != 0)
            continue;
-         if (chain->rld[i].when_needed != rld[i].when_needed)
-           abort ();
+         gcc_assert (chain->rld[i].when_needed == rld[i].when_needed);
          for (j = 0; j < n_spills; j++)
            if (spill_regs[j] == chain->rld[i].regno)
              if (! set_reload_reg (j, i))
@@ -5917,7 +6102,8 @@ choose_reload_regs (struct insn_chain *chain)
            nr = hard_regno_nregs[nregno][rld[r].mode];
 
          while (--nr >= 0)
-           reg_has_output_reload[nregno + nr] = 1;
+           SET_REGNO_REG_SET (&reg_has_output_reload,
+                              nregno + nr);
 
          if (i >= 0)
            {
@@ -5926,10 +6112,9 @@ choose_reload_regs (struct insn_chain *chain)
                SET_HARD_REG_BIT (reg_is_output_reload, i + nr);
            }
 
-         if (rld[r].when_needed != RELOAD_OTHER
-             && rld[r].when_needed != RELOAD_FOR_OUTPUT
-             && rld[r].when_needed != RELOAD_FOR_INSN)
-           abort ();
+         gcc_assert (rld[r].when_needed == RELOAD_OTHER
+                     || rld[r].when_needed == RELOAD_FOR_OUTPUT
+                     || rld[r].when_needed == RELOAD_FOR_INSN);
        }
     }
 }
@@ -6028,6 +6213,8 @@ merge_assigned_reloads (rtx insn)
       if (j == n_reloads
          && max_input_address_opnum <= min_conflicting_input_opnum)
        {
+         gcc_assert (rld[i].when_needed != RELOAD_FOR_OUTPUT);
+
          for (j = 0; j < n_reloads; j++)
            if (i != j && rld[j].reg_rtx != 0
                && rtx_equal_p (rld[i].reg_rtx, rld[j].reg_rtx)
@@ -6046,16 +6233,17 @@ merge_assigned_reloads (rtx insn)
             if they were for inputs, RELOAD_OTHER for outputs.  Note that
             this test is equivalent to looking for reloads for this operand
             number.  */
-         /* We must take special care when there are two or more reloads to
-            be merged and a RELOAD_FOR_OUTPUT_ADDRESS reload that loads the
-            same value or a part of it; we must not change its type if there
-            is a conflicting input.  */
+         /* We must take special care with RELOAD_FOR_OUTPUT_ADDRESS; it may
+            share registers with a RELOAD_FOR_INPUT, so we can not change it
+            to RELOAD_FOR_OTHER_ADDRESS.  We should never need to, since we
+            do not modify RELOAD_FOR_OUTPUT.  */
 
          if (rld[i].when_needed == RELOAD_OTHER)
            for (j = 0; j < n_reloads; j++)
              if (rld[j].in != 0
                  && rld[j].when_needed != RELOAD_OTHER
                  && rld[j].when_needed != RELOAD_FOR_OTHER_ADDRESS
+                 && rld[j].when_needed != RELOAD_FOR_OUTPUT_ADDRESS
                  && (! conflicting_input
                      || rld[j].when_needed == RELOAD_FOR_INPUT_ADDRESS
                      || rld[j].when_needed == RELOAD_FOR_INPADDR_ADDRESS)
@@ -6069,17 +6257,18 @@ merge_assigned_reloads (rtx insn)
                        || rld[j].when_needed == RELOAD_FOR_INPADDR_ADDRESS)
                       ? RELOAD_FOR_OTHER_ADDRESS : RELOAD_OTHER);
 
-                 /* Check to see if we accidentally converted two reloads
-                    that use the same reload register with different inputs
-                    to the same type.  If so, the resulting code won't work,
-                    so abort.  */
+                 /* Check to see if we accidentally converted two
+                    reloads that use the same reload register with
+                    different inputs to the same type.  If so, the
+                    resulting code won't work.  */
                  if (rld[j].reg_rtx)
                    for (k = 0; k < j; k++)
-                     if (rld[k].in != 0 && rld[k].reg_rtx != 0
-                         && rld[k].when_needed == rld[j].when_needed
-                         && rtx_equal_p (rld[k].reg_rtx, rld[j].reg_rtx)
-                         && ! rtx_equal_p (rld[k].in, rld[j].in))
-                       abort ();
+                     gcc_assert (rld[k].in == 0 || rld[k].reg_rtx == 0
+                                 || rld[k].when_needed != rld[j].when_needed
+                                 || !rtx_equal_p (rld[k].reg_rtx,
+                                                  rld[j].reg_rtx)
+                                 || rtx_equal_p (rld[k].in,
+                                                 rld[j].in));
                }
        }
     }
@@ -6102,6 +6291,55 @@ static rtx other_output_reload_insns[MAX_RECOG_OPERANDS];
 static rtx new_spill_reg_store[FIRST_PSEUDO_REGISTER];
 static HARD_REG_SET reg_reloaded_died;
 
+/* Check if *RELOAD_REG is suitable as an intermediate or scratch register
+   of class NEW_CLASS with mode NEW_MODE.  Or alternatively, if alt_reload_reg
+   is nonzero, if that is suitable.  On success, change *RELOAD_REG to the
+   adjusted register, and return true.  Otherwise, return false.  */
+static bool
+reload_adjust_reg_for_temp (rtx *reload_reg, rtx alt_reload_reg,
+                           enum reg_class new_class,
+                           enum machine_mode new_mode)
+
+{
+  rtx reg;
+
+  for (reg = *reload_reg; reg; reg = alt_reload_reg, alt_reload_reg = 0)
+    {
+      unsigned regno = REGNO (reg);
+
+      if (!TEST_HARD_REG_BIT (reg_class_contents[(int) new_class], regno))
+       continue;
+      if (GET_MODE (reg) != new_mode)
+       {
+         if (!HARD_REGNO_MODE_OK (regno, new_mode))
+           continue;
+         if (hard_regno_nregs[regno][new_mode]
+             > hard_regno_nregs[regno][GET_MODE (reg)])
+           continue;
+         reg = reload_adjust_reg_for_mode (reg, new_mode);
+       }
+      *reload_reg = reg;
+      return true;
+    }
+  return false;
+}
+
+/* Check if *RELOAD_REG is suitable as a scratch register for the reload
+   pattern with insn_code ICODE, or alternatively, if alt_reload_reg is
+   nonzero, if that is suitable.  On success, change *RELOAD_REG to the
+   adjusted register, and return true.  Otherwise, return false.  */
+static bool
+reload_adjust_reg_for_icode (rtx *reload_reg, rtx alt_reload_reg,
+                            enum insn_code icode)
+
+{
+  enum reg_class new_class = scratch_reload_class (icode);
+  enum machine_mode new_mode = insn_data[(int) icode].operand[2].mode;
+
+  return reload_adjust_reg_for_temp (reload_reg, alt_reload_reg,
+                                    new_class, new_mode);
+}
+
 /* Generate insns to perform reload RL, which is for the insn in CHAIN and
    has the number J.  OLD contains the value to be used as input.  */
 
@@ -6153,76 +6391,11 @@ emit_input_reload_insns (struct insn_chain *chain, struct reload *rl,
   if (mode == VOIDmode)
     mode = rl->inmode;
 
-#ifdef SECONDARY_INPUT_RELOAD_CLASS
-  /* If we need a secondary register for this operation, see if
-     the value is already in a register in that class.  Don't
-     do this if the secondary register will be used as a scratch
-     register.  */
-
-  if (rl->secondary_in_reload >= 0
-      && rl->secondary_in_icode == CODE_FOR_nothing
-      && optimize)
-    oldequiv
-      = find_equiv_reg (old, insn,
-                       rld[rl->secondary_in_reload].class,
-                       -1, NULL, 0, mode);
-#endif
-
-  /* If reloading from memory, see if there is a register
-     that already holds the same value.  If so, reload from there.
-     We can pass 0 as the reload_reg_p argument because
-     any other reload has either already been emitted,
-     in which case find_equiv_reg will see the reload-insn,
-     or has yet to be emitted, in which case it doesn't matter
-     because we will use this equiv reg right away.  */
-
-  if (oldequiv == 0 && optimize
-      && (MEM_P (old)
-         || (REG_P (old)
-             && REGNO (old) >= FIRST_PSEUDO_REGISTER
-             && reg_renumber[REGNO (old)] < 0)))
-    oldequiv = find_equiv_reg (old, insn, ALL_REGS, -1, NULL, 0, mode);
-
-  if (oldequiv)
-    {
-      unsigned int regno = true_regnum (oldequiv);
-
-      /* Don't use OLDEQUIV if any other reload changes it at an
-        earlier stage of this insn or at this stage.  */
-      if (! free_for_value_p (regno, rl->mode, rl->opnum, rl->when_needed,
-                             rl->in, const0_rtx, j, 0))
-       oldequiv = 0;
-
-      /* If it is no cheaper to copy from OLDEQUIV into the
-        reload register than it would be to move from memory,
-        don't use it. Likewise, if we need a secondary register
-        or memory.  */
-
-      if (oldequiv != 0
-         && (((enum reg_class) REGNO_REG_CLASS (regno) != rl->class
-              && (REGISTER_MOVE_COST (mode, REGNO_REG_CLASS (regno),
-                                      rl->class)
-                  >= MEMORY_MOVE_COST (mode, rl->class, 1)))
-#ifdef SECONDARY_INPUT_RELOAD_CLASS
-             || (SECONDARY_INPUT_RELOAD_CLASS (rl->class,
-                                               mode, oldequiv)
-                 != NO_REGS)
-#endif
-#ifdef SECONDARY_MEMORY_NEEDED
-             || SECONDARY_MEMORY_NEEDED (REGNO_REG_CLASS (regno),
-                                         rl->class,
-                                         mode)
-#endif
-             ))
-       oldequiv = 0;
-    }
-
   /* delete_output_reload is only invoked properly if old contains
      the original pseudo register.  Since this is replaced with a
      hard reg when RELOAD_OVERRIDE_IN is set, see if we can
      find the pseudo in RELOAD_IN_REG.  */
-  if (oldequiv == 0
-      && reload_override_in[j]
+  if (reload_override_in[j]
       && REG_P (rl->in_reg))
     {
       oldequiv = old;
@@ -6293,7 +6466,7 @@ emit_input_reload_insns (struct insn_chain *chain, struct reload *rl,
       where = &other_input_address_reload_insns;
       break;
     default:
-      abort ();
+      gcc_unreachable ();
     }
 
   push_to_sequence (*where);
@@ -6304,8 +6477,7 @@ emit_input_reload_insns (struct insn_chain *chain, struct reload *rl,
       /* We are not going to bother supporting the case where a
         incremented register can't be copied directly from
         OLDEQUIV since this seems highly unlikely.  */
-      if (rl->secondary_in_reload >= 0)
-       abort ();
+      gcc_assert (rl->secondary_in_reload < 0);
 
       if (reload_inherited[j])
        oldequiv = reloadreg;
@@ -6394,7 +6566,6 @@ emit_input_reload_insns (struct insn_chain *chain, struct reload *rl,
 
   /* We can't do that, so output an insn to load RELOADREG.  */
 
-#ifdef SECONDARY_INPUT_RELOAD_CLASS
   /* If we have a secondary reload, pick up the secondary register
      and icode, if any.  If OLDEQUIV and OLD are different or
      if this is an in-out reload, recompute whether or not we
@@ -6409,11 +6580,13 @@ emit_input_reload_insns (struct insn_chain *chain, struct reload *rl,
   if (! special && rl->secondary_in_reload >= 0)
     {
       rtx second_reload_reg = 0;
+      rtx third_reload_reg = 0;
       int secondary_reload = rl->secondary_in_reload;
       rtx real_oldequiv = oldequiv;
       rtx real_old = old;
       rtx tmp;
       enum insn_code icode;
+      enum insn_code tertiary_icode = CODE_FOR_nothing;
 
       /* If OLDEQUIV is a pseudo with a MEM, get the real MEM
         and similarly for OLD.
@@ -6461,53 +6634,89 @@ emit_input_reload_insns (struct insn_chain *chain, struct reload *rl,
        }
 
       second_reload_reg = rld[secondary_reload].reg_rtx;
+      if (rld[secondary_reload].secondary_in_reload >= 0)
+       {
+         int tertiary_reload = rld[secondary_reload].secondary_in_reload;
+
+         third_reload_reg = rld[tertiary_reload].reg_rtx;
+         tertiary_icode = rld[secondary_reload].secondary_in_icode;
+         /* We'd have to add more code for quartary reloads.  */
+         gcc_assert (rld[tertiary_reload].secondary_in_reload < 0);
+       }
       icode = rl->secondary_in_icode;
 
       if ((old != oldequiv && ! rtx_equal_p (old, oldequiv))
          || (rl->in != 0 && rl->out != 0))
        {
-         enum reg_class new_class
-           = SECONDARY_INPUT_RELOAD_CLASS (rl->class,
-                                           mode, real_oldequiv);
+         secondary_reload_info sri, sri2;
+         enum reg_class new_class, new_t_class;
+
+         sri.icode = CODE_FOR_nothing;
+         sri.prev_sri = NULL;
+         new_class = targetm.secondary_reload (1, real_oldequiv, rl->class,
+                                               mode, &sri);
 
-         if (new_class == NO_REGS)
+         if (new_class == NO_REGS && sri.icode == CODE_FOR_nothing)
            second_reload_reg = 0;
-         else
+         else if (new_class == NO_REGS)
            {
-             enum insn_code new_icode;
-             enum machine_mode new_mode;
-
-             if (! TEST_HARD_REG_BIT (reg_class_contents[(int) new_class],
-                                      REGNO (second_reload_reg)))
-               oldequiv = old, real_oldequiv = real_old;
+             if (reload_adjust_reg_for_icode (&second_reload_reg,
+                                              third_reload_reg, sri.icode))
+               icode = sri.icode, third_reload_reg = 0;
              else
+               oldequiv = old, real_oldequiv = real_old;
+           }
+         else if (sri.icode != CODE_FOR_nothing)
+           /* We currently lack a way to express this in reloads.  */
+           gcc_unreachable ();
+         else
+           {
+             sri2.icode = CODE_FOR_nothing;
+             sri2.prev_sri = &sri;
+             new_t_class = targetm.secondary_reload (1, real_oldequiv,
+                                                     new_class, mode, &sri);
+             if (new_t_class == NO_REGS && sri2.icode == CODE_FOR_nothing)
                {
-                 new_icode = reload_in_optab[(int) mode];
-                 if (new_icode != CODE_FOR_nothing
-                     && ((insn_data[(int) new_icode].operand[0].predicate
-                          && ! ((*insn_data[(int) new_icode].operand[0].predicate)
-                                (reloadreg, mode)))
-                         || (insn_data[(int) new_icode].operand[1].predicate
-                             && ! ((*insn_data[(int) new_icode].operand[1].predicate)
-                                   (real_oldequiv, mode)))))
-                   new_icode = CODE_FOR_nothing;
-
-                 if (new_icode == CODE_FOR_nothing)
-                   new_mode = mode;
+                 if (reload_adjust_reg_for_temp (&second_reload_reg,
+                                                 third_reload_reg,
+                                                 new_class, mode))
+                   third_reload_reg = 0, tertiary_icode = sri2.icode;
                  else
-                   new_mode = insn_data[(int) new_icode].operand[2].mode;
+                   oldequiv = old, real_oldequiv = real_old;
+               }
+             else if (new_t_class == NO_REGS && sri2.icode != CODE_FOR_nothing)
+               {
+                 rtx intermediate = second_reload_reg;
 
-                 if (GET_MODE (second_reload_reg) != new_mode)
+                 if (reload_adjust_reg_for_temp (&intermediate, NULL,
+                                                 new_class, mode)
+                     && reload_adjust_reg_for_icode (&third_reload_reg, NULL,
+                                                     sri2.icode))
                    {
-                     if (!HARD_REGNO_MODE_OK (REGNO (second_reload_reg),
-                                              new_mode))
-                       oldequiv = old, real_oldequiv = real_old;
-                     else
-                       second_reload_reg
-                         = reload_adjust_reg_for_mode (second_reload_reg,
-                                                       new_mode);
+                     second_reload_reg = intermediate;
+                     tertiary_icode = sri2.icode;
+                   }
+                 else
+                   oldequiv = old, real_oldequiv = real_old;
+               }
+             else if (new_t_class != NO_REGS && sri2.icode == CODE_FOR_nothing)
+               {
+                 rtx intermediate = second_reload_reg;
+
+                 if (reload_adjust_reg_for_temp (&intermediate, NULL,
+                                                 new_class, mode)
+                     && reload_adjust_reg_for_temp (&third_reload_reg, NULL,
+                                                     new_t_class, mode))
+                   {
+                     second_reload_reg = intermediate;
+                     tertiary_icode = sri2.icode;
                    }
+                 else
+                   oldequiv = old, real_oldequiv = real_old;
                }
+             else
+               /* This could be handled more intelligently too.  */
+               oldequiv = old, real_oldequiv = real_old;
            }
        }
 
@@ -6522,6 +6731,9 @@ emit_input_reload_insns (struct insn_chain *chain, struct reload *rl,
        {
          if (icode != CODE_FOR_nothing)
            {
+             /* We'd have to add extra code to handle this case.  */
+             gcc_assert (!third_reload_reg);
+
              emit_insn (GEN_FCN (icode) (reloadreg, real_oldequiv,
                                          second_reload_reg));
              special = 1;
@@ -6530,18 +6742,21 @@ emit_input_reload_insns (struct insn_chain *chain, struct reload *rl,
            {
              /* See if we need a scratch register to load the
                 intermediate register (a tertiary reload).  */
-             enum insn_code tertiary_icode
-               = rld[secondary_reload].secondary_in_icode;
-
              if (tertiary_icode != CODE_FOR_nothing)
                {
-                 rtx third_reload_reg
-                   = rld[rld[secondary_reload].secondary_in_reload].reg_rtx;
-
                  emit_insn ((GEN_FCN (tertiary_icode)
                              (second_reload_reg, real_oldequiv,
                               third_reload_reg)));
                }
+             else if (third_reload_reg)
+               {
+                 gen_reload (third_reload_reg, real_oldequiv,
+                             rl->opnum,
+                             rl->when_needed);
+                 gen_reload (second_reload_reg, third_reload_reg,
+                             rl->opnum,
+                             rl->when_needed);
+               }
              else
                gen_reload (second_reload_reg, real_oldequiv,
                            rl->opnum,
@@ -6551,7 +6766,6 @@ emit_input_reload_insns (struct insn_chain *chain, struct reload *rl,
            }
        }
     }
-#endif
 
   if (! special && ! rtx_equal_p (reloadreg, oldequiv))
     {
@@ -6618,7 +6832,7 @@ emit_output_reload_insns (struct insn_chain *chain, struct reload *rl,
       if (asm_noperands (PATTERN (insn)) < 0)
        /* It's the compiler's fault.  */
        fatal_insn ("VOIDmode on an output", insn);
-      error_for_asm (insn, "output operand is constant in `asm'");
+      error_for_asm (insn, "output operand is constant in %<asm%>");
       /* Prevent crash--use something we know is valid.  */
       mode = word_mode;
       old = gen_rtx_REG (mode, REGNO (reloadreg));
@@ -6627,8 +6841,6 @@ emit_output_reload_insns (struct insn_chain *chain, struct reload *rl,
   if (GET_MODE (reloadreg) != mode)
     reloadreg = reload_adjust_reg_for_mode (reloadreg, mode);
 
-#ifdef SECONDARY_OUTPUT_RELOAD_CLASS
-
   /* If we need two reload regs, set RELOADREG to the intermediate
      one, since it will be stored into OLD.  We might need a secondary
      register only for an input reload, so check again here.  */
@@ -6636,22 +6848,25 @@ emit_output_reload_insns (struct insn_chain *chain, struct reload *rl,
   if (rl->secondary_out_reload >= 0)
     {
       rtx real_old = old;
+      int secondary_reload = rl->secondary_out_reload;
+      int tertiary_reload = rld[secondary_reload].secondary_out_reload;
 
       if (REG_P (old) && REGNO (old) >= FIRST_PSEUDO_REGISTER
          && reg_equiv_mem[REGNO (old)] != 0)
        real_old = reg_equiv_mem[REGNO (old)];
 
-      if ((SECONDARY_OUTPUT_RELOAD_CLASS (rl->class,
-                                         mode, real_old)
-          != NO_REGS))
+      if (secondary_reload_class (0, rl->class, mode, real_old) != NO_REGS)
        {
          rtx second_reloadreg = reloadreg;
-         reloadreg = rld[rl->secondary_out_reload].reg_rtx;
+         reloadreg = rld[secondary_reload].reg_rtx;
 
          /* See if RELOADREG is to be used as a scratch register
             or as an intermediate register.  */
          if (rl->secondary_out_icode != CODE_FOR_nothing)
            {
+             /* We'd have to add extra code to handle this case.  */
+             gcc_assert (tertiary_reload < 0);
+
              emit_insn ((GEN_FCN (rl->secondary_out_icode)
                          (real_old, second_reloadreg, reloadreg)));
              special = 1;
@@ -6661,17 +6876,19 @@ emit_output_reload_insns (struct insn_chain *chain, struct reload *rl,
              /* See if we need both a scratch and intermediate reload
                 register.  */
 
-             int secondary_reload = rl->secondary_out_reload;
              enum insn_code tertiary_icode
                = rld[secondary_reload].secondary_out_icode;
 
+             /* We'd have to add more code for quartary reloads.  */
+             gcc_assert (tertiary_reload < 0
+                         || rld[tertiary_reload].secondary_out_reload < 0);
+
              if (GET_MODE (reloadreg) != mode)
                reloadreg = reload_adjust_reg_for_mode (reloadreg, mode);
 
              if (tertiary_icode != CODE_FOR_nothing)
                {
-                 rtx third_reloadreg
-                   = rld[rld[secondary_reload].secondary_out_reload].reg_rtx;
+                 rtx third_reloadreg = rld[tertiary_reload].reg_rtx;
                  rtx tem;
 
                  /* Copy primary reload reg to secondary reload reg.
@@ -6697,15 +6914,24 @@ emit_output_reload_insns (struct insn_chain *chain, struct reload *rl,
                }
 
              else
-               /* Copy between the reload regs here and then to
-                  OUT later.  */
+               {
+                 /* Copy between the reload regs here and then to
+                    OUT later.  */
 
-               gen_reload (reloadreg, second_reloadreg,
-                           rl->opnum, rl->when_needed);
+                 gen_reload (reloadreg, second_reloadreg,
+                             rl->opnum, rl->when_needed);
+                 if (tertiary_reload >= 0)
+                   {
+                     rtx third_reloadreg = rld[tertiary_reload].reg_rtx;
+
+                     gen_reload (third_reloadreg, reloadreg,
+                                 rl->opnum, rl->when_needed);
+                     reloadreg = third_reloadreg;
+                   }
+               }
            }
        }
     }
-#endif
 
   /* Output the last reload insn.  */
   if (! special)
@@ -6719,7 +6945,8 @@ emit_output_reload_insns (struct insn_chain *chain, struct reload *rl,
          || !(set = single_set (insn))
          || rtx_equal_p (old, SET_DEST (set))
          || !reg_mentioned_p (old, SET_SRC (set))
-         || !regno_clobbered_p (REGNO (old), insn, rl->mode, 0))
+         || !((REGNO (old) < FIRST_PSEUDO_REGISTER)
+              && regno_clobbered_p (REGNO (old), insn, rl->mode, 0)))
        gen_reload (old, reloadreg, rl->opnum,
                    rl->when_needed);
     }
@@ -6835,6 +7062,10 @@ do_input_reload (struct insn_chain *chain, struct reload *rl, int j)
      actually no need to store the old value in it.  */
 
   if (optimize
+      /* Only attempt this for input reloads; for RELOAD_OTHER we miss
+        that there may be multiple uses of the previous output reload.
+        Restricting to RELOAD_FOR_INPUT is mostly paranoia.  */
+      && rl->when_needed == RELOAD_FOR_INPUT
       && (reload_inherited[j] || reload_override_in[j])
       && rl->reg_rtx
       && REG_P (rl->reg_rtx)
@@ -6921,8 +7152,7 @@ do_output_reload (struct insn_chain *chain, struct reload *rl, int j)
     return;
 
   /* If is a JUMP_INSN, we can't support output reloads yet.  */
-  if (JUMP_P (insn))
-    abort ();
+  gcc_assert (NONJUMP_INSN_P (insn));
 
   emit_output_reload_insns (chain, rld + j, j);
 }
@@ -7016,25 +7246,25 @@ emit_reload_insns (struct insn_chain *chain)
      reloads for the operand.  The RELOAD_OTHER output reloads are
      output in descending order by reload number.  */
 
-  emit_insn_before_sameloc (other_input_address_reload_insns, insn);
-  emit_insn_before_sameloc (other_input_reload_insns, insn);
+  emit_insn_before (other_input_address_reload_insns, insn);
+  emit_insn_before (other_input_reload_insns, insn);
 
   for (j = 0; j < reload_n_operands; j++)
     {
-      emit_insn_before_sameloc (inpaddr_address_reload_insns[j], insn);
-      emit_insn_before_sameloc (input_address_reload_insns[j], insn);
-      emit_insn_before_sameloc (input_reload_insns[j], insn);
+      emit_insn_before (inpaddr_address_reload_insns[j], insn);
+      emit_insn_before (input_address_reload_insns[j], insn);
+      emit_insn_before (input_reload_insns[j], insn);
     }
 
-  emit_insn_before_sameloc (other_operand_reload_insns, insn);
-  emit_insn_before_sameloc (operand_reload_insns, insn);
+  emit_insn_before (other_operand_reload_insns, insn);
+  emit_insn_before (operand_reload_insns, insn);
 
   for (j = 0; j < reload_n_operands; j++)
     {
-      rtx x = emit_insn_after_sameloc (outaddr_address_reload_insns[j], insn);
-      x = emit_insn_after_sameloc (output_address_reload_insns[j], x);
-      x = emit_insn_after_sameloc (output_reload_insns[j], x);
-      emit_insn_after_sameloc (other_output_reload_insns[j], x);
+      rtx x = emit_insn_after (outaddr_address_reload_insns[j], insn);
+      x = emit_insn_after (output_address_reload_insns[j], x);
+      x = emit_insn_after (output_reload_insns[j], x);
+      emit_insn_after (other_output_reload_insns[j], x);
     }
 
   /* For all the spill regs newly reloaded in this instruction,
@@ -7063,7 +7293,7 @@ emit_reload_insns (struct insn_chain *chain)
 
          if (REG_P (reg)
              && REGNO (reg) >= FIRST_PSEUDO_REGISTER
-             && ! reg_has_output_reload[REGNO (reg)])
+             && !REGNO_REG_SET_P (&reg_has_output_reload, REGNO (reg)))
            {
              int nregno = REGNO (reg);
 
@@ -7174,9 +7404,11 @@ emit_reload_insns (struct insn_chain *chain)
                       && rld[r].in != 0
                       && ((REG_P (rld[r].in)
                            && REGNO (rld[r].in) >= FIRST_PSEUDO_REGISTER
-                           && ! reg_has_output_reload[REGNO (rld[r].in)])
+                           && !REGNO_REG_SET_P (&reg_has_output_reload,
+                                                REGNO (rld[r].in)))
                           || (REG_P (rld[r].in_reg)
-                              && ! reg_has_output_reload[REGNO (rld[r].in_reg)]))
+                              && !REGNO_REG_SET_P (&reg_has_output_reload,
+                                                   REGNO (rld[r].in_reg))))
                       && ! reg_set_p (rld[r].reg_rtx, PATTERN (insn)))
                {
                  int nregno;
@@ -7252,13 +7484,18 @@ emit_reload_insns (struct insn_chain *chain)
       /* If a register gets output-reloaded from a non-spill register,
         that invalidates any previous reloaded copy of it.
         But forget_old_reloads_1 won't get to see it, because
-        it thinks only about the original insn.  So invalidate it here.  */
-      if (i < 0 && rld[r].out != 0
-         && (REG_P (rld[r].out)
-             || (MEM_P (rld[r].out)
+        it thinks only about the original insn.  So invalidate it here.
+        Also do the same thing for RELOAD_OTHER constraints where the
+        output is discarded.  */
+      if (i < 0 
+         && ((rld[r].out != 0
+              && (REG_P (rld[r].out)
+                  || (MEM_P (rld[r].out)
+                      && REG_P (rld[r].out_reg))))
+             || (rld[r].out == 0 && rld[r].out_reg
                  && REG_P (rld[r].out_reg))))
        {
-         rtx out = (REG_P (rld[r].out)
+         rtx out = ((rld[r].out && REG_P (rld[r].out))
                     ? rld[r].out : rld[r].out_reg);
          int nregno = REGNO (out);
          if (nregno >= FIRST_PSEUDO_REGISTER)
@@ -7329,12 +7566,13 @@ emit_reload_insns (struct insn_chain *chain)
                  /* We have to set reg_has_output_reload here, or else 
                     forget_old_reloads_1 will clear reg_last_reload_reg
                     right away.  */
-                 reg_has_output_reload[nregno] = 1;
+                 SET_REGNO_REG_SET (&reg_has_output_reload,
+                                    nregno);
                }
            }
          else
            {
-             int num_regs = hard_regno_nregs[nregno][GET_MODE (rld[r].out)];
+             int num_regs = hard_regno_nregs[nregno][GET_MODE (out)];
 
              while (num_regs-- > 0)
                reg_last_reload_reg[nregno + num_regs] = 0;
@@ -7344,13 +7582,39 @@ emit_reload_insns (struct insn_chain *chain)
   IOR_HARD_REG_SET (reg_reloaded_dead, reg_reloaded_died);
 }
 \f
+/* Go through the motions to emit INSN and test if it is strictly valid.
+   Return the emitted insn if valid, else return NULL.  */
+
+static rtx
+emit_insn_if_valid_for_reload (rtx insn)
+{
+  rtx last = get_last_insn ();
+  int code;
+
+  insn = emit_insn (insn);
+  code = recog_memoized (insn);
+
+  if (code >= 0)
+    {
+      extract_insn (insn);
+      /* We want constrain operands to treat this insn strictly in its
+        validity determination, i.e., the way it would after reload has
+        completed.  */
+      if (constrain_operands (1))
+       return insn;
+    }
+
+  delete_insns_since (last);
+  return NULL;
+}
+
 /* Emit code to perform a reload from IN (which may be a reload register) to
    OUT (which may also be a reload register).  IN or OUT is from operand
    OPNUM with reload type TYPE.
 
    Returns first insn emitted.  */
 
-rtx
+static rtx
 gen_reload (rtx out, rtx in, int opnum, enum reload_type type)
 {
   rtx last = get_last_insn ();
@@ -7380,6 +7644,12 @@ gen_reload (rtx out, rtx in, int opnum, enum reload_type type)
      trying to emit a single insn to perform the add.  If it is not valid,
      we use a two insn sequence.
 
+     Or we can be asked to reload an unary operand that was a fragment of
+     an addressing mode, into a register.  If it isn't recognized as-is,
+     we try making the unop operand and the reload-register the same:
+     (set reg:X (unop:X expr:Y))
+     -> (set reg:Y expr:Y) (set reg:X (unop:X reg:Y)).
+
      Finally, we could be called to handle an 'o' constraint by putting
      an address into a register.  In that case, we first try to do this
      with a named pattern of "reload_load_address".  If no such pattern
@@ -7437,20 +7707,9 @@ gen_reload (rtx out, rtx in, int opnum, enum reload_type type)
       if (op0 != XEXP (in, 0) || op1 != XEXP (in, 1))
        in = gen_rtx_PLUS (GET_MODE (in), op0, op1);
 
-      insn = emit_insn (gen_rtx_SET (VOIDmode, out, in));
-      code = recog_memoized (insn);
-
-      if (code >= 0)
-       {
-         extract_insn (insn);
-         /* We want constrain operands to treat this insn strictly in
-            its validity determination, i.e., the way it would after reload
-            has completed.  */
-         if (constrain_operands (1))
-           return insn;
-       }
-
-      delete_insns_since (last);
+      insn = emit_insn_if_valid_for_reload (gen_rtx_SET (VOIDmode, out, in));
+      if (insn)
+       return insn;
 
       /* If that failed, we must use a conservative two-insn sequence.
 
@@ -7486,29 +7745,17 @@ gen_reload (rtx out, rtx in, int opnum, enum reload_type type)
       if (rtx_equal_p (op0, op1))
        op1 = out;
 
-      insn = emit_insn (gen_add2_insn (out, op1));
-
-      /* If that failed, copy the address register to the reload register.
-        Then add the constant to the reload register.  */
-
-      code = recog_memoized (insn);
-
-      if (code >= 0)
+      insn = emit_insn_if_valid_for_reload (gen_add2_insn (out, op1));
+      if (insn)
        {
-         extract_insn (insn);
-         /* We want constrain operands to treat this insn strictly in
-            its validity determination, i.e., the way it would after reload
-            has completed.  */
-         if (constrain_operands (1))
-           {
-             /* Add a REG_EQUIV note so that find_equiv_reg can find it.  */
-             REG_NOTES (insn)
-               = gen_rtx_EXPR_LIST (REG_EQUIV, in, REG_NOTES (insn));
-             return insn;
-           }
+         /* Add a REG_EQUIV note so that find_equiv_reg can find it.  */
+         REG_NOTES (insn)
+           = gen_rtx_EXPR_LIST (REG_EQUIV, in, REG_NOTES (insn));
+         return insn;
        }
 
-      delete_insns_since (last);
+      /* If that failed, copy the address register to the reload register.
+        Then add the constant to the reload register.  */
 
       gen_reload (out, op1, opnum, type);
       insn = emit_insn (gen_add2_insn (out, op0));
@@ -7538,10 +7785,54 @@ gen_reload (rtx out, rtx in, int opnum, enum reload_type type)
       gen_reload (out, loc, opnum, type);
     }
 #endif
+  else if (REG_P (out) && UNARY_P (in))
+    {
+      rtx insn;
+      rtx op1;
+      rtx out_moded;
+      rtx set;
+
+      op1 = find_replacement (&XEXP (in, 0));
+      if (op1 != XEXP (in, 0))
+       in = gen_rtx_fmt_e (GET_CODE (in), GET_MODE (in), op1);
+
+      /* First, try a plain SET.  */
+      set = emit_insn_if_valid_for_reload (gen_rtx_SET (VOIDmode, out, in));
+      if (set)
+       return set;
+
+      /* If that failed, move the inner operand to the reload
+        register, and try the same unop with the inner expression
+        replaced with the reload register.  */
+
+      if (GET_MODE (op1) != GET_MODE (out))
+       out_moded = gen_rtx_REG (GET_MODE (op1), REGNO (out));
+      else
+       out_moded = out;
 
+      gen_reload (out_moded, op1, opnum, type);
+
+      insn
+       = gen_rtx_SET (VOIDmode, out,
+                      gen_rtx_fmt_e (GET_CODE (in), GET_MODE (in),
+                                     out_moded));
+      insn = emit_insn_if_valid_for_reload (insn);
+      if (insn)
+       {
+         REG_NOTES (insn)
+           = gen_rtx_EXPR_LIST (REG_EQUIV, in, REG_NOTES (insn));
+         return insn;
+       }
+
+      fatal_insn ("Failure trying to reload:", set);
+    }
   /* If IN is a simple operand, use gen_move_insn.  */
   else if (OBJECT_P (in) || GET_CODE (in) == SUBREG)
-    emit_insn (gen_move_insn (out, in));
+    {
+      tem = emit_insn (gen_move_insn (out, in));
+      /* IN may contain a LABEL_REF, if so add a REG_LABEL note.  */
+      mark_jump_label (in, tem, 0);
+    }
 
 #ifdef HAVE_reload_load_address
   else if (HAVE_reload_load_address)
@@ -7629,18 +7920,23 @@ delete_output_reload (rtx insn, int j, int last_reload_reg)
     n_occurrences += count_occurrences (PATTERN (insn),
                                        eliminate_regs (substed, 0,
                                                        NULL_RTX), 0);
+  for (i1 = reg_equiv_alt_mem_list [REGNO (reg)]; i1; i1 = XEXP (i1, 1))
+    {
+      gcc_assert (!rtx_equal_p (XEXP (i1, 0), substed));
+      n_occurrences += count_occurrences (PATTERN (insn), XEXP (i1, 0), 0);
+    }
   if (n_occurrences > n_inherited)
     return;
 
   /* If the pseudo-reg we are reloading is no longer referenced
      anywhere between the store into it and here,
-     and no jumps or labels intervene, then the value can get
-     here through the reload reg alone.
+     and we're within the same basic block, then the value can only
+     pass through the reload reg and end up here.
      Otherwise, give up--return.  */
   for (i1 = NEXT_INSN (output_reload_insn);
        i1 != insn; i1 = NEXT_INSN (i1))
     {
-      if (LABEL_P (i1) || JUMP_P (i1))
+      if (NOTE_INSN_BASIC_BLOCK_P (i1))
        return;
       if ((NONJUMP_INSN_P (i1) || CALL_P (i1))
          && reg_mentioned_p (reg, PATTERN (i1)))
@@ -7895,15 +8191,16 @@ static rtx
 inc_for_reload (rtx reloadreg, rtx in, rtx value, int inc_amount)
 {
   /* REG or MEM to be copied and incremented.  */
-  rtx incloc = XEXP (value, 0);
+  rtx incloc = find_replacement (&XEXP (value, 0));
   /* Nonzero if increment after copying.  */
-  int post = (GET_CODE (value) == POST_DEC || GET_CODE (value) == POST_INC);
+  int post = (GET_CODE (value) == POST_DEC || GET_CODE (value) == POST_INC
+             || GET_CODE (value) == POST_MODIFY);
   rtx last;
   rtx inc;
   rtx add_insn;
   int code;
   rtx store;
-  rtx real_in = in == value ? XEXP (in, 0) : in;
+  rtx real_in = in == value ? incloc : in;
 
   /* No hard register is equivalent to this register after
      inc/dec operation.  If REG_LAST_RELOAD_REG were nonzero,
@@ -7912,10 +8209,18 @@ inc_for_reload (rtx reloadreg, rtx in, rtx value, int inc_amount)
   if (REG_P (incloc))
     reg_last_reload_reg[REGNO (incloc)] = 0;
 
-  if (GET_CODE (value) == PRE_DEC || GET_CODE (value) == POST_DEC)
-    inc_amount = -inc_amount;
+  if (GET_CODE (value) == PRE_MODIFY || GET_CODE (value) == POST_MODIFY)
+    {
+      gcc_assert (GET_CODE (XEXP (value, 1)) == PLUS);
+      inc = find_replacement (&XEXP (XEXP (value, 1), 1));
+    }
+  else
+    {
+      if (GET_CODE (value) == PRE_DEC || GET_CODE (value) == POST_DEC)
+       inc_amount = -inc_amount;
 
-  inc = GEN_INT (inc_amount);
+      inc = GEN_INT (inc_amount);
+    }
 
   /* If this is post-increment, first copy the location to the reload reg.  */
   if (post && real_in != reloadreg)
@@ -7975,7 +8280,10 @@ inc_for_reload (rtx reloadreg, rtx in, rtx value, int inc_amount)
 
       emit_insn (gen_add2_insn (reloadreg, inc));
       store = emit_insn (gen_move_insn (incloc, reloadreg));
-      emit_insn (gen_add2_insn (reloadreg, GEN_INT (-inc_amount)));
+      if (GET_CODE (inc) == CONST_INT)
+       emit_insn (gen_add2_insn (reloadreg, GEN_INT (-INTVAL(inc))));
+      else
+       emit_insn (gen_sub2_insn (reloadreg, inc));
     }
 
   return store;
@@ -8041,10 +8349,11 @@ fixup_abnormal_edges (void)
   FOR_EACH_BB (bb)
     {
       edge e;
+      edge_iterator ei;
 
       /* Look for cases we are interested in - calls or instructions causing
          exceptions.  */
-      for (e = bb->succ; e; e = e->succ_next)
+      FOR_EACH_EDGE (e, ei, bb->succs)
        {
          if (e->flags & EDGE_ABNORMAL_CALL)
            break;
@@ -8055,48 +8364,64 @@ fixup_abnormal_edges (void)
       if (e && !CALL_P (BB_END (bb))
          && !can_throw_internal (BB_END (bb)))
        {
-         rtx insn = BB_END (bb), stop = NEXT_INSN (BB_END (bb));
-         rtx next;
-         for (e = bb->succ; e; e = e->succ_next)
-           if (e->flags & EDGE_FALLTHRU)
-             break;
-         /* Get past the new insns generated. Allow notes, as the insns may
-            be already deleted.  */
+         rtx insn;
+
+         /* Get past the new insns generated.  Allow notes, as the insns
+            may be already deleted.  */
+         insn = BB_END (bb);
          while ((NONJUMP_INSN_P (insn) || NOTE_P (insn))
                 && !can_throw_internal (insn)
                 && insn != BB_HEAD (bb))
            insn = PREV_INSN (insn);
-         if (!CALL_P (insn) && !can_throw_internal (insn))
-           abort ();
-         BB_END (bb) = insn;
-         inserted = true;
-         insn = NEXT_INSN (insn);
-         while (insn && insn != stop)
+
+         if (CALL_P (insn) || can_throw_internal (insn))
            {
-             next = NEXT_INSN (insn);
-             if (INSN_P (insn))
+             rtx stop, next;
+
+             stop = NEXT_INSN (BB_END (bb));
+             BB_END (bb) = insn;
+             insn = NEXT_INSN (insn);
+
+             FOR_EACH_EDGE (e, ei, bb->succs)
+               if (e->flags & EDGE_FALLTHRU)
+                 break;
+
+             while (insn && insn != stop)
                {
-                 delete_insn (insn);
-
-                 /* Sometimes there's still the return value USE.
-                    If it's placed after a trapping call (i.e. that
-                    call is the last insn anyway), we have no fallthru
-                    edge.  Simply delete this use and don't try to insert
-                    on the non-existent edge.  */
-                 if (GET_CODE (PATTERN (insn)) != USE)
+                 next = NEXT_INSN (insn);
+                 if (INSN_P (insn))
                    {
-                     /* We're not deleting it, we're moving it.  */
-                     INSN_DELETED_P (insn) = 0;
-                     PREV_INSN (insn) = NULL_RTX;
-                     NEXT_INSN (insn) = NULL_RTX;
+                     delete_insn (insn);
+
+                     /* Sometimes there's still the return value USE.
+                        If it's placed after a trapping call (i.e. that
+                        call is the last insn anyway), we have no fallthru
+                        edge.  Simply delete this use and don't try to insert
+                        on the non-existent edge.  */
+                     if (GET_CODE (PATTERN (insn)) != USE)
+                       {
+                         /* We're not deleting it, we're moving it.  */
+                         INSN_DELETED_P (insn) = 0;
+                         PREV_INSN (insn) = NULL_RTX;
+                         NEXT_INSN (insn) = NULL_RTX;
 
-                     insert_insn_on_edge (insn, e);
+                         insert_insn_on_edge (insn, e);
+                         inserted = true;
+                       }
                    }
+                 insn = next;
                }
-             insn = next;
            }
+
+         /* It may be that we don't find any such trapping insn.  In this
+            case we discovered quite late that the insn that had been 
+            marked as can_throw_internal in fact couldn't trap at all.
+            So we should in fact delete the EH edges out of the block.  */
+         else
+           purge_dead_edges (bb);
        }
     }
+
   /* We've possibly turned single trapping insn into multiple ones.  */
   if (flag_non_call_exceptions)
     {
@@ -8105,6 +8430,14 @@ fixup_abnormal_edges (void)
       sbitmap_ones (blocks);
       find_many_sub_basic_blocks (blocks);
     }
+
   if (inserted)
     commit_edge_insertions ();
+
+#ifdef ENABLE_CHECKING
+  /* Verify that we didn't turn one trapping insn into many, and that
+     we found and corrected all of the problems wrt fixups on the
+     fallthru edge.  */
+  verify_flow_info ();
+#endif
 }