OSDN Git Service

* config/freebsd-spec.h (FBSD_CPP_PREDEFINES): Remove.
[pf3gnuchains/gcc-fork.git] / gcc / caller-save.c
index cbb19fb..53f3aa5 100644 (file)
@@ -1,25 +1,28 @@
 /* Save and restore call-clobbered registers which are live across a call.
-   Copyright (C) 1989, 1992, 94-95, 1997, 1998 Free Software Foundation, Inc.
+   Copyright (C) 1989, 1992, 1994, 1995, 1997, 1998,
+   1999, 2000, 2001, 2002 Free Software Foundation, Inc.
 
-This file is part of GNU CC.
+This file is part of GCC.
 
-GNU CC is free software; you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation; either version 2, or (at your option)
-any later version.
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 2, or (at your option) any later
+version.
 
-GNU CC is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-GNU General Public License for more details.
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the 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, 59 Temple Place - Suite 330,
-Boston, MA 02111-1307, USA.  */
+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.  */
 
 #include "config.h"
 #include "system.h"
+#include "coretypes.h"
+#include "tm.h"
 #include "rtl.h"
 #include "insn-config.h"
 #include "flags.h"
@@ -28,8 +31,10 @@ Boston, MA 02111-1307, 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
@@ -61,10 +66,10 @@ static rtx
    when we emit them, the addresses might not be valid, so they might not
    be recognized.  */
 
-static enum insn_code 
-  reg_save_code[FIRST_PSEUDO_REGISTER][MAX_MOVE_MAX / MIN_UNITS_PER_WORD + 1];
-static enum insn_code 
-  reg_restore_code[FIRST_PSEUDO_REGISTER][MAX_MOVE_MAX / MIN_UNITS_PER_WORD + 1];
+static int
+  reg_save_code[FIRST_PSEUDO_REGISTER][MAX_MACHINE_MODE];
+static int 
+  reg_restore_code[FIRST_PSEUDO_REGISTER][MAX_MACHINE_MODE];
 
 /* Set of hard regs currently residing in save area (during insn scan).  */
 
@@ -83,14 +88,16 @@ static HARD_REG_SET referenced_regs;
 static HARD_REG_SET this_insn_sets;
 
 
-static void mark_set_regs              PROTO((rtx, rtx));
-static void mark_referenced_regs       PROTO((rtx));
-static int insert_save                 PROTO((struct insn_chain *, int, int,
-                                              HARD_REG_SET *));
-static int insert_restore              PROTO((struct insn_chain *, int, int,
-                                              int));
-static void insert_one_insn            PROTO((struct insn_chain *, int,
-                                              enum insn_code, rtx));
+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 *,
+                                                enum machine_mode *));
+static int insert_restore              PARAMS ((struct insn_chain *, int, int,
+                                                int, enum machine_mode *));
+static struct insn_chain *insert_one_insn PARAMS ((struct insn_chain *, int,
+                                                  int, rtx));
+static void add_stored_regs            PARAMS ((rtx, rtx, void *));
 \f
 /* Initialize for caller-save.
 
@@ -105,11 +112,14 @@ static void insert_one_insn               PROTO((struct insn_chain *, int,
 void
 init_caller_save ()
 {
-  char *first_obj = (char *) oballoc (0);
   rtx addr_reg;
   int offset;
   rtx address;
   int i, j;
+  enum machine_mode mode;
+  rtx savepat, restpat;
+  rtx test_reg, test_mem;
+  rtx saveinsn, restinsn;
 
   /* First find all the registers that we need to deal with and all
      the modes that they can have.  If we can't find a mode to use,
@@ -121,7 +131,8 @@ init_caller_save ()
        {
          for (j = 1; j <= MOVE_MAX_WORDS; j++)
            {
-             regno_save_mode[i][j] = HARD_REGNO_CALLER_SAVE_MODE (i, j);
+             regno_save_mode[i][j] = HARD_REGNO_CALLER_SAVE_MODE (i, j,
+                                                                  VOIDmode);
              if (regno_save_mode[i][j] == VOIDmode && j == 1)
                {
                  call_fixed_regs[i] = 1;
@@ -145,7 +156,9 @@ init_caller_save ()
      that register in every mode we will use to save registers.  */
 
   for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
