aboutsummaryrefslogtreecommitdiff
path: root/gcc
diff options
context:
space:
mode:
authorAndreas Krebbel <krebbel1@de.ibm.com>2004-08-12 17:40:02 +0000
committerUlrich Weigand <uweigand@gcc.gnu.org>2004-08-12 17:40:02 +0000
commitadf39f8f5f2e1c877fe6b02654e673875c34ddac (patch)
treee8ce9fc539d0dad49e39d09c25cb74577820e859 /gcc
parentfaa03cf1b40e800415540d92b100ad1e545c0d56 (diff)
downloadgcc-adf39f8f5f2e1c877fe6b02654e673875c34ddac.zip
gcc-adf39f8f5f2e1c877fe6b02654e673875c34ddac.tar.gz
gcc-adf39f8f5f2e1c877fe6b02654e673875c34ddac.tar.bz2
s390.c (struct s390_frame_layout): New struct as element of struct machine_function.
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
Diffstat (limited to 'gcc')
-rw-r--r--gcc/ChangeLog38
-rw-r--r--gcc/config/s390/s390.c681
-rw-r--r--gcc/config/s390/s390.h25
-rw-r--r--gcc/config/s390/s390.md14
-rw-r--r--gcc/doc/invoke.texi26
5 files changed, 567 insertions, 217 deletions
diff --git a/gcc/ChangeLog b/gcc/ChangeLog
index 3e651a9..6881fa6 100644
--- a/gcc/ChangeLog
+++ b/gcc/ChangeLog
@@ -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.
diff --git a/gcc/config/s390/s390.c b/gcc/config/s390/s390.c
index 083d725..be3aeae 100644
--- a/gcc/config/s390/s390.c
+++ b/gcc/config/s390/s390.c
@@ -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;
diff --git a/gcc/config/s390/s390.h b/gcc/config/s390/s390.h
index c92d6ad..eae2d65 100644
--- a/gcc/config/s390/s390.h
+++ b/gcc/config/s390/s390.h
@@ -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)))
diff --git a/gcc/config/s390/s390.md b/gcc/config/s390/s390.md
index f18fe13..53d1220 100644
--- a/gcc/config/s390/s390.md
+++ b/gcc/config/s390/s390.md
@@ -7217,11 +7217,19 @@
(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);
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index bc76689..0cc0078 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -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