OSDN Git Service

Daily bump.
[pf3gnuchains/gcc-fork.git] / gcc / caller-save.c
index ecdc20a..305894e 100644 (file)
@@ -1,5 +1,6 @@
 /* Save and restore call-clobbered registers which are live across a call.
-   Copyright (C) 1989, 1992 Free Software Foundation, Inc.
+   Copyright (C) 1989, 1992, 1994, 1995, 1997, 1998,
+   1999, 2000 Free Software Foundation, Inc.
 
 This file is part of GNU CC.
 
@@ -15,9 +16,11 @@ GNU General Public License for more details.
 
 You should have received a copy of the GNU General Public License
 along with GNU CC; see the file COPYING.  If not, write to
-the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
+the Free Software Foundation, 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.  */
 
 #include "config.h"
+#include "system.h"
 #include "rtl.h"
 #include "insn-config.h"
 #include "flags.h"
@@ -26,7 +29,20 @@ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
 #include "recog.h"
 #include "basic-block.h"
 #include "reload.h"
+#include "function.h"
 #include "expr.h"
+#include "toplev.h"
+#include "tm_p.h"
+
+#ifndef MAX_MOVE_MAX
+#define MAX_MOVE_MAX MOVE_MAX
+#endif
+
+#ifndef MIN_UNITS_PER_WORD
+#define MIN_UNITS_PER_WORD UNITS_PER_WORD
+#endif
+
+#define MOVE_MAX_WORDS (MOVE_MAX / UNITS_PER_WORD)
 
 /* Modes for each hard register that we can save.  The smallest mode is wide
    enough to save the entire contents of the register.  When saving the
@@ -34,13 +50,13 @@ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
    If that is not possible the save is done one register at a time.  */
 
 static enum machine_mode 
-  regno_save_mode[FIRST_PSEUDO_REGISTER][MOVE_MAX / UNITS_PER_WORD + 1];
+  regno_save_mode[FIRST_PSEUDO_REGISTER][MAX_MOVE_MAX / MIN_UNITS_PER_WORD + 1];
 
 /* For each hard register, a place on the stack where it can be saved,
    if needed.  */
 
 static rtx 
-  regno_save_mem[FIRST_PSEUDO_REGISTER][MOVE_MAX / UNITS_PER_WORD + 1];
+  regno_save_mem[FIRST_PSEUDO_REGISTER][MAX_MOVE_MAX / MIN_UNITS_PER_WORD + 1];
 
 /* We will only make a register eligible for caller-save if it can be
    saved in its widest mode with a simple SET insn as long as the memory
@@ -49,72 +65,36 @@ static rtx
    be recognized.  */
 
 static enum insn_code 
-  reg_save_code[FIRST_PSEUDO_REGISTER][MOVE_MAX / UNITS_PER_WORD + 1];
+  reg_save_code[FIRST_PSEUDO_REGISTER][MAX_MOVE_MAX / MIN_UNITS_PER_WORD + 1];
 static enum insn_code 
-  reg_restore_code[FIRST_PSEUDO_REGISTER][MOVE_MAX / UNITS_PER_WORD + 1];
-
-/* Set of hard regs currently live (during scan of all insns).  */
-
-static HARD_REG_SET hard_regs_live;
+  reg_restore_code[FIRST_PSEUDO_REGISTER][MAX_MOVE_MAX / MIN_UNITS_PER_WORD + 1];
 
 /* Set of hard regs currently residing in save area (during insn scan).  */
 
 static HARD_REG_SET hard_regs_saved;
 
-/* Set of hard regs which need to be restored before referenced.  */
-
-static HARD_REG_SET hard_regs_need_restore;
-
 /* Number of registers currently in hard_regs_saved.  */
 
-int n_regs_saved;
+static int n_regs_saved;
 
-static enum machine_mode choose_hard_reg_mode PROTO((int, int));
-static void set_reg_live               PROTO((rtx, rtx));
-static void clear_reg_live             PROTO((rtx));
-static void restore_referenced_regs    PROTO((rtx, rtx, enum machine_mode));
-static int insert_save_restore         PROTO((rtx, int, int,
-                                              enum machine_mode, int));
-\f
-/* Return a machine mode that is legitimate for hard reg REGNO and large
-   enough to save nregs.  If we can't find one, return VOIDmode.  */
+/* Computed by mark_referenced_regs, all regs referenced in a given
+   insn.  */
+static HARD_REG_SET referenced_regs;
 