-    if (TEST_HARD_REG_BIT (reg_class_contents[(int) BASE_REG_CLASS], i))
+    if (TEST_HARD_REG_BIT
+       (reg_class_contents
+        [(int) MODE_BASE_REG_CLASS (regno_save_mode [i][1])], i))
       break;
 
   if (i == FIRST_PSEUDO_REGISTER)
@@ -171,50 +184,73 @@ init_caller_save ()
     address = addr_reg;
 
   /* Next we try to form an insn to save and restore the register.  We
-     see if such an insn is recognized and meets its constraints.  */
+     see if such an insn is recognized and meets its constraints. 
+
+     To avoid lots of unnecessary RTL allocation, we construct all the RTL
+     once, then modify the memory and register operands in-place.  */
+
+  test_reg = gen_rtx_REG (VOIDmode, 0);
+  test_mem = gen_rtx_MEM (VOIDmode, address);
+  savepat = gen_rtx_SET (VOIDmode, test_mem, test_reg);
+  restpat = gen_rtx_SET (VOIDmode, test_reg, test_mem);
 
-  start_sequence ();
+  saveinsn = gen_rtx_INSN (VOIDmode, 0, 0, 0, 0, 0, savepat, -1, 0, 0);
+  restinsn = gen_rtx_INSN (VOIDmode, 0, 0, 0, 0, 0, restpat, -1, 0, 0);
 
   for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
-    for (j = 1; j <= MOVE_MAX_WORDS; j++)
-      if (regno_save_mode[i][j] != VOIDmode)
+    for (mode = 0 ; mode < MAX_MACHINE_MODE; mode++)
+      if (HARD_REGNO_MODE_OK (i, mode))
         {
-         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;
 
-         reg_save_code[i][j] = recog_memoized (saveinsn);
-         reg_restore_code[i][j] = recog_memoized (restinsn);
+         /* Update the register number and modes of the register
+            and memory operand.  */
+         REGNO (test_reg) = i;
+         PUT_MODE (test_reg, mode);
+         PUT_MODE (test_mem, mode);
+
+         /* Force re-recognition of the modified insns.  */
+         INSN_CODE (saveinsn) = -1;
+         INSN_CODE (restinsn) = -1;
+
+         reg_save_code[i][mode] = recog_memoized (saveinsn);
+         reg_restore_code[i][mode] = 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);
+         ok = (reg_save_code[i][mode] != -1
+               && reg_restore_code[i][mode] != -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)
            {
-             regno_save_mode[i][j] = VOIDmode;
-             if (j == 1)
-               {
-                 call_fixed_regs[i] = 1;
-                 SET_HARD_REG_BIT (call_fixed_reg_set, i);
-               }
+             reg_save_code[i][mode] = -1;
+             reg_restore_code[i][mode] = -1;
            }
-      }
-
-  end_sequence ();
+        }
+      else
+       {
+         reg_save_code[i][mode] = -1;
+         reg_restore_code[i][mode] = -1;
+       }
 
-  obfree (first_obj);
+  for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+    for (j = 1; j <= MOVE_MAX_WORDS; j++)
+      if (reg_save_code [i][regno_save_mode[i][j]] == -1)
+       {
+         regno_save_mode[i][j] = VOIDmode;
+         if (j == 1)
+           {
+             call_fixed_regs[i] = 1;
+             SET_HARD_REG_BIT (call_fixed_reg_set, i);
+           }
+       }
 }
 \f
 /* Initialize save areas by showing that we haven't allocated any yet.  */
