From adf39f8f5f2e1c877fe6b02654e673875c34ddac Mon Sep 17 00:00:00 2001 From: Andreas Krebbel Date: Thu, 12 Aug 2004 17:40:02 +0000 Subject: [PATCH] s390.c (struct s390_frame_layout): New struct as element of struct machine_function. 2004-08-12 Andreas Krebbel * 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 | 38 +++ gcc/config/s390/s390.c | 681 ++++++++++++++++++++++++++++------------ gcc/config/s390/s390.h | 25 +- gcc/config/s390/s390.md | 14 +- gcc/doc/invoke.texi | 26 +- 5 files changed, 567 insertions(+), 217 deletions(-) diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 3e651a98b54..6881fa6a5ec 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,41 @@ +2004-08-12 Andreas Krebbel + + * 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 * 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 083d7259fdb..be3aeae59b4 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= */ const char *s390_arch_string; /* for -march= */ -/* 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 c92d6ad272d..eae2d65c953 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 f18fe13d647..53d122022eb 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 bc76689bc56..0cc00780498 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 -- 2.30.2