s390.c (struct s390_frame_layout): New struct as element of struct machine_function.
authorAndreas Krebbel <krebbel1@de.ibm.com>
Thu, 12 Aug 2004 17:40:02 +0000 (17:40 +0000)
committerUlrich Weigand <uweigand@gcc.gnu.org>
Thu, 12 Aug 2004 17:40:02 +0000 (17:40 +0000)
2004-08-12  Andreas Krebbel  <krebbel1@de.ibm.com>

* config/s390/s390.c (struct s390_frame_layout): New struct as element
of struct machine_function.
(cfun->machine->frame_size): Moved into cfun->machine->frame_layout and
changed all uses.
(cfun->machine->save_fprs_p): Replaced by cfun_save_high_fprs and
changed all uses.
(cfun_frame_layout, cfun_save_high_fprs_p, cfun_gprs_save_area_size)
(cfun_set_fpr_bit, cfun_fpr_bit_p): New macros.
(s390_frame_area, s390_register_info): New functions.
(s390_optimize_prolog): Renamed to s390_optimize_prologue.  Added check
for base register.
(s390_return_addr_rtx, s390_return_address_offset)
(s390_va_start, s390_gimplify_va_arg)
(s390_emit_prologue, s390_emit_epilogue): Adjusted for new stack
layouts.
(s390_frame_info): Functionality partly moved to s390_register_info.
Made adaptions for new stack layout.
(save_gprs, restore_gprs): Changed meaning of second parameter and
adapted all callers.

* config/s390/s390.h (s390_backchain_string): New global variable.
(MASK_BACKCHAIN): Removed definition.
(TARGET_BACKCHAIN): Changed check.
(TARGET_KERNEL_BACKCHAIN): New macro.
(TARGET_SWITCHES): Removed entries of "backchain" and "no-backchain".
(TARGET_OPTIONS): Added "backchain", "no-backchain" and
"kernel-backchain".
(DYNAMIC_CHAIN_ADDRESS): Adjusted for new stack layouts.

* config/s390/s390.md ("allocate_stack"): Added TARGET_KERNEL_BACKCHAIN
as condition.  Adjusted for new stack layout.

* doc/invoke.texi: Added documentation for new option
"-mkernel-backchain" and adjusted documentation of "-mbackchain" and
"-mno-backchain".

From-SVN: r85882

gcc/ChangeLog
gcc/config/s390/s390.c
gcc/config/s390/s390.h
gcc/config/s390/s390.md
gcc/doc/invoke.texi

index 3e651a98b54eb6b9eb4a9396abddbfc243af0ae1..6881fa6a5ec529779435c5aed8c0958c32470a95 100644 (file)
@@ -1,3 +1,41 @@
+2004-08-12  Andreas Krebbel  <krebbel1@de.ibm.com>
+
+       * config/s390/s390.c (struct s390_frame_layout): New struct as element
+       of struct machine_function.
+       (cfun->machine->frame_size): Moved into cfun->machine->frame_layout and
+       changed all uses.
+       (cfun->machine->save_fprs_p): Replaced by cfun_save_high_fprs and
+       changed all uses.
+       (cfun_frame_layout, cfun_save_high_fprs_p, cfun_gprs_save_area_size)
+       (cfun_set_fpr_bit, cfun_fpr_bit_p): New macros.
+       (s390_frame_area, s390_register_info): New functions.
+       (s390_optimize_prolog): Renamed to s390_optimize_prologue.  Added check
+       for base register.
+       (s390_return_addr_rtx, s390_return_address_offset)
+       (s390_va_start, s390_gimplify_va_arg)
+       (s390_emit_prologue, s390_emit_epilogue): Adjusted for new stack
+       layouts.
+       (s390_frame_info): Functionality partly moved to s390_register_info.
+       Made adaptions for new stack layout.
+       (save_gprs, restore_gprs): Changed meaning of second parameter and
+       adapted all callers.
+
+       * config/s390/s390.h (s390_backchain_string): New global variable.
+       (MASK_BACKCHAIN): Removed definition.
+       (TARGET_BACKCHAIN): Changed check.
+       (TARGET_KERNEL_BACKCHAIN): New macro.
+       (TARGET_SWITCHES): Removed entries of "backchain" and "no-backchain".
+       (TARGET_OPTIONS): Added "backchain", "no-backchain" and
+       "kernel-backchain".
+       (DYNAMIC_CHAIN_ADDRESS): Adjusted for new stack layouts.
+
+       * config/s390/s390.md ("allocate_stack"): Added TARGET_KERNEL_BACKCHAIN
+       as condition.  Adjusted for new stack layout.
+
+       * doc/invoke.texi: Added documentation for new option
+       "-mkernel-backchain" and adjusted documentation of "-mbackchain" and
+       "-mno-backchain".
+
 2004-08-12  Paul Brook  <paul@codesourcery.com>
 
        * config/arm/lib1funcs.asm (ARM_FUNC_ALIAS): Also alias _L__name.
index 083d7259fdb65e181edf161283b26251278f681f..be3aeae59b4edaac0ccf1b456e308763f30d9486 100644 (file)
@@ -200,24 +200,54 @@ enum processor_flags s390_arch_flags;
 const char *s390_tune_string;          /* for -mtune=<xxx> */
 const char *s390_arch_string;          /* for -march=<xxx> */
 
-/* Define the structure for the machine field in struct function.  */
-
-struct machine_function GTY(())
-{
-  /* Set, if some of the fprs 8-15 need to be saved (64 bit abi).  */
-  int save_fprs_p;
-
-  /* Set if return address needs to be saved.  */
-  bool save_return_addr_p;
-
+/* String to specify backchain mode.  */
+const char *s390_backchain_string = ""; /* "" no-backchain ,"1" backchain,
+                                          "2" kernel-backchain */
+
+/* The following structure is embedded in the machine 
+   specific part of struct function.  */
+
+struct s390_frame_layout GTY (())
+{
+  /* Offset within stack frame.  */
+  HOST_WIDE_INT gprs_offset;
+  HOST_WIDE_INT f0_offset;
+  HOST_WIDE_INT f4_offset;
+  HOST_WIDE_INT f8_offset;
+  HOST_WIDE_INT backchain_offset;
+  
   /* Number of first and last gpr to be saved, restored.  */
   int first_save_gpr;
   int first_restore_gpr;
   int last_save_gpr;
   int last_restore_gpr;
 
+  /* Bits standing for floating point registers. Set, if the 
+     respective register has to be saved. Starting with reg 16 (f0) 
+     at the rightmost bit.
+     Bit 15 -  8  7  6  5  4  3  2  1  0
+     fpr 15 -  8  7  5  3  1  6  4  2  0
+     reg 31 - 24 23 22 21 20 19 18 17 16  */
+  unsigned int fpr_bitmap;
+
+  /* Number of floating point registers f8-f15 which must be saved.  */
+  int high_fprs;
+
+  /* Set if return address needs to be saved.  */
+  bool save_return_addr_p;
+
+  /* Set if backchain needs to be saved.  */
+  bool save_backchain_p;
+
   /* Size of stack frame.  */
   HOST_WIDE_INT frame_size;
+};
+
+/* Define the structure for the machine field in struct function.  */
+
+struct machine_function GTY(())
+{
+  struct s390_frame_layout frame_layout;
 
   /* Literal pool base register.  */
   rtx base_reg;
@@ -226,6 +256,17 @@ struct machine_function GTY(())
   const char *some_ld_name;
 };
 
+/* Few accessor macros for struct cfun->machine->s390_frame_layout.  */
+
+#define cfun_frame_layout (cfun->machine->frame_layout)
+#define cfun_save_high_fprs_p (!!cfun_frame_layout.high_fprs)
+#define cfun_gprs_save_area_size ((cfun_frame_layout.last_save_gpr -           \
+  cfun_frame_layout.first_save_gpr + 1) * UNITS_PER_WORD)
+#define cfun_set_fpr_bit(BITNUM) (cfun->machine->frame_layout.fpr_bitmap |=    \
+  (1 << (BITNUM)))
+#define cfun_fpr_bit_p(BITNUM) (!!(cfun->machine->frame_layout.fpr_bitmap &    \
+  (1 << (BITNUM))))
+
 static int s390_match_ccmode_set (rtx, enum machine_mode);
 static int s390_branch_condition_mask (rtx);
 static const char *s390_branch_condition_mnemonic (rtx, int);