@@ -251,6 +287,7 @@ void
 setup_save_areas ()
 {
   int i, j, k;
+  unsigned int r;
   HARD_REG_SET hard_regs_used;
 
   /* Allocate space in the save area for the largest multi-register
@@ -262,16 +299,13 @@ setup_save_areas ()
   for (i = FIRST_PSEUDO_REGISTER; i < max_regno; i++)
     if (reg_renumber[i] >= 0 && REG_N_CALLS_CROSSED (i) > 0)
       {
-       int regno = reg_renumber[i];
-       int endregno 
+       unsigned int regno = reg_renumber[i];
+       unsigned int endregno 
          = regno + HARD_REGNO_NREGS (regno, GET_MODE (regno_reg_rtx[i]));
-       int nregs = endregno - regno;
 
-       for (j = 0; j < nregs; j++)
-         {
-           if (call_used_regs[regno+j]) 
-             SET_HARD_REG_BIT (hard_regs_used, regno+j);
-         }
+       for (r = regno; r < endregno; r++)
+         if (call_used_regs[r])
+           SET_HARD_REG_BIT (hard_regs_used, r);
       }
 
   /* Now run through all the call-used hard-registers and allocate
@@ -314,23 +348,29 @@ setup_save_areas ()
 
        /* 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);
-         }
+         /* This should not depend on WORDS_BIG_ENDIAN.
+            The order of words in regs is the same as in memory.  */
+         regno_save_mem[i + k][1]
+           = adjust_address_nv (regno_save_mem[i][j],
+                                regno_save_mode[i + k][1],
+                                k * UNITS_PER_WORD);
       }
+
+  /* Now loop again and set the alias set of any save areas we made to
+     the alias set used to represent frame objects.  */
+  for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+    for (j = MOVE_MAX_WORDS; j > 0; j--)
+      if (regno_save_mem[i][j] != 0)
+       set_mem_alias_set (regno_save_mem[i][j], get_frame_alias_set ());
 }
 \f
 /* Find the places where hard regs are live across calls and save them.  */
+
 void
 save_call_clobbered_regs ()
 {
   struct insn_chain *chain, *next;
+  enum machine_mode save_mode [FIRST_PSEUDO_REGISTER];
 
   CLEAR_HARD_REG_SET (hard_regs_saved);
   n_regs_saved = 0;
@@ -366,24 +406,56 @@ save_call_clobbered_regs ()
 
              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);
+                 regno += insert_restore (chain, 1, regno, MOVE_MAX_WORDS, save_mode);
            }
 
          if (code == CALL_INSN)
            {
-             rtx x;
-             int regno, nregs;
+             int regno;
              HARD_REG_SET hard_regs_to_save;
 
              /* Use the register life information in CHAIN to compute which
-                regs are live before the call.  */
-             REG_SET_TO_HARD_REG_SET (hard_regs_to_save, chain->live_before);
-             compute_use_by_pseudos (&hard_regs_to_save, chain->live_before);
+                regs are live during the call.  */
+             REG_SET_TO_HARD_REG_SET (hard_regs_to_save,
+                                      &chain->live_throughout);
+             /* Save hard registers always in the widest mode available.  */
+             for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
+               if (TEST_HARD_REG_BIT (hard_regs_to_save, regno))
+                 save_mode [regno] = regno_save_mode [regno][1];
+               else
+                 save_mode [regno] = VOIDmode;
+
+             /* Look through all live pseudos, mark their hard registers
+                and choose proper mode for saving.  */
+             EXECUTE_IF_SET_IN_REG_SET
+               (&chain->live_throughout, FIRST_PSEUDO_REGISTER, regno,
+                {
+                  int r = reg_renumber[regno];
+                  int nregs;
+
+                  if (r >= 0)
+                    {
+                      enum machine_mode mode;
+
+                      nregs = HARD_REGNO_NREGS (r, PSEUDO_REGNO_MODE (regno));
+                      mode = HARD_REGNO_CALLER_SAVE_MODE
+                               (r, nregs, PSEUDO_REGNO_MODE (regno));
+                      if (GET_MODE_BITSIZE (mode)
+                          > GET_MODE_BITSIZE (save_mode[r]))
+                        save_mode[r] = mode;
+                      while (nregs-- > 0)
+                        SET_HARD_REG_BIT (hard_regs_to_save, r + nregs);
+                    }
+                  else
+                    abort ();
+                });
 
              /* Record all registers set in this call insn.  These don't need
-                to be saved.  */
+                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);
+             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);
@@ -391,48 +463,9 @@ save_call_clobbered_regs ()
              AND_COMPL_HARD_REG_SET (hard_regs_to_save, hard_regs_saved);
              AND_HARD_REG_SET (hard_regs_to_save, call_used_reg_set);
 