-static enum machine_mode
-choose_hard_reg_mode (regno, nregs)
-     int regno;
-     int nregs;
-{
-  enum machine_mode found_mode = VOIDmode, mode;
-
-  /* We first look for the largest integer mode that can be validly
-     held in REGNO.  If none, we look for the largest floating-point mode.
-     If we still didn't find a valid mode, try CCmode.  */
-
-  for (mode = GET_CLASS_NARROWEST_MODE (MODE_INT); mode != VOIDmode;
-       mode = GET_MODE_WIDER_MODE (mode))
-    if (HARD_REGNO_NREGS (regno, mode) == nregs
-       && HARD_REGNO_MODE_OK (regno, mode))
-      found_mode = mode;
-
-  if (found_mode != VOIDmode)
-    return found_mode;
-
-  for (mode = GET_CLASS_NARROWEST_MODE (MODE_FLOAT); mode != VOIDmode;
-       mode = GET_MODE_WIDER_MODE (mode))
-    if (HARD_REGNO_NREGS (regno, mode) == nregs
-       && HARD_REGNO_MODE_OK (regno, mode))
-      found_mode = mode;
+/* Computed in mark_set_regs, holds all registers set by the current
+   instruction.  */
+static HARD_REG_SET this_insn_sets;
 
-  if (found_mode != VOIDmode)
-    return found_mode;
 
-  if (HARD_REGNO_NREGS (regno, CCmode) == nregs
-      && HARD_REGNO_MODE_OK (regno, CCmode))
-    return CCmode;
-
-  /* We can't find a mode valid for this register.  */
-  return VOIDmode;
-}
+static void mark_set_regs              PARAMS ((rtx, rtx, void *));
+static void mark_referenced_regs       PARAMS ((rtx));
+static int insert_save                 PARAMS ((struct insn_chain *, int, int,
+                                                HARD_REG_SET *));
+static int insert_restore              PARAMS ((struct insn_chain *, int, int,
+                                                int));
+static struct insn_chain *insert_one_insn PARAMS ((struct insn_chain *, int,
+                                                  enum insn_code, rtx));
+static void add_stored_regs            PARAMS ((rtx, rtx, void *));
 \f
 /* Initialize for caller-save.
 
@@ -143,9 +123,9 @@ init_caller_save ()
     {
       if (call_used_regs[i] && ! call_fixed_regs[i])
        {
-         for (j = 1; j <= MOVE_MAX / UNITS_PER_WORD; j++)
+         for (j = 1; j <= MOVE_MAX_WORDS; j++)
            {
-             regno_save_mode[i][j] = choose_hard_reg_mode (i, j);
+             regno_save_mode[i][j] = HARD_REGNO_CALLER_SAVE_MODE (i, j);
              if (regno_save_mode[i][j] == VOIDmode && j == 1)
                {
                  call_fixed_regs[i] = 1;
@@ -175,11 +155,11 @@ init_caller_save ()
   if (i == FIRST_PSEUDO_REGISTER)
     abort ();
 
-  addr_reg = gen_rtx (REG, Pmode, i);
+  addr_reg = gen_rtx_REG (Pmode, i);
 
   for (offset = 1 << (HOST_BITS_PER_INT / 2); offset; offset >>= 1)
     {
-      address = gen_rtx (PLUS, Pmode, addr_reg, GEN_INT (offset));
+      address = gen_rtx_PLUS (Pmode, addr_reg, GEN_INT (offset));
 
       for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
        if (regno_save_mode[i][1] != VOIDmode
@@ -200,13 +180,13 @@ init_caller_save ()
   start_sequence ();
 
   for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
-    for (j = 1; j <= MOVE_MAX / UNITS_PER_WORD; j++)
+    for (j = 1; j <= MOVE_MAX_WORDS; j++)
       if (regno_save_mode[i][j] != VOIDmode)
         {
-         rtx mem = gen_rtx (MEM, regno_save_mode[i][j], address);
-         rtx reg = gen_rtx (REG, regno_save_mode[i][j], i);
-         rtx savepat = gen_rtx (SET, VOIDmode, mem, reg);
-         rtx restpat = gen_rtx (SET, VOIDmode, reg, mem);
+         rtx mem = gen_rtx_MEM (regno_save_mode[i][j], address);
+         rtx reg = gen_rtx_REG (regno_save_mode[i][j], i);
+         rtx savepat = gen_rtx_SET (VOIDmode, mem, reg);
+         rtx restpat = gen_rtx_SET (VOIDmode, reg, mem);
          rtx saveinsn = emit_insn (savepat);
          rtx restinsn = emit_insn (restpat);
          int ok;
@@ -214,14 +194,16 @@ init_caller_save ()
          reg_save_code[i][j] = recog_memoized (saveinsn);
          reg_restore_code[i][j] = recog_memoized (restinsn);
 
-         /* Now extract both insns and see if we can meet their constraints. */
-         ok = (reg_save_code[i][j] != -1 && reg_restore_code[i][j] != -1);
+         /* Now extract both insns and see if we can meet their
+             constraints.  */
+         ok = (reg_save_code[i][j] != (enum insn_code)-1
+               && reg_restore_code[i][j] != (enum insn_code)-1);
          if (ok)
            {
-             insn_extract (saveinsn);
-             ok = constrain_operands (reg_save_code[i][j], 1);
-             insn_extract (restinsn);
-             ok &= constrain_operands (reg_restore_code[i][j], 1);
+             extract_insn (saveinsn);
+             ok = constrain_operands (1);
+             extract_insn (restinsn);
+             ok &= constrain_operands (1);
            }
 
          if (! ok)
@@ -248,7 +230,7 @@ init_save_areas ()
   int i, j;
 
   for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
-    for (j = 1; j <= MOVE_MAX / UNITS_PER_WORD; j++)
+    for (j = 1; j <= MOVE_MAX_WORDS; j++)
       regno_save_mem[i][j] = 0;
 }
 
@@ -258,13 +240,6 @@ init_save_areas ()
    overestimate slightly (especially if some of these registers are later
    used as spill registers), but it should not be significant.
 
-   Then perform register elimination in the addresses of the save area
-   locations; return 1 if all eliminated addresses are strictly valid.
-   We assume that our caller has set up the elimination table to the
-   worst (largest) possible offsets.
-
-   Set *PCHANGED to 1 if we had to allocate some memory for the save area.  
-
    Future work:
 
      In the fallback case we should iterate backwards across all possible