@@ -246,8 +287,10 @@ static void find_constant_pool_ref (rtx, rtx *);
 static void replace_constant_pool_ref (rtx *, rtx, rtx);
 static rtx find_ltrel_base (rtx);
 static void replace_ltrel_base (rtx *);
-static void s390_optimize_prolog (bool);
+static void s390_optimize_prologue (bool);
 static int find_unused_clobbered_reg (void);
+static void s390_frame_area (int *, int *);
+static void s390_register_info (int, int);
 static void s390_frame_info (int, int);
 static rtx save_fpr (rtx, int, int);
 static rtx restore_fpr (rtx, int, int);
@@ -4168,7 +4211,7 @@ s390_split_branches (void)
 
       /* We are going to use the return register as scratch register,
         make sure it will be saved/restored by the prologue/epilogue.  */
-      cfun->machine->save_return_addr_p = 1;
+      cfun_frame_layout.save_return_addr_p = 1;
 
       if (!flag_pic)
        {
@@ -5333,33 +5376,33 @@ s390_output_pool_entry (rtx exp, enum machine_mode mode, unsigned int align)
 }
 
 
-/* Rework the prolog/epilog to avoid saving/restoring
+/* Rework the prologue/epilogue to avoid saving/restoring
    registers unnecessarily.  BASE_USED specifies whether
    the literal pool base register needs to be saved.  */
 
 static void
-s390_optimize_prolog (bool base_used)
+s390_optimize_prologue (bool base_used)
 {
   rtx insn, new_insn, next_insn;
 
   /* Do a final recompute of the frame-related data.  */
 
-  s390_frame_info (base_used, cfun->machine->save_return_addr_p);
+  s390_register_info (base_used, cfun_frame_layout.save_return_addr_p);
   regs_ever_live[BASE_REGNUM] = base_used;
-  regs_ever_live[RETURN_REGNUM] = cfun->machine->save_return_addr_p;
-  regs_ever_live[STACK_POINTER_REGNUM] = cfun->machine->frame_size > 0;
+  regs_ever_live[RETURN_REGNUM] = cfun_frame_layout.save_return_addr_p;
+  regs_ever_live[STACK_POINTER_REGNUM] = cfun_frame_layout.frame_size > 0;
 
   /* If all special registers are in fact used, there's nothing we
      can do, so no point in walking the insn list.  */
 
-  if (cfun->machine->first_save_gpr <= BASE_REGNUM 
-      && cfun->machine->last_save_gpr >= BASE_REGNUM
+  if (cfun_frame_layout.first_save_gpr <= BASE_REGNUM 
+      && cfun_frame_layout.last_save_gpr >= BASE_REGNUM
       && (TARGET_CPU_ZARCH 
-          || (cfun->machine->first_save_gpr <= RETURN_REGNUM 
-              && cfun->machine->last_save_gpr >= RETURN_REGNUM)))
+          || (cfun_frame_layout.first_save_gpr <= RETURN_REGNUM 
+              && cfun_frame_layout.last_save_gpr >= RETURN_REGNUM)))
     return;
 
-  /* Search for prolog/epilog insns and replace them.  */
+  /* Search for prologue/epilogue insns and replace them.  */
 
   for (insn = get_insns (); insn; insn = next_insn)
     {
@@ -5379,17 +5422,23 @@ s390_optimize_prolog (bool base_used)
          last = first + XVECLEN (PATTERN (insn), 0) - 1;
          offset = const0_rtx;
          base = eliminate_constant_term (XEXP (SET_DEST (set), 0), &offset);
-         off = INTVAL (offset) - first * UNITS_PER_WORD;
+         off = INTVAL (offset);
 
          if (GET_CODE (base) != REG || off < 0)
            continue;
+         if (REGNO (base) != STACK_POINTER_REGNUM
+             && REGNO (base) != HARD_FRAME_POINTER_REGNUM)
+           continue;
          if (first > BASE_REGNUM || last < BASE_REGNUM)
            continue;
 
-         if (cfun->machine->first_save_gpr != -1)
+         if (cfun_frame_layout.first_save_gpr != -1)
            {
-             new_insn = save_gprs (base, off, cfun->machine->first_save_gpr,
-                                   cfun->machine->last_save_gpr);
+             new_insn  = save_gprs (base, 
+                                    off + (cfun_frame_layout.first_save_gpr
+                                           - first) * UNITS_PER_WORD, 
+                                    cfun_frame_layout.first_save_gpr,
+                                    cfun_frame_layout.last_save_gpr);
              new_insn = emit_insn_before (new_insn, insn);
              INSN_ADDRESSES_NEW (new_insn, -1);
            }
@@ -5406,15 +5455,20 @@ s390_optimize_prolog (bool base_used)
          set = PATTERN (insn);
          offset = const0_rtx;
          base = eliminate_constant_term (XEXP (SET_DEST (set), 0), &offset);
-         off = INTVAL (offset) - BASE_REGNUM * UNITS_PER_WORD;
+         off = INTVAL (offset);
 
          if (GET_CODE (base) != REG || off < 0)
            continue;
-
-         if (cfun->machine->first_save_gpr != -1)
+         if (REGNO (base) != STACK_POINTER_REGNUM
+             && REGNO (base) != HARD_FRAME_POINTER_REGNUM)
+           continue;
+         if (cfun_frame_layout.first_save_gpr != -1)
            {
-             new_insn = save_gprs (base, off, cfun->machine->first_save_gpr,
-                                   cfun->machine->last_save_gpr);
+             new_insn = save_gprs (base, 
+                                   off + (cfun_frame_layout.first_save_gpr 
+                                          - BASE_REGNUM) * UNITS_PER_WORD, 
+                                   cfun_frame_layout.first_save_gpr,
+                                   cfun_frame_layout.last_save_gpr);
              new_insn = emit_insn_before (new_insn, insn);
              INSN_ADDRESSES_NEW (new_insn, -1);
            }
@@ -5431,17 +5485,23 @@ s390_optimize_prolog (bool base_used)
          last = first + XVECLEN (PATTERN (insn), 0) - 1;
          offset = const0_rtx;
          base = eliminate_constant_term (XEXP (SET_SRC (set), 0), &offset);
-         off = INTVAL (offset) - first * UNITS_PER_WORD;
+         off = INTVAL (offset);
 
          if (GET_CODE (base) != REG || off < 0)
            continue;
+         if (REGNO (base) != STACK_POINTER_REGNUM
+             && REGNO (base) != HARD_FRAME_POINTER_REGNUM)
+           continue;
          if (first > BASE_REGNUM || last < BASE_REGNUM)
            continue;
 
-         if (cfun->machine->first_restore_gpr != -1)
+         if (cfun_frame_layout.first_restore_gpr != -1)
            {
-             new_insn = restore_gprs (base, off, cfun->machine->first_restore_gpr,
-                                      cfun->machine->last_restore_gpr);
+             new_insn = restore_gprs (base, 
+                                      off + (cfun_frame_layout.first_restore_gpr
+                                             - first) * UNITS_PER_WORD, 
+                                      cfun_frame_layout.first_restore_gpr,
+                                      cfun_frame_layout.last_restore_gpr);
              new_insn = emit_insn_before (new_insn, insn);
              INSN_ADDRESSES_NEW (new_insn, -1);
            }
@@ -5458,15 +5518,20 @@ s390_optimize_prolog (bool base_used)
          set = PATTERN (insn);
          offset = const0_rtx;
          base = eliminate_constant_term (XEXP (SET_SRC (set), 0), &offset);
-         off = INTVAL (offset) - BASE_REGNUM * UNITS_PER_WORD;
+         off = INTVAL (offset);
 
          if (GET_CODE (base) != REG || off < 0)
            continue;
-
-         if (cfun->machine->first_restore_gpr != -1)
+         if (REGNO (base) != STACK_POINTER_REGNUM
+             && REGNO (base) != HARD_FRAME_POINTER_REGNUM)
+           continue;
+         if (cfun_frame_layout.first_restore_gpr != -1)
            {
-             new_insn = restore_gprs (base, off, cfun->machine->first_restore_gpr,
-                                      cfun->machine->last_restore_gpr);
+             new_insn = restore_gprs (base, 
+                                      off + (cfun_frame_layout.first_restore_gpr 
+                                             - BASE_REGNUM) * UNITS_PER_WORD,
+                                      cfun_frame_layout.first_restore_gpr,
+                                      cfun_frame_layout.last_restore_gpr);
              new_insn = emit_insn_before (new_insn, insn);
              INSN_ADDRESSES_NEW (new_insn, -1);
            }
@@ -5567,7 +5632,7 @@ s390_reorg (void)
       break;
     }
 
