diff --git a/gcc/config/s390/s390.cc b/gcc/config/s390/s390.cc
index f5c75395cf3..5e197b5314b 100644
--- a/gcc/config/s390/s390.cc
+++ b/gcc/config/s390/s390.cc
@@ -411,6 +411,53 @@ 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);
+}
+
+/* Return TRUE if FPR arguments need to be saved onto the stack due to -mpreserve-args.  */
+static inline bool
+s390_preserve_fpr_args_p (void)
+{
+  return (s390_preserve_args_p
+	  && (crtl->args.info.fprs + cfun->va_list_fpr_size));
+}
+
+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 +9940,90 @@ 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;
+    }
+
+  if (s390_preserve_fpr_args_p ())
+    {
+      min_preserve_fpr = FPR0_REGNUM;
+      max_preserve_fpr = min_preserve_fpr + crtl->args.info.fprs - 1;
+    }
 
-  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;
+  min_fpr = MIN (min_stdarg_fpr, min_preserve_fpr);
+  max_fpr = MAX (max_stdarg_fpr, max_preserve_fpr);
 
-  /* FPR argument regs start at f0.  */
-  min_fpr += FPR0_REGNUM;
-  max_fpr += FPR0_REGNUM;
+  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;
 
-  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;
+      /* GPR argument regs start at r2.  */
+      min_stdarg_gpr += GPR2_REGNUM;
+      max_stdarg_gpr += GPR2_REGNUM;
+    }
 
-  /* GPR argument regs start at r2.  */
-  min_gpr += GPR2_REGNUM;
-  max_gpr += GPR2_REGNUM;
+  if (s390_preserve_args_p && crtl->args.info.gprs)
+    {
+      min_preserve_gpr = GPR2_REGNUM;
+      if (crtl->args.info.gprs >= GP_ARG_NUM_REG)
+	max_preserve_gpr = min_preserve_gpr + GP_ARG_NUM_REG - 1;
+      else
+	max_preserve_gpr = min_preserve_gpr + crtl->args.info.gprs - 1;
+    }
+
+  min_gpr = MIN (min_stdarg_gpr, min_preserve_gpr);
+  max_gpr = MAX (max_stdarg_gpr, max_preserve_gpr);
+
+  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 +10155,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
@@ -10143,7 +10219,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.  */
@@ -10866,14 +10942,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_NORESTORE, 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
@@ -10893,10 +10983,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);
@@ -10914,7 +11005,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_NORESTORE, reg);
+	  add_reg_note (insn, REG_CFA_OFFSET, gen_rtx_SET (addr, reg));
+	}
+
+      goto emit;
     }
 
 
@@ -10943,7 +11042,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 >= 6 && !global_not_special_regno_p (first))
+      || s390_preserve_gpr_arg_in_range_p (first, last))
+
     {
       rtx pat = PATTERN (insn);
 
@@ -10954,6 +11058,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_NORESTORE.  */
+      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_NORESTORE, reg);
+	      add_reg_note (insn, REG_CFA_OFFSET,
+			    gen_rtx_SET (gen_frame_mem (Pmode, reg_addr), reg));
+	    }
+	}
     }
   else if (last >= 6)
     {
@@ -10964,7 +11086,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);
@@ -10982,7 +11104,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),
@@ -11001,9 +11123,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.  */
@@ -11427,12 +11555,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.  */
@@ -11440,8 +11568,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.  */
@@ -11471,15 +11597,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_used_regs[i])
-	    RTX_FRAME_RELATED_P (insn) = 1;
 	}
       else if (!TARGET_PACKED_STACK || call_used_regs[i])
 	offset += 8;
@@ -11495,11 +11616,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)
@@ -11667,7 +11787,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)));
@@ -14161,15 +14280,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 c375b9c5f72..507c1f2d103 100644
--- a/gcc/config/s390/s390.opt
+++ b/gcc/config/s390/s390.opt
@@ -325,3 +325,7 @@ purposes.
 munroll-only-small-loops
 Target Undocumented Var(unroll_only_small_loops) Init(0) Save
 ; Use conservative small loop unrolling.
+
+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);
+}