-             /* Registers used for function parameters need not be saved.  */
-             for (x = CALL_INSN_FUNCTION_USAGE (insn); x != 0;
-                  x = XEXP (x, 1))
-               {
-                 rtx y;
-
-                 if (GET_CODE (XEXP (x, 0)) != USE)
-                   continue;
-                 y = XEXP (XEXP (x, 0), 0);
-                 if (GET_CODE (y) != REG)
-                   abort ();
-                 regno = REGNO (y);
-                 if (REGNO (y) >= FIRST_PSEUDO_REGISTER)
-                   abort ();
-                 nregs = HARD_REGNO_NREGS (regno, GET_MODE (y));
-                 while (nregs-- > 0)
-                   CLEAR_HARD_REG_BIT (hard_regs_to_save, regno + nregs);
-               }
-
-             /* Neither do registers for which we find a death note.  */
-             for (x = REG_NOTES (insn); x != 0; x = XEXP (x, 1))
-               {
-                 rtx y = XEXP (x, 0);
-
-                 if (REG_NOTE_KIND (x) != REG_DEAD)
-                   continue;
-                 if (GET_CODE (y) != REG)
-                   abort ();
-                 regno = REGNO (y);
-
-                 if (regno >= FIRST_PSEUDO_REGISTER)
-                   regno = reg_renumber[regno];
-                 if (regno < 0)
-                   continue;
-                 nregs = HARD_REGNO_NREGS (regno, GET_MODE (y));
-                 while (nregs-- > 0)
-                   CLEAR_HARD_REG_BIT (hard_regs_to_save, regno + nregs);                
-               }
-               
              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);
+                 regno += insert_save (chain, 1, regno, &hard_regs_to_save, save_mode);
 
              /* Must recompute n_regs_saved.  */
              n_regs_saved = 0;
@@ -453,7 +486,7 @@ save_call_clobbered_regs ()
            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);
+                                        regno, MOVE_MAX_WORDS, save_mode);
        }
     }  
 }
@@ -463,28 +496,68 @@ save_call_clobbered_regs ()
    been assigned hard regs have had their register number changed already,
    so we can ignore pseudos.  */
 static void
-mark_set_regs (reg, setter)
+mark_set_regs (reg, setter, data)
      rtx reg;
      rtx setter ATTRIBUTE_UNUSED;