-  s390_optimize_prolog (base_used);
+  s390_optimize_prologue (base_used);
 }
 
 
@@ -5578,11 +5643,12 @@ s390_reorg (void)
 rtx
 s390_return_addr_rtx (int count, rtx frame ATTRIBUTE_UNUSED)
 {
+  int offset;
   rtx addr;
 
   /* Without backchain, we fail for all but the current frame.  */
 
-  if (!TARGET_BACKCHAIN && count > 0)
+  if (!TARGET_BACKCHAIN && !TARGET_KERNEL_BACKCHAIN && count > 0)
     return NULL_RTX;
 
   /* For the current frame, we need to make sure the initial
@@ -5590,11 +5656,16 @@ s390_return_addr_rtx (int count, rtx frame ATTRIBUTE_UNUSED)
 
   if (count == 0)
     {
-      cfun->machine->save_return_addr_p = true;
+      cfun_frame_layout.save_return_addr_p = true;
       return gen_rtx_MEM (Pmode, return_address_pointer_rtx);
     }
 
-  addr = plus_constant (frame, RETURN_REGNUM * UNITS_PER_WORD);
+  if (TARGET_BACKCHAIN)
+    offset = RETURN_REGNUM * UNITS_PER_WORD;
+  else
+    offset = -2 * UNITS_PER_WORD;
+
+  addr = plus_constant (frame, offset);
   addr = memory_address (Pmode, addr);
   return gen_rtx_MEM (Pmode, addr);
 }
@@ -5613,41 +5684,69 @@ find_unused_clobbered_reg (void)
   return 0;
 }
 
-/* Fill cfun->machine with info about frame of current function.  
-   BASE_USED and RETURN_ADDR_USED specify whether we assume the
+/* Determine the frame area which actually has to be accessed 
+   in the function epilogue. The values are stored at the 
+   given pointers AREA_BOTTOM (address of the lowest used stack
+   address) and AREA_TOP (address of the first item which does 
+   not belong to the stack frame).  */
+
+static void
+s390_frame_area (int *area_bottom, int *area_top)
+{
+  int b, t;
+  int i;
+
+  b = INT_MAX;
+  t = INT_MIN;
+
+  if (cfun_frame_layout.first_restore_gpr != -1)
+    {
+      b = (cfun_frame_layout.gprs_offset
+          + cfun_frame_layout.first_restore_gpr * UNITS_PER_WORD);
+      t = b + (cfun_frame_layout.last_restore_gpr
+              - cfun_frame_layout.first_restore_gpr + 1) * UNITS_PER_WORD;
+    }
+
+  if (TARGET_64BIT && cfun_save_high_fprs_p)
+    {
+      b = MIN (b, cfun_frame_layout.f8_offset);
+      t = MAX (t, (cfun_frame_layout.f8_offset
+                  + cfun_frame_layout.high_fprs * 8));
+    }
+
+  if (!TARGET_64BIT)
+    for (i = 2; i < 4; i++)
+      if (cfun_fpr_bit_p (i))
+       {
+         b = MIN (b, cfun_frame_layout.f4_offset + (i - 2) * 8);
+         t = MAX (t, cfun_frame_layout.f4_offset + (i - 1) * 8);
+       }
+  
+  *area_bottom = b;
+  *area_top = t;
+}
+
+/* Fill cfun->machine with info about register usage of current 
+   function. BASE_USED and RETURN_ADDR_USED specify whether we assume the
    base and return address register will need to be saved.  */
 
 static void
-s390_frame_info (int base_used, int return_addr_used)
+s390_register_info (int base_used, int return_addr_used)
 {
   int live_regs[16];
   int i, j;
-  HOST_WIDE_INT fsize = get_frame_size ();
-
-  if (!TARGET_64BIT && fsize > 0x7fff0000)
-    fatal_error ("Total size of local variables exceeds architecture limit.");
 
-  /* fprs 8 - 15 are caller saved for 64 Bit ABI.  */
-  cfun->machine->save_fprs_p = 0;
+  /* fprs 8 - 15 are call saved for 64 Bit ABI.  */
+  cfun_frame_layout.fpr_bitmap = 0;
+  cfun_frame_layout.high_fprs = 0;
   if (TARGET_64BIT)
     for (i = 24; i < 32; i++)
       if (regs_ever_live[i] && !global_regs[i])
        {
-          cfun->machine->save_fprs_p = 1;
-         break;
+         cfun_set_fpr_bit (i - 16);
+         cfun_frame_layout.high_fprs++;
        }
 
-  cfun->machine->frame_size = fsize + cfun->machine->save_fprs_p * 64;
-
-  /* Does function need to setup frame and save area.  */
-
-  if (!current_function_is_leaf
-      || TARGET_TPF_PROFILING
-      || cfun->machine->frame_size > 0
-      || current_function_calls_alloca
-      || current_function_stdarg)
-    cfun->machine->frame_size += STARTING_FRAME_OFFSET;
-
   /* Find first and last gpr to be saved.  We trust regs_ever_live
      data, except that we don't save and restore global registers.
 
@@ -5663,8 +5762,13 @@ s390_frame_info (int base_used, int return_addr_used)
 
   live_regs[BASE_REGNUM] = base_used;
   live_regs[RETURN_REGNUM] = return_addr_used;
-  live_regs[STACK_POINTER_REGNUM] = cfun->machine->frame_size > 0;
-
+  live_regs[STACK_POINTER_REGNUM] = (!current_function_is_leaf
+                                    || TARGET_TPF_PROFILING
+                                    || cfun_save_high_fprs_p
+                                    || get_frame_size () > 0
+                                    || current_function_calls_alloca
+                                    || current_function_stdarg);
+  
   for (i = 6; i < 16; i++)
     if (live_regs[i])
       break;
@@ -5675,30 +5779,147 @@ s390_frame_info (int base_used, int return_addr_used)
   if (i == 16)
     {
       /* Nothing to save/restore.  */
-      cfun->machine->first_save_gpr = -1;
-      cfun->machine->first_restore_gpr = -1;
-      cfun->machine->last_save_gpr = -1;
-      cfun->machine->last_restore_gpr = -1;
+      cfun_frame_layout.first_save_gpr = -1;
+      cfun_frame_layout.first_restore_gpr = -1;
+      cfun_frame_layout.last_save_gpr = -1;
+      cfun_frame_layout.last_restore_gpr = -1;
     }
   else
     {
       /* Save / Restore from gpr i to j.  */
-      cfun->machine->first_save_gpr = i;
-      cfun->machine->first_restore_gpr = i;
-      cfun->machine->last_save_gpr = j;
-      cfun->machine->last_restore_gpr = j;
+      cfun_frame_layout.first_save_gpr = i;
+      cfun_frame_layout.first_restore_gpr = i;
+      cfun_frame_layout.last_save_gpr = j;
+      cfun_frame_layout.last_restore_gpr = j;
     }
 
-  /* Varargs functions need to save gprs 2 to 6.  */
   if (current_function_stdarg)
     {
-      if (cfun->machine->first_save_gpr == -1
-          || cfun->machine->first_save_gpr > 2)
-        cfun->machine->first_save_gpr = 2;
+      /* Varargs functions need to save gprs 2 to 6.  */
+      if (cfun_frame_layout.first_save_gpr == -1
+          || cfun_frame_layout.first_save_gpr > 2)
+        cfun_frame_layout.first_save_gpr = 2;
+
+      if (cfun_frame_layout.last_save_gpr == -1
+          || cfun_frame_layout.last_save_gpr < 6)
+        cfun_frame_layout.last_save_gpr = 6;
+
+      /* Mark f0, f2 for 31 bit and f0-f4 for 64 bit to be saved.  */
+      for (i = 0; i < (TARGET_64BIT ? 4 : 2); i++)
+       cfun_set_fpr_bit (i);
+    }
+
+  if (!TARGET_64BIT)
+    for (i = 2; i < 4; i++)
+      if (regs_ever_live[i + 16] && !global_regs[i + 16])
+       cfun_set_fpr_bit (i);
+}
+
+/* Fill cfun->machine with info about frame of current 
+   function. BASE_USED and RETURN_ADDR_USED specify whether we assume the
+   base and return address register will need to be saved.  */
+
+static void
+s390_frame_info (int base_used, int return_addr_used)
+{
+  int i;
+
+  cfun_frame_layout.frame_size = get_frame_size ();
+
+  s390_register_info (base_used, return_addr_used);
+
+  if (!TARGET_64BIT && cfun_frame_layout.frame_size > 0x7fff0000)
+    fatal_error ("Total size of local variables exceeds architecture limit.");
+  
+  cfun_frame_layout.save_backchain_p = (TARGET_BACKCHAIN 
+                                       || TARGET_KERNEL_BACKCHAIN);
+
+  if (TARGET_BACKCHAIN)
+    {
+      cfun_frame_layout.backchain_offset = 0;
+      cfun_frame_layout.f0_offset = 16 * UNITS_PER_WORD;
+      cfun_frame_layout.f4_offset = cfun_frame_layout.f0_offset + 2 * 8;
+      cfun_frame_layout.f8_offset = -cfun_frame_layout.high_fprs * 8;
+      cfun_frame_layout.gprs_offset = (cfun_frame_layout.first_save_gpr
+                                      * UNITS_PER_WORD);
+    }
+  else if (TARGET_KERNEL_BACKCHAIN)
+    {
+      cfun_frame_layout.backchain_offset = (STACK_POINTER_OFFSET
+                                           - UNITS_PER_WORD);
+      cfun_frame_layout.gprs_offset 
+       = (cfun_frame_layout.backchain_offset 
+          - (STACK_POINTER_REGNUM - cfun_frame_layout.first_save_gpr + 1)
+          * UNITS_PER_WORD);
+         
+      if (TARGET_64BIT)
+       {
+         cfun_frame_layout.f4_offset 
+           = (cfun_frame_layout.gprs_offset
+              - 8 * (cfun_fpr_bit_p (2) + cfun_fpr_bit_p (3)));
+         
+         cfun_frame_layout.f0_offset 
+           = (cfun_frame_layout.f4_offset 
+              - 8 * (cfun_fpr_bit_p (0) + cfun_fpr_bit_p (1)));
+       }
+      else
+       {
+         cfun_frame_layout.f0_offset 
+           = (cfun_frame_layout.gprs_offset
+              - 8 * (cfun_fpr_bit_p (0) + cfun_fpr_bit_p (1)));
+         
+         cfun_frame_layout.f4_offset 
+           = (cfun_frame_layout.f0_offset
+              - 8 * (cfun_fpr_bit_p (2) + cfun_fpr_bit_p (3)));
+       }
+    }
+  else /* no backchain */
+    {
+      cfun_frame_layout.f4_offset 
+       = (STACK_POINTER_OFFSET
+          - 8 * (cfun_fpr_bit_p (2) + cfun_fpr_bit_p (3)));
+      
+      cfun_frame_layout.f0_offset 
+       = (cfun_frame_layout.f4_offset
+          - 8 * (cfun_fpr_bit_p (0) + cfun_fpr_bit_p (1)));
+      
+      cfun_frame_layout.gprs_offset 
+       = cfun_frame_layout.f0_offset - cfun_gprs_save_area_size;
+    }
+
+  if (current_function_is_leaf
+      && !TARGET_TPF_PROFILING
+      && cfun_frame_layout.frame_size == 0
+      && !cfun_save_high_fprs_p
+      && !current_function_calls_alloca
+      && !current_function_stdarg)
+    return;
+
+  if (TARGET_BACKCHAIN)
+    cfun_frame_layout.frame_size += (STARTING_FRAME_OFFSET
+                                    + cfun_frame_layout.high_fprs * 8);
+  else
+    {
+      cfun_frame_layout.frame_size += (cfun_frame_layout.save_backchain_p
+                                      * UNITS_PER_WORD);
+      
+      cfun_frame_layout.f8_offset = (MIN (MIN (cfun_frame_layout.f0_offset,
+                                              cfun_frame_layout.f4_offset),
+                                         cfun_frame_layout.gprs_offset)
+                                    - cfun_frame_layout.high_fprs * 8);
+
+      cfun_frame_layout.frame_size += cfun_frame_layout.high_fprs * 8;
+
+      for (i = 0; i < 8; i++)
+       if (cfun_fpr_bit_p (i))
+         cfun_frame_layout.frame_size += 8;
+      
+      cfun_frame_layout.frame_size += cfun_gprs_save_area_size;
+      cfun_frame_layout.frame_size = ((cfun_frame_layout.frame_size +
+                                      STACK_BOUNDARY / BITS_PER_UNIT - 1)
+                                     & ~(STACK_BOUNDARY / BITS_PER_UNIT - 1));
 
-      if (cfun->machine->last_save_gpr == -1
-          || cfun->machine->last_save_gpr < 6)
-        cfun->machine->last_save_gpr = 6;
+      cfun_frame_layout.frame_size += current_function_outgoing_args_size;
     }
 }
 