@@ -277,14 +252,11 @@ init_save_areas ()
      machine independent since they might be saving non-consecutive 
      registers. (imagine caller-saving d0,d1,a0,a1 on the 68k) */
 
-int
-setup_save_areas (pchanged)
-     int *pchanged;
+void
+setup_save_areas ()
 {
   int i, j, k;
   HARD_REG_SET hard_regs_used;
-  int ok = 1;
-
 
   /* Allocate space in the save area for the largest multi-register
      pseudos first, then work backwards to single register
@@ -293,7 +265,7 @@ setup_save_areas (pchanged)
   /* Find and record all call-used hard-registers in this function.  */
   CLEAR_HARD_REG_SET (hard_regs_used);
   for (i = FIRST_PSEUDO_REGISTER; i < max_regno; i++)
-    if (reg_renumber[i] >= 0 && reg_n_calls_crossed[i] > 0)
+    if (reg_renumber[i] >= 0 && REG_N_CALLS_CROSSED (i) > 0)
       {
        int regno = reg_renumber[i];
        int endregno 
@@ -312,10 +284,9 @@ setup_save_areas (pchanged)
      in a manner which allows multi-register saves/restores to be done.  */
 
   for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
-    for (j = MOVE_MAX / UNITS_PER_WORD; j > 0; j--)
+    for (j = MOVE_MAX_WORDS; j > 0; j--)
       {
-       int ok = 1;
-       int do_save;
+       int do_save = 1;
 
        /* If no mode exists for this size, try another.  Also break out
           if we have already saved this hard register.  */
@@ -323,7 +294,6 @@ setup_save_areas (pchanged)
          continue;
 
        /* See if any register in this group has been saved.  */
-       do_save = 1;
        for (k = 0; k < j; k++)
          if (regno_save_mem[i + k][1])
            {
@@ -334,190 +304,138 @@ setup_save_areas (pchanged)
          continue;
 
        for (k = 0; k < j; k++)
+         if (! TEST_HARD_REG_BIT (hard_regs_used, i + k))
            {
-             int regno = i + k;
-             ok &= (TEST_HARD_REG_BIT (hard_regs_used, regno) != 0);
+             do_save = 0;
+             break;
            }
+       if (! do_save)
+         continue;
 
-       /* We have found an acceptable mode to store in. */
-       if (ok)
-         {
-
-           regno_save_mem[i][j]
-             = assign_stack_local (regno_save_mode[i][j],
-                                   GET_MODE_SIZE (regno_save_mode[i][j]), 0);
+       /* We have found an acceptable mode to store in.  */
+       regno_save_mem[i][j]
+         = assign_stack_local (regno_save_mode[i][j],
+                               GET_MODE_SIZE (regno_save_mode[i][j]), 0);
 
-           /* Setup single word save area just in case... */
-           for (k = 0; k < j; k++)
-             {
-               /* This should not depend on WORDS_BIG_ENDIAN.
-                  The order of words in regs is the same as in memory.  */
-               rtx temp = gen_rtx (MEM, regno_save_mode[i+k][1], 
+       /* Setup single word save area just in case...  */
+       for (k = 0; k < j; k++)
+         {
+           /* This should not depend on WORDS_BIG_ENDIAN.
+              The order of words in regs is the same as in memory.  */
+           rtx temp = gen_rtx_MEM (regno_save_mode[i+k][1], 
                                    XEXP (regno_save_mem[i][j], 0));
 
-               regno_save_mem[i+k][1] 
-                 = adj_offsettable_operand (temp, k * UNITS_PER_WORD);
-             }
-           *pchanged = 1;
+           regno_save_mem[i+k][1] 
+             = adj_offsettable_operand (temp, k * UNITS_PER_WORD);
          }
       }
-
-  for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
-    for (j = 1; j <= MOVE_MAX / UNITS_PER_WORD; j++)
-      if (regno_save_mem[i][j] != 0)
-       ok &= strict_memory_address_p (GET_MODE (regno_save_mem[i][j]),
-                                      XEXP (eliminate_regs (regno_save_mem[i][j], 0, NULL_RTX), 0));
-
-  return ok;
 }
 \f
-/* Find the places where hard regs are live across calls and save them.
-
-   INSN_MODE is the mode to assign to any insns that we add.  This is used
-   by reload to determine whether or not reloads or register eliminations
-   need be done on these insns.  */
-
+/* Find the places where hard regs are live across calls and save them.  */
 void
-save_call_clobbered_regs (insn_mode)
-     enum machine_mode insn_mode;
+save_call_clobbered_regs ()
 {
-  rtx insn;
-  int b;
-
-  for (b = 0; b < n_basic_blocks; b++)
-    {
-      regset regs_live = basic_block_live_at_start[b];
-      rtx prev_block_last = PREV_INSN (basic_block_head[b]);
-      REGSET_ELT_TYPE bit;
-      int offset, i, j;
-      int regno;
-
-      /* Compute hard regs live at start of block -- this is the
-        real hard regs marked live, plus live pseudo regs that
-        have been renumbered to hard regs.  No registers have yet been
-        saved because we restore all of them before the end of the basic
-        block.  */
-
-#ifdef HARD_REG_SET
-      hard_regs_live = *regs_live;
-#else
-      COPY_HARD_REG_SET (hard_regs_live, regs_live);
-#endif
+  struct insn_chain *chain, *next;
 
-      CLEAR_HARD_REG_SET (hard_regs_saved);
-      CLEAR_HARD_REG_SET (hard_regs_need_restore);
-      n_regs_saved = 0;
+  CLEAR_HARD_REG_SET (hard_regs_saved);
+  n_regs_saved = 0;
 
-      for (offset = 0, i = 0; offset < regset_size; offset++)
-       {
-         if (regs_live[offset] == 0)
-           i += REGSET_ELT_BITS;
-         else
-           for (bit = 1; bit && i < max_regno; bit <<= 1, i++)
-             if ((regs_live[offset] & bit)
-                 && (regno = reg_renumber[i]) >= 0)
-               for (j = regno;
-                    j < regno + HARD_REGNO_NREGS (regno,
-                                                  PSEUDO_REGNO_MODE (i));
-                    j++)
-                 SET_HARD_REG_BIT (hard_regs_live, j);
+  for (chain = reload_insn_chain; chain != 0; chain = next)
+    {
+      rtx insn = chain->insn;
+      enum rtx_code code = GET_CODE (insn);
 
-       }
+      next = chain->next;
 
-      /* Now scan the insns in the block, keeping track of what hard
-        regs are live as we go.  When we see a call, save the live
-        call-clobbered hard regs.  */
+      if (chain->is_caller_save_insn)
+       abort ();
 
-      for (insn = basic_block_head[b]; ; insn = NEXT_INSN (insn))
+      if (GET_RTX_CLASS (code) == 'i')
        {
-         RTX_CODE code = GET_CODE (insn);
+         /* If some registers have been saved, see if INSN references
+            any of them.  We must restore them before the insn if so.  */
 
-         if (GET_RTX_CLASS (code) == 'i')
+         if (n_regs_saved)
            {
-             rtx link;
+             int regno;
 
-             /* If some registers have been saved, see if INSN references
-                any of them.  We must restore them before the insn if so.  */
-
-             if (n_regs_saved)
-               restore_referenced_regs (PATTERN (insn), insn, insn_mode);
-
-             /* NB: the normal procedure is to first enliven any
-                registers set by insn, then deaden any registers that
-                had their last use at insn.  This is incorrect now,
-                since multiple pseudos may have been mapped to the
-                same hard reg, and the death notes are ambiguous.  So
-                it must be done in the other, safe, order.  */
-
-             for (link = REG_NOTES (insn); link; link = XEXP (link, 1))
-               if (REG_NOTE_KIND (link) == REG_DEAD)
-                 clear_reg_live (XEXP (link, 0));
-
-             /* When we reach a call, we need to save all registers that are
-                live, call-used, not fixed, and not already saved.  We must
-                test at this point because registers that die in a CALL_INSN
-                are not live across the call and likewise for registers that
-                are born in the CALL_INSN.  */
-
-             if (code == CALL_INSN)
+             if (code == JUMP_INSN)
+               /* Restore all registers if this is a JUMP_INSN.  */
+               COPY_HARD_REG_SET (referenced_regs, hard_regs_saved);
+             else
                {
-                 for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
-                   if (call_used_regs[regno] && ! call_fixed_regs[regno]
-                       && TEST_HARD_REG_BIT (hard_regs_live, regno)
-                       && ! TEST_HARD_REG_BIT (hard_regs_saved, regno))
-                     regno += insert_save_restore (insn, 1, regno, 
-                                                   insn_mode, 0);
-#ifdef HARD_REG_SET
-                 hard_regs_need_restore = hard_regs_saved;
-#else
-                 COPY_HARD_REG_SET (hard_regs_need_restore,
-                                    hard_regs_saved);
-#endif
-
-                 /* Must recompute n_regs_saved.  */
-                 n_regs_saved = 0;
-                 for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
-                   if (TEST_HARD_REG_BIT (hard_regs_saved, regno))
-                     n_regs_saved++;
-                 
+                 CLEAR_HARD_REG_SET (referenced_regs);
+                 mark_referenced_regs (PATTERN (insn));
+                 AND_HARD_REG_SET (referenced_regs, hard_regs_saved);
                }
-             
-             note_stores (PATTERN (insn), set_reg_live);
 
-             for (link = REG_NOTES (insn); link; link = XEXP (link, 1))
-               if (REG_NOTE_KIND (link) == REG_UNUSED)
-                 clear_reg_live (XEXP (link, 0));
+             for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
+               if (TEST_HARD_REG_BIT (referenced_regs, regno))
+                 regno += insert_restore (chain, 1, regno, MOVE_MAX_WORDS);
            }
 
-         if (insn == basic_block_end[b])
-           break;
+         if (code == CALL_INSN)
+           {
+             int regno;
+             HARD_REG_SET hard_regs_to_save;
+
+             /* Use the register life information in CHAIN to compute which
+                regs are live during the call.  */
+             REG_SET_TO_HARD_REG_SET (hard_regs_to_save,
+                                      &chain->live_throughout);
+             compute_use_by_pseudos (&hard_regs_to_save,
+                                     &chain->live_throughout);
+
+             /* Record all registers set in this call insn.  These don't need
+                to be saved.  N.B. the call insn might set a subreg of a
+                multi-hard-reg pseudo; then the pseudo is considered live
+                during the call, but the subreg that is set isn't.  */
+             CLEAR_HARD_REG_SET (this_insn_sets);
+             note_stores (PATTERN (insn), mark_set_regs, NULL);
+
+             /* Compute which hard regs must be saved before this call.  */
+             AND_COMPL_HARD_REG_SET (hard_regs_to_save, call_fixed_reg_set);
+             AND_COMPL_HARD_REG_SET (hard_regs_to_save, this_insn_sets);
+             AND_COMPL_HARD_REG_SET (hard_regs_to_save, hard_regs_saved);
+             AND_HARD_REG_SET (hard_regs_to_save, call_used_reg_set);
+
+             for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
+               if (TEST_HARD_REG_BIT (hard_regs_to_save, regno))
+                 regno += insert_save (chain, 1, regno, &hard_regs_to_save);
+
+             /* Must recompute n_regs_saved.  */
+             n_regs_saved = 0;
+             for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
+               if (TEST_HARD_REG_BIT (hard_regs_saved, regno))
+                 n_regs_saved++;
+           }
        }
 
-      /* At the end of the basic block, we must restore any registers that
-        remain saved.  If the last insn in the block is a JUMP_INSN, put
-        the restore before the insn, otherwise, put it after the insn.  */
-
-      if (n_regs_saved)
-       for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
-         if (TEST_HARD_REG_BIT (hard_regs_need_restore, regno))
-           regno += insert_save_restore ((GET_CODE (insn) == JUMP_INSN
-                                 ? insn : NEXT_INSN (insn)), 0,
-                                 regno, insn_mode, MOVE_MAX / UNITS_PER_WORD);
-
-      /* If we added any insns at the start of the block, update the start
-        of the block to point at those insns.  */
-      basic_block_head[b] = NEXT_INSN (prev_block_last);
-    }
+      if (chain->next == 0 || chain->next->block > chain->block)
+       {
+         int regno;
+         /* At the end of the basic block, we must restore any registers that
+            remain saved.  If the last insn in the block is a JUMP_INSN, put
+            the restore before the insn, otherwise, put it after the insn.  */
+
+         if (n_regs_saved)
+           for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
+             if (TEST_HARD_REG_BIT (hard_regs_saved, regno))
+               regno += insert_restore (chain, GET_CODE (insn) == JUMP_INSN,
+                                        regno, MOVE_MAX_WORDS);
+       }
+    }  
 }
 
 /* Here from note_stores when an insn stores a value in a register.
-   Set the proper bit or bits in hard_regs_live.  All pseudos that have
+   Set the proper bit or bits in this_insn_sets.  All pseudos that have
    been assigned hard regs have had their register number changed already,
    so we can ignore pseudos.  */
-
 static void
-set_reg_live (reg, setter)
-     rtx reg, setter;
+mark_set_regs (reg, setter, data)
+     rtx reg;
+     rtx setter ATTRIBUTE_UNUSED;
+     void *data ATTRIBUTE_UNUSED;
 {
   register int regno, endregno, i;
   enum machine_mode mode = GET_MODE (reg);
@@ -536,105 +454,104 @@ set_reg_live (reg, setter)
   endregno = regno + HARD_REGNO_NREGS (regno, mode);
 
   for (i = regno; i < endregno; i++)
-    {
-      SET_HARD_REG_BIT (hard_regs_live, i);
-      CLEAR_HARD_REG_BIT (hard_regs_saved, i);
-      CLEAR_HARD_REG_BIT (hard_regs_need_restore, i);
-    }
+    SET_HARD_REG_BIT (this_insn_sets, i);
 }
 
-/* Here when a REG_DEAD note records the last use of a reg.  Clear
-   the appropriate bit or bits in hard_regs_live.  Again we can ignore
-   pseudos.  */
-
+/* Here from note_stores when an insn stores a value in a register.
+   Set the proper bit or bits in the passed regset.  All pseudos that have
+   been assigned hard regs have had their register number changed already,
+   so we can ignore pseudos.  */
 static void
-clear_reg_live (reg)
+add_stored_regs (reg, setter, data)
      rtx reg;
+     rtx setter;
+     void *data;
 {
   register int regno, endregno, i;
+  enum machine_mode mode = GET_MODE (reg);
+  int word = 0;
+
+  if (GET_CODE (setter) == CLOBBER)
+    return;
+
+  while (GET_CODE (reg) == SUBREG)
+    {
+      word += SUBREG_WORD (reg);
+      reg = SUBREG_REG (reg);
+    }
 
   if (GET_CODE (reg) != REG || REGNO (reg) >= FIRST_PSEUDO_REGISTER)
     return;
 
-  regno = REGNO (reg);
-  endregno= regno + HARD_REGNO_NREGS (regno, GET_MODE (reg));
+  regno = REGNO (reg) + word;
+  endregno = regno + HARD_REGNO_NREGS (regno, mode);
 
   for (i = regno; i < endregno; i++)
-    {
-      CLEAR_HARD_REG_BIT (hard_regs_live, i);
-      CLEAR_HARD_REG_BIT (hard_regs_need_restore, i);
-      CLEAR_HARD_REG_BIT (hard_regs_saved, i);
-    }
-}      
-\f
-/* If any register currently residing in the save area is referenced in X,
-   which is part of INSN, emit code to restore the register in front of INSN.
-   INSN_MODE is the mode to assign to any insns that we add.  */
+    SET_REGNO_REG_SET ((regset) data, i);
+}
 
+/* Walk X and record all referenced registers in REFERENCED_REGS.  */
 static void
-restore_referenced_regs (x, insn, insn_mode)
+mark_referenced_regs (x)
      rtx x;
-     rtx insn;
-     enum machine_mode insn_mode;
 {
   enum rtx_code code = GET_CODE (x);
-  char *fmt;
+  const char *fmt;
   int i, j;
 
-  if (code == CLOBBER)
-    return;
+  if (code == SET)
+    mark_referenced_regs (SET_SRC (x));
+  if (code == SET || code == CLOBBER)
+    {
+      x = SET_DEST (x);
+      code = GET_CODE (x);
+      if (code == REG || code == PC || code == CC0
+         || (code == SUBREG && GET_CODE (SUBREG_REG (x)) == REG))
+       return;
+    }
+  if (code == MEM || code == SUBREG)
+    {
+      x = XEXP (x, 0);
+      code = GET_CODE (x);
+    }
 
   if (code == REG)
     {
       int regno = REGNO (x);
+      int hardregno = (regno < FIRST_PSEUDO_REGISTER ? regno
+                      : reg_renumber[regno]);
 
-      /* If this is a pseudo, scan its memory location, since it might
-        involve the use of another register, which might be saved.  */
-
-      if (regno >= FIRST_PSEUDO_REGISTER
-         && reg_equiv_mem[regno] != 0)
-       restore_referenced_regs (XEXP (reg_equiv_mem[regno], 0),
-                                insn, insn_mode);
-      else if (regno >= FIRST_PSEUDO_REGISTER
-              && reg_equiv_address[regno] != 0)
-       restore_referenced_regs (reg_equiv_address[regno],
-                                insn, insn_mode);
-
-      /* Otherwise if this is a hard register, restore any piece of it that
-        is currently saved.  */
-
-      else if (regno < FIRST_PSEUDO_REGISTER)
+      if (hardregno >= 0)
        {
-         int numregs = HARD_REGNO_NREGS (regno, GET_MODE (x));
-         /* Save at most SAVEREGS at a time.  This can not be larger than
-            MOVE_MAX, because that causes insert_save_restore to fail.  */
-         int saveregs = MIN (numregs, MOVE_MAX / UNITS_PER_WORD);
-         int endregno = regno + numregs;
-
-         for (i = regno; i < endregno; i++)
-           if (TEST_HARD_REG_BIT (hard_regs_need_restore, i))
-             i += insert_save_restore (insn, 0, i, insn_mode, saveregs);
+         int nregs = HARD_REGNO_NREGS (hardregno, GET_MODE (x));
+         while (nregs-- > 0)
+           SET_HARD_REG_BIT (referenced_regs, hardregno + nregs);
        }
-
+      /* If this is a pseudo that did not get a hard register, scan its
+        memory location, since it might involve the use of another
+        register, which might be saved.  */
+      else if (reg_equiv_mem[regno] != 0)
+       mark_referenced_regs (XEXP (reg_equiv_mem[regno], 0));
+      else if (reg_equiv_address[regno] != 0)
+       mark_referenced_regs (reg_equiv_address[regno]);
       return;
     }
-         
+
   fmt = GET_RTX_FORMAT (code);
   for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
     {
       if (fmt[i] == 'e')
-       restore_referenced_regs (XEXP (x, i), insn, insn_mode);
+       mark_referenced_regs (XEXP (x, i));
       else if (fmt[i] == 'E')
        for (j = XVECLEN (x, i) - 1; j >= 0; j--)
-         restore_referenced_regs (XVECEXP (x, i, j), insn, insn_mode);
+         mark_referenced_regs (XVECEXP (x, i, j));
     }
 }
 \f
-/* Insert a sequence of insns to save or restore, SAVE_P says which,
-   REGNO.  Place these insns in front of INSN.  INSN_MODE is the mode
-   to assign to these insns.   MAXRESTORE is the maximum number of registers
-   which should be restored during this call (when SAVE_P == 0).  It should
-   never be less than 1 since we only work with entire registers.
+/* Insert a sequence of insns to restore.  Place these insns in front of
+   CHAIN if BEFORE_P is nonzero, behind the insn otherwise.  MAXRESTORE is
+   the maximum number of registers which should be restored during this call.
+   It should never be less than 1 since we only work with entire registers.
 
    Note that we have verified in init_caller_save that we can do this
    with a simple SET, so use it.  Set INSN_CODE to what we save there
@@ -645,16 +562,88 @@ restore_referenced_regs (x, insn, insn_mode)
    Return the extra number of registers saved.  */
 
 static int
-insert_save_restore (insn, save_p, regno, insn_mode, maxrestore)
-     rtx insn;
-     int save_p;
+insert_restore (chain, before_p, regno, maxrestore)
+     struct insn_chain *chain;
+     int before_p;
      int regno;
-     enum machine_mode insn_mode;
      int maxrestore;
 {
-  rtx pat;
-  enum insn_code code;
-  int i, numregs;
+  int i, k;
+  rtx pat = NULL_RTX;
+  enum insn_code code = CODE_FOR_nothing;
+  int numregs = 0;
+  struct insn_chain *new;
+
+  /* A common failure mode if register status is not correct in the RTL
+     is for this routine to be called with a REGNO we didn't expect to
+     save.  That will cause us to write an insn with a (nil) SET_DEST
+     or SET_SRC.  Instead of doing so and causing a crash later, check
+     for this common case and abort here instead.  This will remove one
+     step in debugging such problems.  */
+
+  if (regno_save_mem[regno][1] == 0)
+    abort ();
+
+  /* Get the pattern to emit and update our status.
+
+     See if we can restore `maxrestore' registers at once.  Work
+     backwards to the single register case.  */
+  for (i = maxrestore; i > 0; i--)
+    {
+      int j;
+      int ok = 1;
+
+      if (regno_save_mem[regno][i] == 0)
+       continue;
+
+      for (j = 0; j < i; j++)
+       if (! TEST_HARD_REG_BIT (hard_regs_saved, regno + j))
+         {
+           ok = 0;
+           break;
+         }
+      /* Must do this one restore at a time */
+      if (! ok)
+       continue;
+
+      numregs = i;
+      break;
+    }
+
+  pat = gen_rtx_SET (VOIDmode,
+                    gen_rtx_REG (GET_MODE (regno_save_mem[regno][numregs]), 
+                                 regno), 
+                    regno_save_mem[regno][numregs]);
+  code = reg_restore_code[regno][numregs];
+  new = insert_one_insn (chain, before_p, code, pat);
+
+  /* Clear status for all registers we restored.  */
+  for (k = 0; k < i; k++)
+    {
+      CLEAR_HARD_REG_BIT (hard_regs_saved, regno + k);
+      SET_REGNO_REG_SET (&new->dead_or_set, regno + k);
+      n_regs_saved--;
+    }
+
+
+
+  /* Tell our callers how many extra registers we saved/restored */
+  return numregs - 1;
+}
+
+/* Like insert_restore above, but save registers instead.  */
+static int
+insert_save (chain, before_p, regno, to_save)
+     struct insn_chain *chain;
+     int before_p;
+     int regno;
+     HARD_REG_SET *to_save;
+{
+  int i, k;
+  rtx pat = NULL_RTX;
+  enum insn_code code = CODE_FOR_nothing;
+  int numregs = 0;
+  struct insn_chain *new;
 
   /* A common failure mode if register status is not correct in the RTL
      is for this routine to be called with a REGNO we didn't expect to
@@ -666,14 +655,60 @@ insert_save_restore (insn, save_p, regno, insn_mode, maxrestore)
   if (regno_save_mem[regno][1] == 0)
     abort ();
 
-  /* If INSN is a CALL_INSN, we must insert our insns before any
-     USE insns in front of the CALL_INSN.  */
+  /* Get the pattern to emit and update our status.
+
+     See if we can save several registers with a single instruction.  
+     Work backwards to the single register case.  */
+  for (i = MOVE_MAX_WORDS; i > 0; i--)
+    {
+      int j;
+      int ok = 1;
+      if (regno_save_mem[regno][i] == 0)
+       continue;
+
+      for (j = 0; j < i; j++)
+       if (! TEST_HARD_REG_BIT (*to_save, regno + j))
+         {
+           ok = 0;
+           break;
+         }
+      /* Must do this one save at a time */
+      if (! ok)
+       continue;
+
+      numregs = i;
+      break;
+    }
+
+  pat = gen_rtx_SET (VOIDmode, regno_save_mem[regno][numregs],
+                    gen_rtx_REG (GET_MODE (regno_save_mem[regno][numregs]),
+                                 regno));
+  code = reg_save_code[regno][numregs];
+  new = insert_one_insn (chain, before_p, code, pat);
+
+  /* Set hard_regs_saved and dead_or_set for all the registers we saved.  */
+  for (k = 0; k < numregs; k++)
+    {
+      SET_HARD_REG_BIT (hard_regs_saved, regno + k);
+      SET_REGNO_REG_SET (&new->dead_or_set, regno + k);
+      n_regs_saved++;
+    }
 
-  if (GET_CODE (insn) == CALL_INSN)
-    while (GET_CODE (PREV_INSN (insn)) == INSN
-          && GET_CODE (PATTERN (PREV_INSN (insn))) == USE)
-      insn = PREV_INSN (insn);
+  /* Tell our callers how many extra registers we saved/restored */
+  return numregs - 1;
+}
 
+/* Emit a new caller-save insn and set the code.  */
+static struct insn_chain *
+insert_one_insn (chain, before_p, code, pat)
+     struct insn_chain *chain;
+     int before_p;
+     enum insn_code code;
+     rtx pat;
+{
+  rtx insn = chain->insn;
+  struct insn_chain *new;
+  
 #ifdef HAVE_cc0
   /* If INSN references CC0, put our insns in front of the insn that sets
      CC0.  This is always safe, since the only way we could be passed an
@@ -682,99 +717,76 @@ insert_save_restore (insn, save_p, regno, insn_mode, maxrestore)
      reference CC0.  Guard against non-INSN's like CODE_LABEL.  */
 
   if ((GET_CODE (insn) == INSN || GET_CODE (insn) == JUMP_INSN)
+      && before_p
       && reg_referenced_p (cc0_rtx, PATTERN (insn)))
-    insn = prev_nonnote_insn (insn);
+    chain = chain->prev, insn = chain->insn;
 #endif
 
-  /* Get the pattern to emit and update our status.  */
-  if (save_p)
+  new = new_insn_chain ();
+  if (before_p)
     {
-      int i, j, k;
-      int ok;
+      rtx link;
 
-      /* See if we can save several registers with a single instruction.  
-        Work backwards to the single register case.  */
-      for (i = MOVE_MAX / UNITS_PER_WORD; i > 0; i--)
+      new->prev = chain->prev;
+      if (new->prev != 0)
+       new->prev->next = new;
+      else
+       reload_insn_chain = new;
+
+      chain->prev = new;
+      new->next = chain;
+      new->insn = emit_insn_before (pat, insn);
+      /* ??? It would be nice if we could exclude the already / still saved
+        registers from the live sets.  */
+      COPY_REG_SET (&new->live_throughout, &chain->live_throughout);
+      /* Registers that die in CHAIN->INSN still live in the new insn.  */
+      for (link = REG_NOTES (chain->insn); link; link = XEXP (link, 1))
        {
-         ok = 1;
-         if (regno_save_mem[regno][i] != 0)
-           for (j = 0; j < i; j++)
-             {
-               if (! call_used_regs[regno + j] || call_fixed_regs[regno + j]
-                   || ! TEST_HARD_REG_BIT (hard_regs_live, regno + j)
-                   || TEST_HARD_REG_BIT (hard_regs_saved, regno + j))
-                 ok = 0;
-             }
-         else 
-           continue;
-
-         /* Must do this one save at a time */
-         if (! ok)
-           continue;
-
-          pat = gen_rtx (SET, VOIDmode, regno_save_mem[regno][i],
-                    gen_rtx (REG, GET_MODE (regno_save_mem[regno][i]), regno));
-          code = reg_save_code[regno][i];
-
-         /* Set hard_regs_saved for all the registers we saved.  */
-         for (k = 0; k < i; k++)
+         if (REG_NOTE_KIND (link) == REG_DEAD)
            {
-             SET_HARD_REG_BIT (hard_regs_saved, regno + k);
-             SET_HARD_REG_BIT (hard_regs_need_restore, regno + k);
-             n_regs_saved++;
+             rtx reg = XEXP (link, 0);
+             int regno, i;
+
+             if (GET_CODE (reg) != REG)
+               abort ();
+
+             regno = REGNO (reg);
+             if (regno >= FIRST_PSEUDO_REGISTER)
+               regno = reg_renumber[regno];
+             if (regno < 0)
+               continue;
+             for (i = HARD_REGNO_NREGS (regno, GET_MODE (reg)) - 1;
+                  i >= 0; i--)
+               SET_REGNO_REG_SET (&new->live_throughout, regno + i);
            }
-
-         numregs = i;
-         break;
-        }
+       }
+      CLEAR_REG_SET (&new->dead_or_set);
+      if (chain->insn == BLOCK_HEAD (chain->block))
+       BLOCK_HEAD (chain->block) = new->insn;
     }
   else
     {
-      int i, j, k;
-      int ok;
-
-      /* See if we can restore `maxrestore' registers at once.  Work
-        backwards to the single register case.  */
-      for (i = maxrestore; i > 0; i--)
-       {
-         ok = 1;
-         if (regno_save_mem[regno][i])
-           for (j = 0; j < i; j++)
-             {
-               if (! TEST_HARD_REG_BIT (hard_regs_need_restore, regno + j))
-                 ok = 0;
-             }
-         else
-           continue;
-
-         /* Must do this one restore at a time */
-         if (! ok)
-           continue;
-           
-          pat = gen_rtx (SET, VOIDmode,
-                        gen_rtx (REG, GET_MODE (regno_save_mem[regno][i]), 
-                                 regno), 
-                        regno_save_mem[regno][i]);
-          code = reg_restore_code[regno][i];
-
-
-         /* Clear status for all registers we restored.  */
-         for (k = 0; k < i; k++)
-           {
-             CLEAR_HARD_REG_BIT (hard_regs_need_restore, regno + k);
-             n_regs_saved--;
-           }
-
-         numregs = i;
-         break;
-        }
+      new->next = chain->next;
+      if (new->next != 0)
+       new->next->prev = new;
+      chain->next = new;
+      new->prev = chain;
+      new->insn = emit_insn_after (pat, insn);
+      /* ??? It would be nice if we could exclude the already / still saved
+        registers from the live sets, and observe REG_UNUSED notes.  */
+      COPY_REG_SET (&new->live_throughout, &chain->live_throughout);
+      /* Registers that are set in CHAIN->INSN live in the new insn.
+         (Unless there is a REG_UNUSED note for them, but we don't
+         look for them here.) */
+      note_stores (PATTERN (chain->insn), add_stored_regs,
+                  &new->live_throughout);
+      CLEAR_REG_SET (&new->dead_or_set);
+      if (chain->insn == BLOCK_END (chain->block))
+       BLOCK_END (chain->block) = new->insn;
     }
-  /* Emit the insn and set the code and mode.  */
-
-  insn = emit_insn_before (pat, insn);
-  PUT_MODE (insn, insn_mode);
-  INSN_CODE (insn) = code;
+  new->block = chain->block;
+  new->is_caller_save_insn = 1;
 
-  /* Tell our callers how many extra registers we saved/restored */
-  return numregs - 1;
+  INSN_CODE (new->insn) = code;
+  return new;
 }