+     void *data ATTRIBUTE_UNUSED;
 {
-  register int regno, endregno, i;
+  int regno, endregno, i;
   enum machine_mode mode = GET_MODE (reg);
-  int word = 0;
 
   if (GET_CODE (reg) == SUBREG)
     {
-      word = SUBREG_WORD (reg);
+      rtx inner = SUBREG_REG (reg);
+      if (GET_CODE (inner) != REG || REGNO (inner) >= FIRST_PSEUDO_REGISTER)
+       return;
+
+      regno = subreg_hard_regno (reg, 1);
+    }
+  else if (GET_CODE (reg) == REG
+          && REGNO (reg) < FIRST_PSEUDO_REGISTER)
+    regno = REGNO (reg);
+  else
+    return;
+
+  endregno = regno + HARD_REGNO_NREGS (regno, mode);
+
+  for (i = regno; i < endregno; i++)
+    SET_HARD_REG_BIT (this_insn_sets, i);
+}
+
+/* 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
+add_stored_regs (reg, setter, data)
+     rtx reg;
+     rtx setter;
+     void *data;
+{
+  int regno, endregno, i;
+  enum machine_mode mode = GET_MODE (reg);
+  int offset = 0;
+
+  if (GET_CODE (setter) == CLOBBER)
+    return;
+
+  if (GET_CODE (reg) == SUBREG && GET_CODE (SUBREG_REG (reg)) == REG)
+    {
+      offset = subreg_regno_offset (REGNO (SUBREG_REG (reg)),
+                                   GET_MODE (SUBREG_REG (reg)),
+                                   SUBREG_BYTE (reg),
+                                   GET_MODE (reg));
       reg = SUBREG_REG (reg);
     }
 
   if (GET_CODE (reg) != REG || REGNO (reg) >= FIRST_PSEUDO_REGISTER)
     return;
 
-  regno = REGNO (reg) + word;
+  regno = REGNO (reg) + offset;
   endregno = regno + HARD_REGNO_NREGS (regno, mode);
 
   for (i = regno; i < endregno; i++)
-    SET_HARD_REG_BIT (this_insn_sets, i);
+    SET_REGNO_REG_SET ((regset) data, i);
 }
 
 /* Walk X and record all referenced registers in REFERENCED_REGS.  */
@@ -493,7 +566,7 @@ mark_referenced_regs (x)
      rtx x;
 {
   enum rtx_code code = GET_CODE (x);
-  char *fmt;
+  const char *fmt;
   int i, j;
 
   if (code == SET)
@@ -503,7 +576,14 @@ mark_referenced_regs (x)
       x = SET_DEST (x);
       code = GET_CODE (x);
       if (code == REG || code == PC || code == CC0
-         || (code == SUBREG && GET_CODE (SUBREG_REG (x)) == REG))
+         || (code == SUBREG && GET_CODE (SUBREG_REG (x)) == REG
+             /* If we're setting only part of a multi-word register,
+                we shall mark it as referenced, because the words
+                that are not being set should be restored.  */
+             && ((GET_MODE_SIZE (GET_MODE (x))
+                  >= GET_MODE_SIZE (GET_MODE (SUBREG_REG (x))))
+                 || (GET_MODE_SIZE (GET_MODE (SUBREG_REG (x)))
+                     <= UNITS_PER_WORD))))
        return;
     }
   if (code == MEM || code == SUBREG)
@@ -559,16 +639,19 @@ mark_referenced_regs (x)
    Return the extra number of registers saved.  */
 
 static int
-insert_restore (chain, before_p, regno, maxrestore)
+insert_restore (chain, before_p, regno, maxrestore, save_mode)
      struct insn_chain *chain;
      int before_p;
      int regno;
      int maxrestore;
+     enum machine_mode *save_mode;
 {
-  int i;
+  int i, k;
   rtx pat = NULL_RTX;
-  enum insn_code code = CODE_FOR_nothing;
-  int numregs = 0;
+  int code;
+  unsigned int numregs = 0;
+  struct insn_chain *new;
+  rtx mem;
 
   /* 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
@@ -586,7 +669,7 @@ insert_restore (chain, before_p, regno, maxrestore)
      backwards to the single register case.  */
   for (i = maxrestore; i > 0; i--)
     {
-      int j, k;
+      int j;
       int ok = 1;
 
       if (regno_save_mem[regno][i] == 0)
@@ -602,41 +685,50 @@ insert_restore (chain, before_p, regno, maxrestore)
       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_saved, regno + k);
-         n_regs_saved--;
-       }
-
       numregs = i;
       break;
     }
 