@@ -5713,10 +5934,11 @@ s390_arg_frame_offset (void)
   int return_addr_used = !current_function_is_leaf
                         || TARGET_TPF_PROFILING
                         || regs_ever_live[RETURN_REGNUM]
-                        || cfun->machine->save_return_addr_p;
+                        || cfun_frame_layout.save_return_addr_p;
 
   s390_frame_info (1, !TARGET_CPU_ZARCH || return_addr_used);
-  return cfun->machine->frame_size + STACK_POINTER_OFFSET;
+
+  return cfun_frame_layout.frame_size + STACK_POINTER_OFFSET;
 }
 
 /* Return offset between return address pointer (location of r14
@@ -5727,7 +5949,11 @@ s390_return_address_offset (void)
 {
   s390_frame_info (1, 1);
 
-  return cfun->machine->frame_size + RETURN_REGNUM * UNITS_PER_WORD;
+  if (cfun_frame_layout.last_save_gpr < RETURN_REGNUM)
+    abort ();
+
+  return (cfun_frame_layout.frame_size + cfun_frame_layout.gprs_offset
+         + (RETURN_REGNUM - cfun_frame_layout.first_save_gpr) * UNITS_PER_WORD);
 }
 
 /* Emit insn to save fpr REGNUM at offset OFFSET relative
@@ -5766,7 +5992,7 @@ save_gprs (rtx base, int offset, int first, int last)
   rtx addr, insn, note;
   int i;
 
-  addr = plus_constant (base, offset + first * UNITS_PER_WORD);
+  addr = plus_constant (base, offset);
   addr = gen_rtx_MEM (Pmode, addr);
   set_mem_alias_set (addr, s390_sr_alias_set);
 
@@ -5812,7 +6038,7 @@ save_gprs (rtx base, int offset, int first, int last)
     }
   else if (last >= 6)
     {
-      addr = plus_constant (base, offset + 6 * UNITS_PER_WORD);
+      addr = plus_constant (base, offset + (6 - first) * UNITS_PER_WORD);
       note = gen_store_multiple (gen_rtx_MEM (Pmode, addr),
                                 gen_rtx_REG (Pmode, 6),
                                 GEN_INT (last - 6 + 1));
@@ -5841,7 +6067,7 @@ restore_gprs (rtx base, int offset, int first, int last)
 {
   rtx addr, insn;
 
-  addr = plus_constant (base, offset + first * UNITS_PER_WORD);
+  addr = plus_constant (base, offset);
   addr = gen_rtx_MEM (Pmode, addr);
   set_mem_alias_set (addr, s390_sr_alias_set);
 
@@ -5913,6 +6139,8 @@ s390_emit_prologue (void)
   rtx insn, addr;
   rtx temp_reg;
   int i;
+  int offset;
+  int next_fpr = 0;
 
   /* At this point, we decide whether we'll need to save/restore the
      return address register.  This decision is final on zSeries machines;
@@ -5921,7 +6149,7 @@ s390_emit_prologue (void)
   if (!current_function_is_leaf
       || TARGET_TPF_PROFILING
       || regs_ever_live[RETURN_REGNUM])
-    cfun->machine->save_return_addr_p = 1;
+    cfun_frame_layout.save_return_addr_p = 1;
 
   /* Decide which register to use as literal pool base.  In small leaf 
      functions, try to use an unused call-clobbered register as base 
@@ -5937,17 +6165,18 @@ s390_emit_prologue (void)
   /* Compute frame info.  Note that at this point, we assume the base 
      register and -on S/390- the return register always need to be saved.
      This is done because the usage of these registers might change even 
-     after the prolog was emitted.  If it turns out later that we really 
-     don't need them, the prolog/epilog code is modified again.  */
+     after the prologue was emitted.  If it turns out later that we really 
+     don't need them, the prologue/epilogue code is modified again.  */
 
