diff --git a/gcc.spec b/gcc.spec index 71bdf33..4d3bf52 100644 --- a/gcc.spec +++ b/gcc.spec @@ -4,7 +4,7 @@ %global gcc_major 8 # Note, gcc_release must be integer, if you want to add suffixes to # %%{release}, append them after %%{gcc_release} on Release: line. -%global gcc_release 18 +%global gcc_release 19 %global nvptx_tools_gitrev c28050f60193b3b95a18866a96f03334e874e78f %global nvptx_newlib_gitrev aadc8eb0ec43b7cd0dd2dfb484bae63c8b05ef24 %global _unpackaged_files_terminate_build 0 @@ -298,6 +298,9 @@ Patch37: gcc8-pr105502.patch Patch38: gcc8-pr99536.patch Patch39: gcc8-libstdc++-make_shared.patch Patch40: gcc8-rh2137448.patch +Patch41: gcc11-s390x-regarg-1.patch +Patch42: gcc11-s390x-regarg-2.patch +Patch43: gcc11-s390x-regarg-3.patch Patch1000: nvptx-tools-no-ptxas.patch Patch1001: nvptx-tools-build.patch @@ -913,6 +916,9 @@ so that there cannot be any synchronization problems. %patch38 -p1 -b .pr99536~ %patch39 -p1 -b .make_shared~ %patch40 -p1 -b .rh2137448~ +%patch41 -p1 -b .s390x-regarg-1~ +%patch42 -p1 -b .s390x-regarg-2~ +%patch43 -p1 -b .s390x-regarg-3~ cd nvptx-tools-%{nvptx_tools_gitrev} %patch1000 -p1 -b .nvptx-tools-no-ptxas~ @@ -3320,6 +3326,9 @@ fi %{ANNOBIN_GCC_PLUGIN_DIR}/gcc-annobin.so.0.0.0 %changelog +* Tue Apr 4 2023 Marek Polacek 8.5.0-19 +- s390x: add support for register arguments preserving (#2168205) + * Tue Dec 6 2022 Marek Polacek 8.5.0-18 - fix strlen range with a flexible member array (#2137448) diff --git a/gcc8-s390x-regarg-1.patch b/gcc8-s390x-regarg-1.patch new file mode 100644 index 0000000..b8de714 --- /dev/null +++ b/gcc8-s390x-regarg-1.patch @@ -0,0 +1,91 @@ +commit ef5f7b89bbc352255595069eb870d6f30f1f9134 +Author: Andreas Krebbel +Date: Wed Feb 1 08:59:41 2023 +0100 + + New reg note REG_CFA_NORESTORE + + This patch introduces a new reg note which can be used to tell the CFI + verification in dwarf2cfi that a register is stored without intending + to restore from it. + + This is useful when storing e.g. register contents to the stack and + generate CFI for it although the register is not really supposed to be + restored. + + gcc/ChangeLog: + + * dwarf2cfi.c (dwarf2out_frame_debug_cfa_restore): Add + EMIT_CFI parameter. + (dwarf2out_frame_debug): Add case for REG_CFA_NORESTORE. + * reg-notes.def (REG_CFA_NOTE): New reg note definition. + +--- a/gcc/dwarf2cfi.c ++++ b/gcc/dwarf2cfi.c +@@ -1496,10 +1496,12 @@ dwarf2out_frame_debug_cfa_val_expression (rtx set) + update_row_reg_save (cur_row, dwf_regno (dest), cfi); + } + +-/* A subroutine of dwarf2out_frame_debug, process a REG_CFA_RESTORE note. */ ++/* A subroutine of dwarf2out_frame_debug, process a REG_CFA_RESTORE ++ note. When called with EMIT_CFI set to false emitting a CFI ++ statement is suppressed. */ + + static void +-dwarf2out_frame_debug_cfa_restore (rtx reg) ++dwarf2out_frame_debug_cfa_restore (rtx reg, bool emit_cfi) + { + gcc_assert (REG_P (reg)); + +@@ -1507,7 +1509,8 @@ dwarf2out_frame_debug_cfa_restore (rtx reg) + if (!span) + { + unsigned int regno = dwf_regno (reg); +- add_cfi_restore (regno); ++ if (emit_cfi) ++ add_cfi_restore (regno); + update_row_reg_save (cur_row, regno, NULL); + } + else +@@ -1522,7 +1525,8 @@ dwarf2out_frame_debug_cfa_restore (rtx reg) + reg = XVECEXP (span, 0, par_index); + gcc_assert (REG_P (reg)); + unsigned int regno = dwf_regno (reg); +- add_cfi_restore (regno); ++ if (emit_cfi) ++ add_cfi_restore (regno); + update_row_reg_save (cur_row, regno, NULL); + } + } +@@ -2309,6 +2313,7 @@ dwarf2out_frame_debug (rtx_insn *insn) + break; + + case REG_CFA_RESTORE: ++ case REG_CFA_NO_RESTORE: + n = XEXP (note, 0); + if (n == NULL) + { +@@ -2317,7 +2322,7 @@ dwarf2out_frame_debug (rtx_insn *insn) + n = XVECEXP (n, 0, 0); + n = XEXP (n, 0); + } +- dwarf2out_frame_debug_cfa_restore (n); ++ dwarf2out_frame_debug_cfa_restore (n, REG_NOTE_KIND (note) == REG_CFA_RESTORE); + handled_one = true; + break; + +diff --git a/gcc/reg-notes.def b/gcc/reg-notes.def +index 23de1f13ee9..1f74a605b3e 100644 +--- a/gcc/reg-notes.def ++++ b/gcc/reg-notes.def +@@ -157,6 +157,11 @@ REG_CFA_NOTE (CFA_VAL_EXPRESSION) + first pattern is the register to be restored. */ + REG_CFA_NOTE (CFA_RESTORE) + ++/* Like CFA_RESTORE but without actually emitting CFI. This can be ++ used to tell the verification infrastructure that a register is ++ saved without intending to restore it. */ ++REG_CFA_NOTE (CFA_NO_RESTORE) ++ + /* Attached to insns that are RTX_FRAME_RELATED_P, marks insn that sets + vDRAP from DRAP. If vDRAP is a register, vdrap_reg is initalized + to the argument, if it is a MEM, it is ignored. */ diff --git a/gcc8-s390x-regarg-2.patch b/gcc8-s390x-regarg-2.patch new file mode 100644 index 0000000..834d062 --- /dev/null +++ b/gcc8-s390x-regarg-2.patch @@ -0,0 +1,106 @@ +commit 1467907e405bc18fb6935b5d432cfa5f936e838d +Author: Andreas Krebbel +Date: Thu Mar 30 13:57:54 2023 +0200 + + IBM zSystems: Make stack_tie to work with hard frame pointer + + With this patch a scheduling barrier is created to prevent the insn + setting up the frame-pointer and instructions which save GPRs to the + stack to be swapped. Otherwise broken CFI information would be + generated since the stack save insns would use a base register which + is not currently declared as holding the CFA. + + Without -mpreserve-args this did not happen because the store multiple + we used for saving the GPRs would also cover the frame-pointer + register and therefore creates a dependency on the frame-pointer + hardreg. However, with this patch the stack_tie is emitted regardless + of -mpreserve-args since this in general appears to be the safer + approach. + + * config/s390/s390.c (save_gprs): Use gen_frame_mem. + (restore_gprs): Likewise. + (s390_emit_stack_tie): Make the stack_tie to be dependent on the + frame pointer if a frame-pointer is used. + (s390_emit_prologue): Emit stack_tie when frame-pointer is needed. + * config/s390/s390.md (stack_tie): Add a register operand and + rename to ... + (stack_tiesi, stack_tiedi): ... this. + +diff --git a/gcc/config/s390/s390.c b/gcc/config/s390/s390.c +index 20424537558..952c708be7a 100644 +--- a/gcc/config/s390/s390.c ++++ b/gcc/config/s390/s390.c +@@ -10921,9 +10921,7 @@ save_gprs (rtx base, int offset, int first, int last) + int i; + + addr = plus_constant (Pmode, base, offset); +- addr = gen_rtx_MEM (Pmode, addr); +- +- set_mem_alias_set (addr, get_frame_alias_set ()); ++ addr = gen_frame_mem (Pmode, addr); + + /* Special-case single register. */ + if (first == last) +@@ -11035,8 +11033,7 @@ restore_gprs (rtx base, int offset, int first, int last) + rtx addr, insn; + + addr = plus_constant (Pmode, base, offset); +- addr = gen_rtx_MEM (Pmode, addr); +- set_mem_alias_set (addr, get_frame_alias_set ()); ++ addr = gen_frame_mem (Pmode, addr); + + /* Special-case single register. */ + if (first == last) +@@ -11105,10 +11102,14 @@ s390_load_got (void) + static void + s390_emit_stack_tie (void) + { +- rtx mem = gen_frame_mem (BLKmode, +- gen_rtx_REG (Pmode, STACK_POINTER_REGNUM)); +- +- emit_insn (gen_stack_tie (mem)); ++ rtx mem = gen_frame_mem (BLKmode, stack_pointer_rtx); ++ rtx stack_reg = (frame_pointer_needed ++ ? hard_frame_pointer_rtx ++ : stack_pointer_rtx); ++ if (TARGET_64BIT) ++ emit_insn (gen_stack_tiedi (mem, stack_reg)); ++ else ++ emit_insn (gen_stack_tiesi (mem, stack_reg)); + } + + /* Copy GPRS into FPR save slots. */ +@@ -11701,6 +11702,7 @@ s390_emit_prologue (void) + + if (frame_pointer_needed) + { ++ s390_emit_stack_tie (); + insn = emit_move_insn (hard_frame_pointer_rtx, stack_pointer_rtx); + RTX_FRAME_RELATED_P (insn) = 1; + } +diff --git a/gcc/config/s390/s390.md b/gcc/config/s390/s390.md +index 7114609b676..f235df3a25d 100644 +--- a/gcc/config/s390/s390.md ++++ b/gcc/config/s390/s390.md +@@ -11366,10 +11366,19 @@ + ; This is used in s390_emit_prologue in order to prevent insns + ; adjusting the stack pointer to be moved over insns writing stack + ; slots using a copy of the stack pointer in a different register. +-(define_insn "stack_tie" ++(define_insn "stack_tiesi" + [(set (match_operand:BLK 0 "memory_operand" "+m") +- (unspec:BLK [(match_dup 0)] UNSPEC_TIE))] ++ (unspec:BLK [(match_dup 0) ++ (match_operand:SI 1 "register_operand" "r")] UNSPEC_TIE))] ++ "!TARGET_64BIT" + "" ++ [(set_attr "length" "0")]) ++ ++(define_insn "stack_tiedi" ++ [(set (match_operand:BLK 0 "memory_operand" "+m") ++ (unspec:BLK [(match_dup 0) ++ (match_operand:DI 1 "register_operand" "r")] UNSPEC_TIE))] ++ "TARGET_64BIT" + "" + [(set_attr "length" "0")]) + diff --git a/gcc8-s390x-regarg-3.patch b/gcc8-s390x-regarg-3.patch new file mode 100644 index 0000000..943a589 --- /dev/null +++ b/gcc8-s390x-regarg-3.patch @@ -0,0 +1,545 @@ +commit 8091199cdf4d0aa9c28e4526548ddc25d02898ca +Author: Andreas Krebbel +Date: Wed Feb 1 08:59:42 2023 +0100 + + IBM zSystems: Save argument registers to the stack -mpreserve-args + + This adds support for preserving the content of parameter registers to + the stack and emit CFI for it. This useful for applications which want + to implement their own stack unwinding and need access to function + arguments. + + With the -mpreserve-args option GPRs and FPRs are save to the stack + slots which are reserved for stdargs in the register save area. + + gcc/ChangeLog: + + * config/s390/s390.c (s390_restore_gpr_p): New function. + (s390_preserve_gpr_arg_in_range_p): New function. + (s390_preserve_gpr_arg_p): New function. + (s390_preserve_fpr_arg_p): New function. + (s390_register_info_stdarg_fpr): Rename to ... + (s390_register_info_arg_fpr): ... this. Add -mpreserve-args handling. + (s390_register_info_stdarg_gpr): Rename to ... + (s390_register_info_arg_gpr): ... this. Add -mpreserve-args handling. + (s390_register_info): Use the renamed functions above. + (s390_optimize_register_info): Likewise. + (save_fpr): Generate CFI for -mpreserve-args. + (save_gprs): Generate CFI for -mpreserve-args. Drop return value. + (s390_emit_prologue): Adjust to changed calling convention of save_gprs. + (s390_optimize_prologue): Likewise. + * config/s390/s390.opt: New option -mpreserve-args + + gcc/testsuite/ChangeLog: + + * gcc.target/s390/preserve-args-1.c: New test. + * gcc.target/s390/preserve-args-2.c: New test. + +--- a/gcc/config/s390/s390.c ++++ b/gcc/config/s390/s390.c +@@ -411,6 +411,45 @@ struct s390_address + #define FP_ARG_NUM_REG (TARGET_64BIT? 4 : 2) + #define VEC_ARG_NUM_REG 8 + ++/* Return TRUE if GPR REGNO is supposed to be restored in the function ++ epilogue. */ ++static inline bool ++s390_restore_gpr_p (int regno) ++{ ++ return (cfun_frame_layout.first_restore_gpr != -1 ++ && regno >= cfun_frame_layout.first_restore_gpr ++ && regno <= cfun_frame_layout.last_restore_gpr); ++} ++ ++/* Return TRUE if any of the registers in range [FIRST, LAST] is saved ++ because of -mpreserve-args. */ ++static inline bool ++s390_preserve_gpr_arg_in_range_p (int first, int last) ++{ ++ int num_arg_regs = MIN (crtl->args.info.gprs + cfun->va_list_gpr_size, ++ GP_ARG_NUM_REG); ++ return (num_arg_regs ++ && s390_preserve_args_p ++ && first <= GPR2_REGNUM + num_arg_regs - 1 ++ && last >= GPR2_REGNUM); ++} ++ ++static inline bool ++s390_preserve_gpr_arg_p (int regno) ++{ ++ return s390_preserve_gpr_arg_in_range_p (regno, regno); ++} ++ ++static inline bool ++s390_preserve_fpr_arg_p (int regno) ++{ ++ int num_arg_regs = MIN (crtl->args.info.fprs + cfun->va_list_fpr_size, ++ FP_ARG_NUM_REG); ++ return (s390_preserve_args_p ++ && regno <= FPR0_REGNUM + num_arg_regs - 1 ++ && regno >= FPR0_REGNUM); ++} ++ + /* A couple of shortcuts. */ + #define CONST_OK_FOR_J(x) \ + CONST_OK_FOR_CONSTRAINT_P((x), 'J', "J") +@@ -9893,61 +9932,89 @@ s390_register_info_gprtofpr () + } + + /* Set the bits in fpr_bitmap for FPRs which need to be saved due to +- stdarg. ++ stdarg or -mpreserve-args. + This is a helper routine for s390_register_info. */ +- + static void +-s390_register_info_stdarg_fpr () ++s390_register_info_arg_fpr () + { + int i; +- int min_fpr; +- int max_fpr; ++ int min_stdarg_fpr = INT_MAX, max_stdarg_fpr = -1; ++ int min_preserve_fpr = INT_MAX, max_preserve_fpr = -1; ++ int min_fpr, max_fpr; + + /* Save the FP argument regs for stdarg. f0, f2 for 31 bit and + f0-f4 for 64 bit. */ +- if (!cfun->stdarg +- || !TARGET_HARD_FLOAT +- || !cfun->va_list_fpr_size +- || crtl->args.info.fprs >= FP_ARG_NUM_REG) +- return; ++ if (cfun->stdarg ++ && TARGET_HARD_FLOAT ++ && cfun->va_list_fpr_size ++ && crtl->args.info.fprs < FP_ARG_NUM_REG) ++ { ++ min_stdarg_fpr = crtl->args.info.fprs; ++ max_stdarg_fpr = min_stdarg_fpr + cfun->va_list_fpr_size - 1; ++ if (max_stdarg_fpr >= FP_ARG_NUM_REG) ++ max_stdarg_fpr = FP_ARG_NUM_REG - 1; ++ ++ /* FPR argument regs start at f0. */ ++ min_stdarg_fpr += FPR0_REGNUM; ++ max_stdarg_fpr += FPR0_REGNUM; ++ } + +- min_fpr = crtl->args.info.fprs; +- max_fpr = min_fpr + cfun->va_list_fpr_size - 1; +- if (max_fpr >= FP_ARG_NUM_REG) +- max_fpr = FP_ARG_NUM_REG - 1; ++ if (s390_preserve_args_p && crtl->args.info.fprs) ++ { ++ min_preserve_fpr = FPR0_REGNUM; ++ max_preserve_fpr = MIN (FPR0_REGNUM + FP_ARG_NUM_REG - 1, ++ FPR0_REGNUM + crtl->args.info.fprs - 1); ++ } + +- /* FPR argument regs start at f0. */ +- min_fpr += FPR0_REGNUM; +- max_fpr += FPR0_REGNUM; ++ min_fpr = MIN (min_stdarg_fpr, min_preserve_fpr); ++ max_fpr = MAX (max_stdarg_fpr, max_preserve_fpr); ++ ++ if (max_fpr == -1) ++ return; + + for (i = min_fpr; i <= max_fpr; i++) + cfun_set_fpr_save (i); + } + ++ + /* Reserve the GPR save slots for GPRs which need to be saved due to +- stdarg. ++ stdarg or -mpreserve-args. + This is a helper routine for s390_register_info. */ + + static void +-s390_register_info_stdarg_gpr () ++s390_register_info_arg_gpr () + { + int i; +- int min_gpr; +- int max_gpr; ++ int min_stdarg_gpr = INT_MAX, max_stdarg_gpr = -1; ++ int min_preserve_gpr = INT_MAX, max_preserve_gpr = -1; ++ int min_gpr, max_gpr; + +- if (!cfun->stdarg +- || !cfun->va_list_gpr_size +- || crtl->args.info.gprs >= GP_ARG_NUM_REG) +- return; ++ if (cfun->stdarg ++ && cfun->va_list_gpr_size ++ && crtl->args.info.gprs < GP_ARG_NUM_REG) ++ { ++ min_stdarg_gpr = crtl->args.info.gprs; ++ max_stdarg_gpr = min_stdarg_gpr + cfun->va_list_gpr_size - 1; ++ if (max_stdarg_gpr >= GP_ARG_NUM_REG) ++ max_stdarg_gpr = GP_ARG_NUM_REG - 1; ++ ++ /* GPR argument regs start at r2. */ ++ min_stdarg_gpr += GPR2_REGNUM; ++ max_stdarg_gpr += GPR2_REGNUM; ++ } ++ ++ if (s390_preserve_args_p && crtl->args.info.gprs) ++ { ++ min_preserve_gpr = GPR2_REGNUM; ++ max_preserve_gpr = MIN (GPR6_REGNUM, ++ GPR2_REGNUM + crtl->args.info.gprs - 1); ++ } + +- min_gpr = crtl->args.info.gprs; +- max_gpr = min_gpr + cfun->va_list_gpr_size - 1; +- if (max_gpr >= GP_ARG_NUM_REG) +- max_gpr = GP_ARG_NUM_REG - 1; ++ min_gpr = MIN (min_stdarg_gpr, min_preserve_gpr); ++ max_gpr = MAX (max_stdarg_gpr, max_preserve_gpr); + +- /* GPR argument regs start at r2. */ +- min_gpr += GPR2_REGNUM; +- max_gpr += GPR2_REGNUM; ++ if (max_gpr == -1) ++ return; + + /* If r6 was supposed to be saved into an FPR and now needs to go to + the stack for vararg we have to adjust the restore range to make +@@ -10079,14 +10146,14 @@ s390_register_info () + if (clobbered_regs[i]) + cfun_gpr_save_slot (i) = SAVE_SLOT_STACK; + +- s390_register_info_stdarg_fpr (); ++ s390_register_info_arg_fpr (); + s390_register_info_gprtofpr (); + s390_register_info_set_ranges (); +- /* stdarg functions might need to save GPRs 2 to 6. This might +- override the GPR->FPR save decision made by +- s390_register_info_gprtofpr for r6 since vararg regs must go to +- the stack. */ +- s390_register_info_stdarg_gpr (); ++ ++ /* Forcing argument registers to be saved on the stack might ++ override the GPR->FPR save decision for r6 so this must come ++ last. */ ++ s390_register_info_arg_gpr (); + } + + /* Return true if REGNO is a global register, but not one +@@ -10141,7 +10208,7 @@ s390_optimize_register_info () + cfun_gpr_save_slot (i) = SAVE_SLOT_NONE; + + s390_register_info_set_ranges (); +- s390_register_info_stdarg_gpr (); ++ s390_register_info_arg_gpr (); + } + + /* Fill cfun->machine with info about frame of current function. */ +@@ -10864,14 +10931,28 @@ static rtx + save_fpr (rtx base, int offset, int regnum) + { + rtx addr; ++ rtx insn; ++ + addr = gen_rtx_MEM (DFmode, plus_constant (Pmode, base, offset)); + +- if (regnum >= 16 && regnum <= (16 + FP_ARG_NUM_REG)) ++ if (regnum >= FPR0_REGNUM && regnum <= (FPR0_REGNUM + FP_ARG_NUM_REG)) + set_mem_alias_set (addr, get_varargs_alias_set ()); + else + set_mem_alias_set (addr, get_frame_alias_set ()); + +- return emit_move_insn (addr, gen_rtx_REG (DFmode, regnum)); ++ insn = emit_move_insn (addr, gen_rtx_REG (DFmode, regnum)); ++ ++ if (!call_used_regs[regnum] || s390_preserve_fpr_arg_p (regnum)) ++ RTX_FRAME_RELATED_P (insn) = 1; ++ ++ if (s390_preserve_fpr_arg_p (regnum) && !cfun_fpr_save_p (regnum)) ++ { ++ rtx reg = gen_rtx_REG (DFmode, regnum); ++ add_reg_note (insn, REG_CFA_NO_RESTORE, reg); ++ add_reg_note (insn, REG_CFA_OFFSET, gen_rtx_SET (addr, reg)); ++ } ++ ++ return insn; + } + + /* Emit insn to restore fpr REGNUM from offset OFFSET relative +@@ -10891,10 +10972,11 @@ restore_fpr (rtx base, int offset, int regnum) + the register save area located at offset OFFSET + relative to register BASE. */ + +-static rtx +-save_gprs (rtx base, int offset, int first, int last) ++static void ++save_gprs (rtx base, int offset, int first, int last, rtx_insn *before = NULL) + { + rtx addr, insn, note; ++ rtx_insn *out_insn; + int i; + + addr = plus_constant (Pmode, base, offset); +@@ -10910,7 +10992,15 @@ save_gprs (rtx base, int offset, int first, int last) + + if (!global_not_special_regno_p (first)) + RTX_FRAME_RELATED_P (insn) = 1; +- return insn; ++ ++ if (s390_preserve_gpr_arg_p (first) && !s390_restore_gpr_p (first)) ++ { ++ rtx reg = gen_rtx_REG (Pmode, first); ++ add_reg_note (insn, REG_CFA_NO_RESTORE, reg); ++ add_reg_note (insn, REG_CFA_OFFSET, gen_rtx_SET (addr, reg)); ++ } ++ ++ goto emit; + } + + +@@ -10939,7 +11029,12 @@ save_gprs (rtx base, int offset, int first, int last) + set, even if it does not. Therefore we emit a new pattern + without those registers as REG_FRAME_RELATED_EXPR note. */ + +- if (first >= 6 && !global_not_special_regno_p (first)) ++ /* In these cases all of the sets are marked as frame related: ++ 1. call-save GPR saved and restored ++ 2. argument GPR saved because of -mpreserve-args */ ++ if ((first >= GPR6_REGNUM && !global_not_special_regno_p (first)) ++ || s390_preserve_gpr_arg_in_range_p (first, last)) ++ + { + rtx pat = PATTERN (insn); + +@@ -10950,6 +11045,24 @@ save_gprs (rtx base, int offset, int first, int last) + RTX_FRAME_RELATED_P (XVECEXP (pat, 0, i)) = 1; + + RTX_FRAME_RELATED_P (insn) = 1; ++ ++ /* For the -mpreserve-args register saves no restore operations ++ will be emitted. CFI checking would complain about this. We ++ manually generate the REG_CFA notes here to be able to mark ++ those operations with REG_CFA_NO_RESTORE. */ ++ if (s390_preserve_gpr_arg_in_range_p (first, last)) ++ { ++ for (int regno = first; regno <= last; regno++) ++ { ++ rtx reg = gen_rtx_REG (Pmode, regno); ++ rtx reg_addr = plus_constant (Pmode, base, ++ offset + (regno - first) * UNITS_PER_LONG); ++ if (!s390_restore_gpr_p (regno)) ++ add_reg_note (insn, REG_CFA_NO_RESTORE, reg); ++ add_reg_note (insn, REG_CFA_OFFSET, ++ gen_rtx_SET (gen_frame_mem (Pmode, reg_addr), reg)); ++ } ++ } + } + else if (last >= 6) + { +@@ -10960,7 +11073,7 @@ save_gprs (rtx base, int offset, int first, int last) + break; + + if (start > last) +- return insn; ++ goto emit; + + addr = plus_constant (Pmode, base, + offset + (start - first) * UNITS_PER_LONG); +@@ -10978,7 +11091,7 @@ save_gprs (rtx base, int offset, int first, int last) + add_reg_note (insn, REG_FRAME_RELATED_EXPR, note); + RTX_FRAME_RELATED_P (insn) = 1; + +- return insn; ++ goto emit; + } + + note = gen_store_multiple (gen_rtx_MEM (Pmode, addr), +@@ -10997,9 +11110,15 @@ save_gprs (rtx base, int offset, int first, int last) + RTX_FRAME_RELATED_P (insn) = 1; + } + +- return insn; ++ emit: ++ if (before != NULL_RTX) ++ out_insn = emit_insn_before (insn, before); ++ else ++ out_insn = emit_insn (insn); ++ INSN_ADDRESSES_NEW (out_insn, -1); + } + ++ + /* Generate insn to restore registers FIRST to LAST from + the register save area located at offset OFFSET + relative to register BASE. */ +@@ -11423,12 +11542,12 @@ s390_emit_prologue (void) + /* Save call saved gprs. */ + if (cfun_frame_layout.first_save_gpr != -1) + { +- insn = save_gprs (stack_pointer_rtx, +- cfun_frame_layout.gprs_offset + +- UNITS_PER_LONG * (cfun_frame_layout.first_save_gpr +- - cfun_frame_layout.first_save_gpr_slot), +- cfun_frame_layout.first_save_gpr, +- cfun_frame_layout.last_save_gpr); ++ save_gprs (stack_pointer_rtx, ++ cfun_frame_layout.gprs_offset + ++ UNITS_PER_LONG * (cfun_frame_layout.first_save_gpr ++ - cfun_frame_layout.first_save_gpr_slot), ++ cfun_frame_layout.first_save_gpr, ++ cfun_frame_layout.last_save_gpr); + + /* This is not 100% correct. If we have more than one register saved, + then LAST_PROBE_OFFSET can move even closer to sp. */ +@@ -11436,8 +11555,6 @@ s390_emit_prologue (void) + = (cfun_frame_layout.gprs_offset + + UNITS_PER_LONG * (cfun_frame_layout.first_save_gpr + - cfun_frame_layout.first_save_gpr_slot)); +- +- emit_insn (insn); + } + + /* Dummy insn to mark literal pool slot. */ +@@ -11467,15 +11584,10 @@ s390_emit_prologue (void) + { + if (cfun_fpr_save_p (i)) + { +- insn = save_fpr (stack_pointer_rtx, offset, i); ++ save_fpr (stack_pointer_rtx, offset, i); + if (offset < last_probe_offset) + last_probe_offset = offset; + 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]) +- RTX_FRAME_RELATED_P (insn) = 1; + } + else if (!TARGET_PACKED_STACK || call_really_used_regs[i]) + offset += 8; +@@ -11491,11 +11603,10 @@ s390_emit_prologue (void) + for (i = FPR15_REGNUM; i >= FPR8_REGNUM && offset >= 0; i--) + if (cfun_fpr_save_p (i)) + { +- insn = save_fpr (stack_pointer_rtx, offset, i); ++ save_fpr (stack_pointer_rtx, offset, i); + if (offset < last_probe_offset) + last_probe_offset = offset; + +- RTX_FRAME_RELATED_P (insn) = 1; + offset -= 8; + } + if (offset >= cfun_frame_layout.f8_offset) +@@ -11663,7 +11774,6 @@ s390_emit_prologue (void) + + insn = save_fpr (temp_reg, offset, i); + offset += 8; +- RTX_FRAME_RELATED_P (insn) = 1; + add_reg_note (insn, REG_FRAME_RELATED_EXPR, + gen_rtx_SET (gen_rtx_MEM (DFmode, addr), + gen_rtx_REG (DFmode, i))); +@@ -14158,15 +14268,11 @@ s390_optimize_prologue (void) + continue; + + if (cfun_frame_layout.first_save_gpr != -1) +- { +- rtx s_pat = save_gprs (base, +- off + (cfun_frame_layout.first_save_gpr +- - first) * UNITS_PER_LONG, +- cfun_frame_layout.first_save_gpr, +- cfun_frame_layout.last_save_gpr); +- new_insn = emit_insn_before (s_pat, insn); +- INSN_ADDRESSES_NEW (new_insn, -1); +- } ++ save_gprs (base, ++ off + (cfun_frame_layout.first_save_gpr ++ - first) * UNITS_PER_LONG, ++ cfun_frame_layout.first_save_gpr, ++ cfun_frame_layout.last_save_gpr, insn); + + remove_insn (insn); + continue; +diff --git a/gcc/config/s390/s390.opt b/gcc/config/s390/s390.opt +index 57d1b95bd65..344aa551f44 100644 +--- a/gcc/config/s390/s390.opt ++++ b/gcc/config/s390/s390.opt +@@ -321,3 +321,7 @@ and the default behavior is to emit separate multiplication and addition + -mindirect-branch* or -mfunction-return* options. The sections + consist of an array of 32 bit elements. Each entry holds the offset + from the entry to the patched location. ++ ++mpreserve-args ++Target Var(s390_preserve_args_p) Init(0) ++Store all argument registers on the stack. +diff --git a/gcc/testsuite/gcc.target/s390/preserve-args-1.c b/gcc/testsuite/gcc.target/s390/preserve-args-1.c +new file mode 100644 +index 00000000000..24dcf547432 +--- /dev/null ++++ b/gcc/testsuite/gcc.target/s390/preserve-args-1.c +@@ -0,0 +1,17 @@ ++/* Functional tests for the -mpreserve-args cmdline option. */ ++ ++/* { dg-do compile } */ ++/* { dg-options "-O3 -march=z900 -mpreserve-args" } */ ++ ++ ++int ++foo (int a, int b, int c, double d, double e) ++{ ++ return a + c + (int)d + (int)e; ++} ++ ++/* { dg-final { scan-assembler "stmg\t%r2,%r4,\[0-9\]*\\(%r15\\)" { target lp64 } } } */ ++/* { dg-final { scan-assembler "stm\t%r2,%r4,\[0-9\]*\\(%r15\\)" { target { ! lp64 } } } } */ ++ ++/* { dg-final { scan-assembler "std\t%f0,\[0-9\]*\\(%r15\\)" } } */ ++/* { dg-final { scan-assembler "std\t%f2,\[0-9\]*\\(%r15\\)" } } */ +diff --git a/gcc/testsuite/gcc.target/s390/preserve-args-2.c b/gcc/testsuite/gcc.target/s390/preserve-args-2.c +new file mode 100644 +index 00000000000..006aad9c371 +--- /dev/null ++++ b/gcc/testsuite/gcc.target/s390/preserve-args-2.c +@@ -0,0 +1,19 @@ ++/* This test requires special handling of a GPR which is saved because ++ of -mpreserve-args but not restored. dwarf2cfi used to ICE for ++ this in maybe_record_trace_start. The solution was to introduce a ++ REG_CFA_NORESTORE reg note. */ ++ ++/* { dg-do compile } */ ++/* { dg-options "-O2 -march=z900 -mpreserve-args" } */ ++ ++void *foo (void *); ++void bar (); ++int x; ++void * ++baz (void *y) ++{ ++ if (__builtin_expect (x, 0)) ++ return foo (y); ++ bar (); ++ return foo (y); ++} +diff --git a/gcc/testsuite/gcc.target/s390/preserve-args-3.c b/gcc/testsuite/gcc.target/s390/preserve-args-3.c +new file mode 100644 +index 00000000000..f4b135ab8e6 +--- /dev/null ++++ b/gcc/testsuite/gcc.target/s390/preserve-args-3.c +@@ -0,0 +1,19 @@ ++/* Functional tests for the -mpreserve-args cmdline option. */ ++ ++/* { dg-do compile } */ ++/* { dg-options "-O3 -march=z900 -mpreserve-args" } */ ++ ++#include ++int ++foo (int a, int, int c, double d, ...) ++{ ++ va_list argp; ++ va_start(argp, d); ++ return a + c + va_arg(argp, int) + va_arg(argp, int) + (int)va_arg(argp, double); ++} ++ ++/* { dg-final { scan-assembler "stmg\t%r2,%r15,\[0-9\]*\\(%r15\\)" { target lp64 } } } */ ++/* { dg-final { scan-assembler "stm\t%r2,%r15,\[0-9\]*\\(%r15\\)" { target { ! lp64 } } } } */ ++ ++/* { dg-final { scan-assembler "std\t%f0,\[0-9\]*\\(%r15\\)" } } */ ++/* { dg-final { scan-assembler "std\t%f2,\[0-9\]*\\(%r15\\)" } } */