-  insert_one_insn (chain, before_p, code, pat);
+  mem = regno_save_mem [regno][numregs];
+  if (save_mode [regno] != VOIDmode
+      && save_mode [regno] != GET_MODE (mem)
+      && numregs == (unsigned int) HARD_REGNO_NREGS (regno, save_mode [regno]))
+    mem = adjust_address (mem, save_mode[regno], 0);
+  pat = gen_rtx_SET (VOIDmode,
+                    gen_rtx_REG (GET_MODE (mem), 
+                                 regno), mem);
+  code = reg_restore_code[regno][GET_MODE (mem)];
+  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)
+insert_save (chain, before_p, regno, to_save, save_mode)
      struct insn_chain *chain;
      int before_p;
      int regno;
      HARD_REG_SET *to_save;
+     enum machine_mode *save_mode;
 {
   int i;
+  unsigned int k;
   rtx pat = NULL_RTX;
-  enum insn_code code = CODE_FOR_nothing;
-  int numregs = 0;
+  int code;
+  unsigned int numregs = 0;
+  struct insn_chain *new;
+  rtx mem;
 
   /* 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
@@ -654,7 +746,7 @@ insert_save (chain, before_p, regno, to_save)
      Work backwards to the single register case.  */
   for (i = MOVE_MAX_WORDS; i > 0; i--)
     {
-      int j, k;
+      int j;
       int ok = 1;
       if (regno_save_mem[regno][i] == 0)
        continue;
@@ -669,34 +761,39 @@ insert_save (chain, before_p, regno, to_save)
       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++)
-       {
-         SET_HARD_REG_BIT (hard_regs_saved, regno + k);
-         n_regs_saved++;
-       }
-
       numregs = i;
       break;
     }
 
-  insert_one_insn (chain, before_p, code, pat);
+  mem = regno_save_mem [regno][numregs];
+  if (save_mode [regno] != VOIDmode
+      && save_mode [regno] != GET_MODE (mem)
+      && numregs == (unsigned int) HARD_REGNO_NREGS (regno, save_mode [regno]))
+    mem = adjust_address (mem, save_mode[regno], 0);
+  pat = gen_rtx_SET (VOIDmode, mem,
+                    gen_rtx_REG (GET_MODE (mem),
+                                 regno));
+  code = reg_save_code[regno][GET_MODE (mem)];
+  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++;
+    }
 
   /* Tell our callers how many extra registers we saved/restored */
   return numregs - 1;
 }
 
 /* Emit a new caller-save insn and set the code.  */
-static void
+static struct insn_chain *
 insert_one_insn (chain, before_p, code, pat)
      struct insn_chain *chain;
      int before_p;
-     enum insn_code code;
+     int code;
      rtx pat;
 {
   rtx insn = chain->insn;
@@ -718,6 +815,8 @@ insert_one_insn (chain, before_p, code, pat)
   new = new_insn_chain ();
   if (before_p)
     {
+      rtx link;
+
       new->prev = chain->prev;
       if (new->prev != 0)
        new->prev->next = new;
@@ -727,8 +826,33 @@ insert_one_insn (chain, before_p, code, pat)
       chain->prev = new;
       new->next = chain;
       new->insn = emit_insn_before (pat, insn);
-      if (chain->insn == basic_block_head[chain->block])
-       basic_block_head[chain->block] = new->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))
+       {
+         if (REG_NOTE_KIND (link) == REG_DEAD)
+           {
+             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);
+           }
+       }
+      CLEAR_REG_SET (&new->dead_or_set);
+      if (chain->insn == BLOCK_HEAD (chain->block))
+       BLOCK_HEAD (chain->block) = new->insn;
     }
   else
     {
@@ -738,11 +862,21 @@ insert_one_insn (chain, before_p, code, pat)
       chain->next = new;
       new->prev = chain;
       new->insn = emit_insn_after (pat, insn);
-      if (chain->insn == basic_block_end[chain->block])
-       basic_block_end[chain->block] = new->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;
     }
   new->block = chain->block;
   new->is_caller_save_insn = 1;
 
   INSN_CODE (new->insn) = code;
+  return new;
 }