-  s390_frame_info (1, !TARGET_CPU_ZARCH || cfun->machine->save_return_addr_p);
+  s390_frame_info (1, !TARGET_CPU_ZARCH
+                  || cfun_frame_layout.save_return_addr_p);
 
   /* We need to update regs_ever_live to avoid data-flow problems.  */
 
   regs_ever_live[BASE_REGNUM] = 1;
-  regs_ever_live[RETURN_REGNUM] = !TARGET_CPU_ZARCH 
-                                 || cfun->machine->save_return_addr_p;
-  regs_ever_live[STACK_POINTER_REGNUM] = cfun->machine->frame_size > 0;
+  regs_ever_live[RETURN_REGNUM] = (!TARGET_CPU_ZARCH 
+                                  || cfun_frame_layout.save_return_addr_p);
+  regs_ever_live[STACK_POINTER_REGNUM] = cfun_frame_layout.frame_size > 0;
 
   /* Annotate all constant pool references to let the scheduler know
      they implicitly use the base register.  */
@@ -5963,57 +6192,93 @@ s390_emit_prologue (void)
   /* Choose best register to use for temp use within prologue.
      See below for why TPF must use the register 1.  */
 
-  if (!current_function_is_leaf
-      && !TARGET_TPF_PROFILING)
+  if (!current_function_is_leaf && !TARGET_TPF_PROFILING)
     temp_reg = gen_rtx_REG (Pmode, RETURN_REGNUM);
   else
     temp_reg = gen_rtx_REG (Pmode, 1);
 
   /* Save call saved gprs.  */
-
-  insn = save_gprs (stack_pointer_rtx, 0,
-                   cfun->machine->first_save_gpr, cfun->machine->last_save_gpr);
+  if (cfun_frame_layout.first_save_gpr != -1)
+    insn = save_gprs (stack_pointer_rtx, 
+                     cfun_frame_layout.gprs_offset,
+                     cfun_frame_layout.first_save_gpr, 
+                     cfun_frame_layout.last_save_gpr);
   emit_insn (insn);
 
   /* Dummy insn to mark literal pool slot.  */
 
   emit_insn (gen_main_pool (cfun->machine->base_reg));
 
-  /* Save fprs for variable args.  */
+  offset = cfun_frame_layout.f0_offset;
 
-  if (current_function_stdarg)
-    for (i = 16; i < (TARGET_64BIT ? 20 : 18); i++)
-      save_fpr (stack_pointer_rtx, 16*UNITS_PER_WORD + 8*(i-16), i);
-
-  /* Save fprs 4 and 6 if used (31 bit ABI).  */
+  /* Save f0 and f2.  */
+  for (i = 0; i < 2; i++)
+    {
+      if (cfun_fpr_bit_p (i))
+       {
+         save_fpr (stack_pointer_rtx, offset, i + 16);
+         offset += 8;
+       }
+      else if (TARGET_BACKCHAIN)
+         offset += 8;
+    }
 
-  if (!TARGET_64BIT)
-    for (i = 18; i < 20; i++)
-      if (regs_ever_live[i] && !global_regs[i])
+  /* Save f4 and f6.  */
+  offset = cfun_frame_layout.f4_offset;
+  for (i = 2; i < 4; i++)
+    {
+      if (cfun_fpr_bit_p (i))
        {
-         insn = save_fpr (stack_pointer_rtx, 16*UNITS_PER_WORD + 8*(i-16), i);
-         RTX_FRAME_RELATED_P (insn) = 1;
+         insn = save_fpr (stack_pointer_rtx, offset, i + 16);
+         offset += 8;
+
+         /* If f4 and f6 are call clobbered they are saved due to stdargs and
+            therefore are not frame related.  */
+         if (!call_really_used_regs[i + 16])
+           RTX_FRAME_RELATED_P (insn) = 1;
        }
+      else if (TARGET_BACKCHAIN)
+       offset += 8;
+    }
+
+  if (!TARGET_BACKCHAIN 
+      && cfun_save_high_fprs_p
+      && cfun_frame_layout.f8_offset + cfun_frame_layout.high_fprs * 8 > 0)
+    {
+      offset = (cfun_frame_layout.f8_offset
+               + (cfun_frame_layout.high_fprs - 1) * 8);
+
+      for (i = 15; i > 7 && offset >= 0; i--)
+       if (cfun_fpr_bit_p (i))
+         {
+           insn = save_fpr (stack_pointer_rtx, offset, i + 16);
+                      
+           RTX_FRAME_RELATED_P (insn) = 1;
+           offset -= 8;
+         }
+      if (offset >= cfun_frame_layout.f8_offset)
+       next_fpr = i + 16;
+    }
+  
+  if (TARGET_BACKCHAIN)
+    next_fpr = cfun_save_high_fprs_p ? 31 : 0;
 
   /* Decrement stack pointer.  */
 
-  if (cfun->machine->frame_size > 0)
+  if (cfun_frame_layout.frame_size > 0)
     {
-      rtx frame_off = GEN_INT (-cfun->machine->frame_size);
+      rtx frame_off = GEN_INT (-cfun_frame_layout.frame_size);
 
       /* Save incoming stack pointer into temp reg.  */
-
-      if (TARGET_BACKCHAIN || cfun->machine->save_fprs_p)
-       {
-         insn = emit_insn (gen_move_insn (temp_reg, stack_pointer_rtx));
-       }
+      if (cfun_frame_layout.save_backchain_p || next_fpr)
+       insn = emit_insn (gen_move_insn (temp_reg, stack_pointer_rtx));
 
       /* Subtract frame size from stack pointer.  */
 
       if (DISP_IN_RANGE (INTVAL (frame_off)))
        {
          insn = gen_rtx_SET (VOIDmode, stack_pointer_rtx,
-                             gen_rtx_PLUS (Pmode, stack_pointer_rtx,
+                             gen_rtx_PLUS (Pmode, stack_pointer_rtx, 
                                            frame_off));
          insn = emit_insn (insn);
        }
@@ -6030,15 +6295,20 @@ s390_emit_prologue (void)
       REG_NOTES (insn) =
        gen_rtx_EXPR_LIST (REG_FRAME_RELATED_EXPR,
                           gen_rtx_SET (VOIDmode, stack_pointer_rtx,
-                                  gen_rtx_PLUS (Pmode, stack_pointer_rtx,
-                                  GEN_INT (-cfun->machine->frame_size))),
+                            gen_rtx_PLUS (Pmode, stack_pointer_rtx,
+                              GEN_INT (-cfun_frame_layout.frame_size))),
                           REG_NOTES (insn));
 
       /* Set backchain.  */
 
-      if (TARGET_BACKCHAIN)
+      if (cfun_frame_layout.save_backchain_p)
        {
-         addr = gen_rtx_MEM (Pmode, stack_pointer_rtx);
+         if (cfun_frame_layout.backchain_offset)
+           addr = gen_rtx_MEM (Pmode, 
+                               plus_constant (stack_pointer_rtx, 
+                                 cfun_frame_layout.backchain_offset));
+         else
+           addr = gen_rtx_MEM (Pmode, stack_pointer_rtx);  
          set_mem_alias_set (addr, s390_sr_alias_set);
          insn = emit_insn (gen_move_insn (addr, temp_reg));
        }
@@ -6047,7 +6317,7 @@ s390_emit_prologue (void)
         we need to make sure the backchain pointer is set up
         before any possibly trapping memory access.  */
 
-      if (TARGET_BACKCHAIN && flag_non_call_exceptions)
+      if (cfun_frame_layout.save_backchain_p && flag_non_call_exceptions)
        {
          addr = gen_rtx_MEM (BLKmode, gen_rtx_SCRATCH (VOIDmode));
          emit_insn (gen_rtx_CLOBBER (VOIDmode, addr));
@@ -6056,24 +6326,30 @@ s390_emit_prologue (void)
 
   /* Save fprs 8 - 15 (64 bit ABI).  */
 
-  if (cfun->machine->save_fprs_p)
+  if (cfun_save_high_fprs_p && next_fpr)
     {
-      insn = emit_insn (gen_add2_insn (temp_reg, GEN_INT(-64)));
+      insn = emit_insn (gen_add2_insn (temp_reg, 
+                                      GEN_INT (cfun_frame_layout.f8_offset)));
 
-      for (i = 24; i < 32; i++)
-       if (regs_ever_live[i] && !global_regs[i])
+      offset = 0;
+
+      for (i = 24; i <= next_fpr; i++)
+       if (cfun_fpr_bit_p (i - 16))
          {
            rtx addr = plus_constant (stack_pointer_rtx,
-                                     cfun->machine->frame_size - 64 + (i-24)*8);
-
-           insn = save_fpr (temp_reg, (i-24)*8, i);
+                                     cfun_frame_layout.frame_size
+                                     + cfun_frame_layout.f8_offset
+                                     + offset);
+           
+           insn = save_fpr (temp_reg, offset, i);
+           offset += 8;
            RTX_FRAME_RELATED_P (insn) = 1;
            REG_NOTES (insn) =
              gen_rtx_EXPR_LIST (REG_FRAME_RELATED_EXPR,
-               gen_rtx_SET (VOIDmode,
-                            gen_rtx_MEM (DFmode, addr),
-                            gen_rtx_REG (DFmode, i)),
-               REG_NOTES (insn));
+                                gen_rtx_SET (VOIDmode,
+                                             gen_rtx_MEM (DFmode, addr),
+                                             gen_rtx_REG (DFmode, i)),
+                                REG_NOTES (insn));
          }
     }
 
@@ -6122,6 +6398,7 @@ s390_emit_epilogue (bool sibcall)
 {
   rtx frame_pointer, return_reg;
   int area_bottom, area_top, offset = 0;
+  int next_offset;
   rtvec p;
   int i;
 
@@ -6141,43 +6418,10 @@ s390_emit_epilogue (bool sibcall)
 
   /* Check whether to use frame or stack pointer for restore.  */
 
-  frame_pointer = frame_pointer_needed ?
-    hard_frame_pointer_rtx : stack_pointer_rtx;
+  frame_pointer = (frame_pointer_needed 
+                  ? hard_frame_pointer_rtx : stack_pointer_rtx);
 
-  /* Compute which parts of the save area we need to access.  */
-
-  if (cfun->machine->first_restore_gpr != -1)
-    {
-      area_bottom = cfun->machine->first_restore_gpr * UNITS_PER_WORD;
-      area_top = (cfun->machine->last_restore_gpr + 1) * UNITS_PER_WORD;
-    }
-  else
-    {
-      area_bottom = INT_MAX;
-      area_top = INT_MIN;
-    }
-
-  if (TARGET_64BIT)
-    {
-      if (cfun->machine->save_fprs_p)
-       {
-         if (area_bottom > -64)
-           area_bottom = -64;
-         if (area_top < 0)
-           area_top = 0;
-       }
-    }
-  else
-    {
-      for (i = 18; i < 20; i++)
-       if (regs_ever_live[i] && !global_regs[i])
-         {
-           if (area_bottom > 16*UNITS_PER_WORD + 8*(i-16))
-             area_bottom = 16*UNITS_PER_WORD + 8*(i-16);
-           if (area_top < 16*UNITS_PER_WORD + 8*(i-16) + 8)
-             area_top = 16*UNITS_PER_WORD + 8*(i-16) + 8;
-         }
-    }
+  s390_frame_area (&area_bottom, &area_top);
 
   /* Check whether we can access the register save area.
      If not, increment the frame pointer as required.  */
@@ -6186,18 +6430,18 @@ s390_emit_epilogue (bool sibcall)
     {
       /* Nothing to restore.  */
     }
-  else if (DISP_IN_RANGE (cfun->machine->frame_size + area_bottom)
-           && DISP_IN_RANGE (cfun->machine->frame_size + area_top-1))
+  else if (DISP_IN_RANGE (cfun_frame_layout.frame_size + area_bottom)
+           && DISP_IN_RANGE (cfun_frame_layout.frame_size + area_top - 1))
     {
       /* Area is in range.  */
-      offset = cfun->machine->frame_size;
+      offset = cfun_frame_layout.frame_size;
     }
   else
     {
       rtx insn, frame_off;
 
       offset = area_bottom < 0 ? -area_bottom : 0;
-      frame_off = GEN_INT (cfun->machine->frame_size - offset);
+      frame_off = GEN_INT (cfun_frame_layout.frame_size - offset);
 
       if (DISP_IN_RANGE (INTVAL (frame_off)))
        {
@@ -6219,18 +6463,36 @@ s390_emit_epilogue (bool sibcall)
 
   if (TARGET_64BIT)
     {
-      if (cfun->machine->save_fprs_p)
-       for (i = 24; i < 32; i++)
-         if (regs_ever_live[i] && !global_regs[i])
-           restore_fpr (frame_pointer,
-                        offset - 64 + (i-24) * 8, i);
+      if (cfun_save_high_fprs_p)
+       {
+         next_offset = cfun_frame_layout.f8_offset;
+         for (i = 24; i < 32; i++)
+           {
+             if (cfun_fpr_bit_p (i - 16))
+               {
+                 restore_fpr (frame_pointer,
+                              offset + next_offset, i);
+                 next_offset += 8;
+               }
+           }
+       }
+             
     }
   else
     {
+      next_offset = cfun_frame_layout.f4_offset;
       for (i = 18; i < 20; i++)
-       if (regs_ever_live[i] && !global_regs[i])
-         restore_fpr (frame_pointer,
-                      offset + 16*UNITS_PER_WORD + 8*(i-16), i);
+       {
+         if (cfun_fpr_bit_p (i - 16))
+           {
+             restore_fpr (frame_pointer,
+                          offset + next_offset, i);
+             next_offset += 8;
+           }
+         else if (TARGET_BACKCHAIN)
+           next_offset += 8;
+       }
+      
     }
 
   /* Return register.  */
@@ -6239,7 +6501,7 @@ s390_emit_epilogue (bool sibcall)
 
   /* Restore call saved gprs.  */
 
-  if (cfun->machine->first_restore_gpr != -1)
+  if (cfun_frame_layout.first_restore_gpr != -1)
     {
       rtx insn, addr;
       int i;
@@ -6247,8 +6509,8 @@ s390_emit_epilogue (bool sibcall)
       /* Check for global register and save them
         to stack location from where they get restored.  */
 
-      for (i = cfun->machine->first_restore_gpr;
-          i <= cfun->machine->last_restore_gpr;
+      for (i = cfun_frame_layout.first_restore_gpr;
+          i <= cfun_frame_layout.last_restore_gpr;
           i++)
        {
          /* These registers are special and need to be
@@ -6262,7 +6524,9 @@ s390_emit_epilogue (bool sibcall)
          if (global_regs[i])
            {
              addr = plus_constant (frame_pointer,
-                    offset + i * UNITS_PER_WORD);
+                                   offset + cfun_frame_layout.gprs_offset 
+                                   + (i - cfun_frame_layout.first_save_gpr)
+                                   * UNITS_PER_WORD);
              addr = gen_rtx_MEM (Pmode, addr);
              set_mem_alias_set (addr, s390_sr_alias_set);
              emit_move_insn (addr, gen_rtx_REG (Pmode, i));
@@ -6274,9 +6538,9 @@ s390_emit_epilogue (bool sibcall)
          /* Fetch return address from stack before load multiple,
             this will do good for scheduling.  */
 
-         if (cfun->machine->save_return_addr_p
-             || (cfun->machine->first_restore_gpr < BASE_REGNUM
-                 && cfun->machine->last_restore_gpr > RETURN_REGNUM))
+         if (cfun_frame_layout.save_return_addr_p
+             || (cfun_frame_layout.first_restore_gpr < BASE_REGNUM
+                 && cfun_frame_layout.last_restore_gpr > RETURN_REGNUM))
            {
              int return_regnum = find_unused_clobbered_reg();
              if (!return_regnum)
@@ -6284,16 +6548,23 @@ s390_emit_epilogue (bool sibcall)
              return_reg = gen_rtx_REG (Pmode, return_regnum);
 
              addr = plus_constant (frame_pointer,
-                                   offset + RETURN_REGNUM * UNITS_PER_WORD);
+                                   offset + cfun_frame_layout.gprs_offset
+                                   + (RETURN_REGNUM 
+                                      - cfun_frame_layout.first_save_gpr)
+                                   * UNITS_PER_WORD);
              addr = gen_rtx_MEM (Pmode, addr);
              set_mem_alias_set (addr, s390_sr_alias_set);
              emit_move_insn (return_reg, addr);
            }
        }
 
-      insn = restore_gprs (frame_pointer, offset,
-                          cfun->machine->first_restore_gpr,
-                          cfun->machine->last_restore_gpr);
+      insn = restore_gprs (frame_pointer,
+                          offset + cfun_frame_layout.gprs_offset
+                          + (cfun_frame_layout.first_restore_gpr 
+                             - cfun_frame_layout.first_save_gpr)
+                          * UNITS_PER_WORD,
+                          cfun_frame_layout.first_restore_gpr,
+                          cfun_frame_layout.last_restore_gpr);
       emit_insn (insn);
     }
 
@@ -6681,9 +6952,15 @@ s390_va_start (tree valist, rtx nextarg ATTRIBUTE_UNUSED)
   expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
 
   /* Find the register save area.  */
-  t = make_tree (TREE_TYPE (sav), virtual_incoming_args_rtx);
-  t = build (PLUS_EXPR, TREE_TYPE (sav), t,
-            build_int_2 (-STACK_POINTER_OFFSET, -1));
+  t = make_tree (TREE_TYPE (sav), return_address_pointer_rtx);
+  if (TARGET_KERNEL_BACKCHAIN)
+    t = build (PLUS_EXPR, TREE_TYPE (sav), t,
+              build_int_2 (-(RETURN_REGNUM - 2) * UNITS_PER_WORD
+                           - (TARGET_64BIT ? 4 : 2) * 8, -1));
+  else
+    t = build (PLUS_EXPR, TREE_TYPE (sav), t,
+              build_int_2 (-RETURN_REGNUM * UNITS_PER_WORD, -1));
+
   t = build (MODIFY_EXPR, TREE_TYPE (sav), sav, t);
   TREE_SIDE_EFFECTS (t) = 1;
   expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
@@ -6747,7 +7024,8 @@ s390_gimplify_va_arg (tree valist, tree type, tree *pre_p,
       indirect_p = 1;
       reg = gpr;
       n_reg = 1;
-      sav_ofs = 2 * UNITS_PER_WORD;
+      sav_ofs = (TARGET_KERNEL_BACKCHAIN
+                ? (TARGET_64BIT ? 4 : 2) * 8 : 2 * UNITS_PER_WORD);
       sav_scale = UNITS_PER_WORD;
       size = UNITS_PER_WORD;
       max_reg = 4;
@@ -6764,7 +7042,7 @@ s390_gimplify_va_arg (tree valist, tree type, tree *pre_p,
       indirect_p = 0;
       reg = fpr;
       n_reg = 1;
-      sav_ofs = 16 * UNITS_PER_WORD;
+      sav_ofs = TARGET_KERNEL_BACKCHAIN ? 0 : 16 * UNITS_PER_WORD;
       sav_scale = 8;
       /* TARGET_64BIT has up to 4 parameter in fprs */
       max_reg = TARGET_64BIT ? 3 : 1;
@@ -6781,7 +7059,8 @@ s390_gimplify_va_arg (tree valist, tree type, tree *pre_p,
       indirect_p = 0;
       reg = gpr;
       n_reg = (size + UNITS_PER_WORD - 1) / UNITS_PER_WORD;
-      sav_ofs = 2 * UNITS_PER_WORD;
+      sav_ofs = TARGET_KERNEL_BACKCHAIN ? 
+       (TARGET_64BIT ? 4 : 2) * 8 : 2*UNITS_PER_WORD;
 
       if (size < UNITS_PER_WORD)
        sav_ofs += UNITS_PER_WORD - size;
index c92d6ad272d20e29409113c7229f512110209ee8..eae2d65c95391a4798b434cfaf95a8569dff0dd7 100644 (file)
@@ -60,6 +60,8 @@ extern enum processor_type s390_arch;
 extern enum processor_flags s390_arch_flags;
 extern const char *s390_arch_string;
 
+extern const char *s390_backchain_string;
+
 #define TARGET_CPU_IEEE_FLOAT \
        (s390_arch_flags & PF_IEEE_FLOAT)
 #define TARGET_CPU_ZARCH \
@@ -89,7 +91,6 @@ extern const char *s390_arch_string;
 extern int target_flags;
 
 #define MASK_HARD_FLOAT            0x01
-#define MASK_BACKCHAIN             0x02
 #define MASK_SMALL_EXEC            0x04
 #define MASK_DEBUG_ARG             0x08
 #define MASK_64BIT                 0x10
@@ -100,7 +101,6 @@ extern int target_flags;
 
 #define TARGET_HARD_FLOAT          (target_flags & MASK_HARD_FLOAT)
 #define TARGET_SOFT_FLOAT          (!(target_flags & MASK_HARD_FLOAT))
-#define TARGET_BACKCHAIN           (target_flags & MASK_BACKCHAIN)
 #define TARGET_SMALL_EXEC          (target_flags & MASK_SMALL_EXEC)
 #define TARGET_DEBUG_ARG           (target_flags & MASK_DEBUG_ARG)
 #define TARGET_64BIT               (target_flags & MASK_64BIT)
@@ -110,6 +110,9 @@ extern int target_flags;
 #define TARGET_NO_FUSED_MADD       (target_flags & MASK_NO_FUSED_MADD)
 #define TARGET_FUSED_MADD         (! TARGET_NO_FUSED_MADD)
 
+#define TARGET_BACKCHAIN           (s390_backchain_string[0] == '1')
+#define TARGET_KERNEL_BACKCHAIN    (s390_backchain_string[0] == '2')
+
 /* ??? Once this actually works, it could be made a runtime option.  */
 #define TARGET_IBM_FLOAT           0
 #define TARGET_IEEE_FLOAT          1
@@ -123,8 +126,6 @@ extern int target_flags;
 #define TARGET_SWITCHES                                                  \
 { { "hard-float",      1, N_("Use hardware fp")},                        \
   { "soft-float",     -1, N_("Don't use hardware fp")},                  \
-  { "backchain",       2, N_("Set backchain")},                          \
-  { "no-backchain",   -2, N_("Don't set backchain (faster, but debug harder")},\
   { "small-exec",      4, N_("Use bras for executable < 64k")},          \
   { "no-small-exec",  -4, N_("Don't use bras")},                         \
   { "debug",           8, N_("Additional debug prints")},                \
@@ -146,6 +147,12 @@ extern int target_flags;
     N_("Schedule code for given CPU"), 0},                      \
   { "arch=",            &s390_arch_string,                      \
     N_("Generate code for given CPU"), 0},                      \
+  { "backchain",        &s390_backchain_string,                 \
+    N_("Set backchain"), "1"},                                  \
+  { "no-backchain",     &s390_backchain_string,                 \
+    N_("Do not set backchain"), ""},                            \
+  { "kernel-backchain", &s390_backchain_string,                 \
+    N_("Set backchain appropriate for the linux kernel"), "2"}, \
 }
 
 /* Support for configure-time defaults.  */
@@ -559,9 +566,13 @@ extern int current_function_outgoing_args_size;
    For frames farther back, we use the stack slot where
    the corresponding RETURN_REGNUM register was saved.  */
 
-#define DYNAMIC_CHAIN_ADDRESS(FRAME)                                           \
-  ((FRAME) != hard_frame_pointer_rtx ? (FRAME) :                               \
-   plus_constant (arg_pointer_rtx, -STACK_POINTER_OFFSET))
+#define DYNAMIC_CHAIN_ADDRESS(FRAME)                                            \
+  (TARGET_BACKCHAIN ?                                                           \
+   ((FRAME) != hard_frame_pointer_rtx ? (FRAME) :                              \
+    plus_constant (arg_pointer_rtx, -STACK_POINTER_OFFSET)) :                   \
+    ((FRAME) != hard_frame_pointer_rtx ?                                        \
+     plus_constant ((FRAME), STACK_POINTER_OFFSET - UNITS_PER_WORD) :           \
+     plus_constant (arg_pointer_rtx, -UNITS_PER_WORD)))
 
 #define RETURN_ADDR_RTX(COUNT, FRAME)                                          \
   s390_return_addr_rtx ((COUNT), DYNAMIC_CHAIN_ADDRESS ((FRAME)))
index f18fe13d64713693973c5f5764b3b04a96fdf6bc..53d122022eb8494ce5b994114c897b3f1d7d2d4d 100644 (file)
         (plus (reg 15) (match_operand 1 "general_operand" "")))
    (set (match_operand 0 "general_operand" "")
         (reg 15))]
- "TARGET_BACKCHAIN"
+ "TARGET_BACKCHAIN || TARGET_KERNEL_BACKCHAIN"
 {
     rtx stack = gen_rtx_REG (Pmode, STACK_POINTER_REGNUM);
-    rtx chain = gen_rtx_MEM (Pmode, stack);
-    rtx temp = gen_reg_rtx (Pmode);
+    rtx chain;
+    rtx temp;
+
+    if (TARGET_KERNEL_BACKCHAIN)
+      chain = plus_constant (stack, STACK_POINTER_OFFSET - UNITS_PER_WORD);
+    else
+      chain = stack;
+
+    chain = gen_rtx_MEM (Pmode, chain);
+    temp = gen_reg_rtx (Pmode);
 
     emit_move_insn (temp, chain);
 
index bc76689bc5661ce0ac3967593d0c211c6a5c160e..0cc00780498e83e811fbd3f956293ffb2dfe5620 100644 (file)
@@ -617,7 +617,7 @@ See RS/6000 and PowerPC Options.
 
 @emph{S/390 and zSeries Options}
 @gccoptlist{-mtune=@var{cpu-type}  -march=@var{cpu-type} @gol
--mhard-float  -msoft-float  -mbackchain  -mno-backchain @gol
+-mhard-float  -msoft-float  -mbackchain  -mno-backchain -mkernel-backchain @gol
 -msmall-exec  -mno-small-exec  -mmvcle -mno-mvcle @gol
 -m64  -m31  -mdebug  -mno-debug  -mesa  -mzarch @gol
 -mtpf-trace -mno-tpf-trace  -mfused-madd  -mno-fused-madd}
@@ -10454,13 +10454,27 @@ generates IEEE floating-point instructions.  This is the default.
 
 @item -mbackchain
 @itemx -mno-backchain
+@itemx -mkernel-backchain
 @opindex mbackchain
 @opindex mno-backchain
-Generate (or do not generate) code which maintains an explicit
-backchain within the stack frame that points to the caller's frame.
-This may be needed to allow debugging using tools that do not understand
-DWARF-2 call frame information.  The default is not to generate the
-backchain.
+@opindex mkernel-backchain
+In order to provide a backchain the address of the caller's frame
+is stored within the callee's stack frame.
+A backchain may be needed to allow debugging using tools that do not understand
+DWARF-2 call frame information.
+For @option{-mno-backchain} no backchain is maintained at all which is the 
+default. 
+If one of the other options is present the backchain pointer is placed either 
+on top of the stack frame  (@option{-mkernel-backchain}) or on
+the bottom (@option{-mbackchain}).
+Beside the different backchain location @option{-mkernel-backchain}
+also changes stack frame layout breaking the ABI. This option
+is intended to be used for code which internally needs a backchain but has
+to get by with a limited stack size e.g. the linux kernel. 
+Internal unwinding code not using DWARF-2 info has to be able to locate the
+return address of a function. That will be eased be the fact that
+the return address of a function is placed two words below the backchain 
+pointer.
 
 @item -msmall-exec
 @itemx -mno-small-exec