Patchwork Fix ARM dwarf2cfi ICE and unwind info issues (PR target/59575)

login
register
mail settings
Submitter Jakub Jelinek
Date Jan. 30, 2014, 8:38 p.m.
Message ID <20140130203819.GF892@tucnak.redhat.com>
Download mbox | patch
Permalink /patch/315447/
State New
Headers show

Comments

Jakub Jelinek - Jan. 30, 2014, 8:38 p.m.
Hi!

For -Os, apparently ARM backend sometimes decides to push (up to 8?) dummy
registers to stack in addition to the registers that actually need to be
saved, in order to avoid extra instruction to adjust stack pointer.
These registers shouldn't be mentioned for .debug_frame though (both because
it breaks assumptions of dwarf2cfi and because it doesn't make sense), they
are either call clobbered which don't need saving (r0-r3) or for r4-r7 would
be dummy if they aren't ever modified in the current function, again, there
is no point in saving them.  Even for ARM unwind info I think it doesn't
make sense to mention them in the unwind info, the patch instead adds .pad #NNN
directive after the .save to adjust sp correspondingly.

Kyrill has kindly bootstrapped/regtested this on arm, ok for trunk?

2014-01-30  Jakub Jelinek  <jakub@redhat.com>

	PR target/59575
	* config/arm/arm.c (emit_multi_reg_push): Add dwarf_regs_mask argument,
	don't record in REG_FRAME_RELATED_EXPR registers not set in that
	bitmask.
	(arm_expand_prologue): Adjust all callers.
	(arm_unwind_emit_sequence): Allow saved, but not important for unwind
	info, registers also at the lowest numbered registers side.  Use
	gcc_assert instead of abort, and SET_SRC/SET_DEST macros instead of
	XEXP.

	* gcc.target/arm/pr59575.c: New test.


	Jakub
Richard Henderson - Feb. 6, 2014, 3 p.m.
On 01/30/2014 12:38 PM, Jakub Jelinek wrote:
> 2014-01-30  Jakub Jelinek  <jakub@redhat.com>
> 
> 	PR target/59575
> 	* config/arm/arm.c (emit_multi_reg_push): Add dwarf_regs_mask argument,
> 	don't record in REG_FRAME_RELATED_EXPR registers not set in that
> 	bitmask.
> 	(arm_expand_prologue): Adjust all callers.
> 	(arm_unwind_emit_sequence): Allow saved, but not important for unwind
> 	info, registers also at the lowest numbered registers side.  Use
> 	gcc_assert instead of abort, and SET_SRC/SET_DEST macros instead of
> 	XEXP.
> 
> 	* gcc.target/arm/pr59575.c: New test.

Give the ARM guys another 24 hours to comment, but as I said in the PR I think
the patch is good.

r~
Ramana Radhakrishnan - Feb. 6, 2014, 3:25 p.m.
On Thu, Jan 30, 2014 at 8:38 PM, Jakub Jelinek <jakub@redhat.com> wrote:
> Hi!
>
> For -Os, apparently ARM backend sometimes decides to push (up to 8?) dummy
> registers to stack in addition to the registers that actually need to be
> saved, in order to avoid extra instruction to adjust stack pointer.
> These registers shouldn't be mentioned for .debug_frame though (both because
> it breaks assumptions of dwarf2cfi and because it doesn't make sense), they
> are either call clobbered which don't need saving (r0-r3) or for r4-r7 would
> be dummy if they aren't ever modified in the current function, again, there
> is no point in saving them.  Even for ARM unwind info I think it doesn't
> make sense to mention them in the unwind info, the patch instead adds .pad #NNN
> directive after the .save to adjust sp correspondingly.
>
> Kyrill has kindly bootstrapped/regtested this on arm, ok for trunk?

This is OK with a small comment on top of arm_unwind_emit_sequence
just describing the reasoning here. I was trying to convince myself
that the changes for the unwind information was safe and yes it
follows similar to what you describe.

regards
Ramana



>
> 2014-01-30  Jakub Jelinek  <jakub@redhat.com>
>
>         PR target/59575
>         * config/arm/arm.c (emit_multi_reg_push): Add dwarf_regs_mask argument,
>         don't record in REG_FRAME_RELATED_EXPR registers not set in that
>         bitmask.
>         (arm_expand_prologue): Adjust all callers.
>         (arm_unwind_emit_sequence): Allow saved, but not important for unwind
>         info, registers also at the lowest numbered registers side.  Use
>         gcc_assert instead of abort, and SET_SRC/SET_DEST macros instead of
>         XEXP.
>
>         * gcc.target/arm/pr59575.c: New test.
>
> --- gcc/config/arm/arm.c.jj     2014-01-29 10:21:11.448031616 +0100
> +++ gcc/config/arm/arm.c        2014-01-29 15:32:22.246381587 +0100
> @@ -177,7 +177,7 @@ static rtx arm_expand_builtin (tree, rtx
>  static tree arm_builtin_decl (unsigned, bool);
>  static void emit_constant_insn (rtx cond, rtx pattern);
>  static rtx emit_set_insn (rtx, rtx);
> -static rtx emit_multi_reg_push (unsigned long);
> +static rtx emit_multi_reg_push (unsigned long, unsigned long);
>  static int arm_arg_partial_bytes (cumulative_args_t, enum machine_mode,
>                                   tree, bool);
>  static rtx arm_function_arg (cumulative_args_t, enum machine_mode,
> @@ -19573,28 +19573,33 @@ arm_emit_strd_push (unsigned long saved_
>  /* Generate and emit an insn that we will recognize as a push_multi.
>     Unfortunately, since this insn does not reflect very well the actual
>     semantics of the operation, we need to annotate the insn for the benefit
> -   of DWARF2 frame unwind information.  */
> +   of DWARF2 frame unwind information.  DWARF_REGS_MASK is a subset of
> +   MASK for registers that should be annotated for DWARF2 frame unwind
> +   information.  */
>  static rtx
> -emit_multi_reg_push (unsigned long mask)
> +emit_multi_reg_push (unsigned long mask, unsigned long dwarf_regs_mask)
>  {
>    int num_regs = 0;
> -  int num_dwarf_regs;
> +  int num_dwarf_regs = 0;
>    int i, j;
>    rtx par;
>    rtx dwarf;
>    int dwarf_par_index;
>    rtx tmp, reg;
>
> +  /* We don't record the PC in the dwarf frame information.  */
> +  dwarf_regs_mask &= ~(1 << PC_REGNUM);
> +
>    for (i = 0; i <= LAST_ARM_REGNUM; i++)
> -    if (mask & (1 << i))
> -      num_regs++;
> +    {
> +      if (mask & (1 << i))
> +       num_regs++;
> +      if (dwarf_regs_mask & (1 << i))
> +       num_dwarf_regs++;
> +    }
>
>    gcc_assert (num_regs && num_regs <= 16);
> -
> -  /* We don't record the PC in the dwarf frame information.  */
> -  num_dwarf_regs = num_regs;
> -  if (mask & (1 << PC_REGNUM))
> -    num_dwarf_regs--;
> +  gcc_assert ((dwarf_regs_mask & ~mask) == 0);
>
>    /* For the body of the insn we are going to generate an UNSPEC in
>       parallel with several USEs.  This allows the insn to be recognized
> @@ -19660,14 +19665,13 @@ emit_multi_reg_push (unsigned long mask)
>                                            gen_rtvec (1, reg),
>                                            UNSPEC_PUSH_MULT));
>
> -         if (i != PC_REGNUM)
> +         if (dwarf_regs_mask & (1 << i))
>             {
>               tmp = gen_rtx_SET (VOIDmode,
>                                  gen_frame_mem (SImode, stack_pointer_rtx),
>                                  reg);
>               RTX_FRAME_RELATED_P (tmp) = 1;
> -             XVECEXP (dwarf, 0, dwarf_par_index) = tmp;
> -             dwarf_par_index++;
> +             XVECEXP (dwarf, 0, dwarf_par_index++) = tmp;
>             }
>
>           break;
> @@ -19682,7 +19686,7 @@ emit_multi_reg_push (unsigned long mask)
>
>           XVECEXP (par, 0, j) = gen_rtx_USE (VOIDmode, reg);
>
> -         if (i != PC_REGNUM)
> +         if (dwarf_regs_mask & (1 << i))
>             {
>               tmp
>                 = gen_rtx_SET (VOIDmode,
> @@ -20689,7 +20693,7 @@ arm_expand_prologue (void)
>           /* Interrupt functions must not corrupt any registers.
>              Creating a frame pointer however, corrupts the IP
>              register, so we must push it first.  */
> -         emit_multi_reg_push (1 << IP_REGNUM);
> +         emit_multi_reg_push (1 << IP_REGNUM, 1 << IP_REGNUM);
>
>           /* Do not set RTX_FRAME_RELATED_P on this insn.
>              The dwarf stack unwinding code only wants to see one
> @@ -20750,7 +20754,8 @@ arm_expand_prologue (void)
>               if (cfun->machine->uses_anonymous_args)
>                 {
>                   insn
> -                   = emit_multi_reg_push ((0xf0 >> (args_to_push / 4)) & 0xf);
> +                   = emit_multi_reg_push ((0xf0 >> (args_to_push / 4)) & 0xf,
> +                                          (0xf0 >> (args_to_push / 4)) & 0xf);
>                   emit_set_insn (gen_rtx_REG (SImode, 3), ip_rtx);
>                   saved_pretend_args = 1;
>                 }
> @@ -20794,7 +20799,8 @@ arm_expand_prologue (void)
>        /* Push the argument registers, or reserve space for them.  */
>        if (cfun->machine->uses_anonymous_args)
>         insn = emit_multi_reg_push
> -         ((0xf0 >> (args_to_push / 4)) & 0xf);
> +         ((0xf0 >> (args_to_push / 4)) & 0xf,
> +          (0xf0 >> (args_to_push / 4)) & 0xf);
>        else
>         insn = emit_insn
>           (gen_addsi3 (stack_pointer_rtx, stack_pointer_rtx,
> @@ -20819,6 +20825,8 @@ arm_expand_prologue (void)
>
>    if (live_regs_mask)
>      {
> +      unsigned long dwarf_regs_mask = live_regs_mask;
> +
>        saved_regs += bit_count (live_regs_mask) * 4;
>        if (optimize_size && !frame_pointer_needed
>           && saved_regs == offsets->saved_regs - offsets->saved_args)
> @@ -20845,25 +20853,22 @@ arm_expand_prologue (void)
>           && current_tune->prefer_ldrd_strd
>            && !optimize_function_for_size_p (cfun))
>          {
> +         gcc_checking_assert (live_regs_mask == dwarf_regs_mask);
>            if (TARGET_THUMB2)
> -            {
> -              thumb2_emit_strd_push (live_regs_mask);
> -            }
> +           thumb2_emit_strd_push (live_regs_mask);
>            else if (TARGET_ARM
>                     && !TARGET_APCS_FRAME
>                     && !IS_INTERRUPT (func_type))
> -            {
> -              arm_emit_strd_push (live_regs_mask);
> -            }
> +           arm_emit_strd_push (live_regs_mask);
>            else
>              {
> -              insn = emit_multi_reg_push (live_regs_mask);
> +             insn = emit_multi_reg_push (live_regs_mask, live_regs_mask);
>                RTX_FRAME_RELATED_P (insn) = 1;
>              }
>          }
>        else
>          {
> -          insn = emit_multi_reg_push (live_regs_mask);
> +         insn = emit_multi_reg_push (live_regs_mask, dwarf_regs_mask);
>            RTX_FRAME_RELATED_P (insn) = 1;
>          }
>      }
> @@ -28691,32 +28696,43 @@ arm_unwind_emit_sequence (FILE * asm_out
>    int reg_size;
>    unsigned reg;
>    unsigned lastreg;
> +  unsigned padfirst = 0, padlast = 0;
>    rtx e;
>
>    e = XVECEXP (p, 0, 0);
> -  if (GET_CODE (e) != SET)
> -    abort ();
> +  gcc_assert (GET_CODE (e) == SET);
>
>    /* First insn will adjust the stack pointer.  */
> -  if (GET_CODE (e) != SET
> -      || !REG_P (XEXP (e, 0))
> -      || REGNO (XEXP (e, 0)) != SP_REGNUM
> -      || GET_CODE (XEXP (e, 1)) != PLUS)
> -    abort ();
> +  gcc_assert (GET_CODE (e) == SET
> +             && REG_P (SET_DEST (e))
> +             && REGNO (SET_DEST (e)) == SP_REGNUM
> +             && GET_CODE (SET_SRC (e)) == PLUS);
>
> -  offset = -INTVAL (XEXP (XEXP (e, 1), 1));
> +  offset = -INTVAL (XEXP (SET_SRC (e), 1));
>    nregs = XVECLEN (p, 0) - 1;
> +  gcc_assert (nregs);
>
> -  reg = REGNO (XEXP (XVECEXP (p, 0, 1), 1));
> +  reg = REGNO (SET_SRC (XVECEXP (p, 0, 1)));
>    if (reg < 16)
>      {
> +      /* For -Os dummy registers can be pushed at the beginning to
> +        avoid separate stack pointer adjustment.  */
> +      e = XVECEXP (p, 0, 1);
> +      e = XEXP (SET_DEST (e), 0);
> +      if (GET_CODE (e) == PLUS)
> +       padfirst = INTVAL (XEXP (e, 1));
> +      gcc_assert (padfirst == 0 || optimize_size);
>        /* The function prologue may also push pc, but not annotate it as it is
>          never restored.  We turn this into a stack pointer adjustment.  */
> -      if (nregs * 4 == offset - 4)
> -       {
> -         fprintf (asm_out_file, "\t.pad #4\n");
> -         offset -= 4;
> -       }
> +      e = XVECEXP (p, 0, nregs);
> +      e = XEXP (SET_DEST (e), 0);
> +      if (GET_CODE (e) == PLUS)
> +       padlast = offset - INTVAL (XEXP (e, 1)) - 4;
> +      else
> +       padlast = offset - 4;
> +      gcc_assert (padlast == 0 || padlast == 4);
> +      if (padlast == 4)
> +       fprintf (asm_out_file, "\t.pad #4\n");
>        reg_size = 4;
>        fprintf (asm_out_file, "\t.save {");
>      }
> @@ -28727,14 +28743,13 @@ arm_unwind_emit_sequence (FILE * asm_out
>      }
>    else
>      /* Unknown register type.  */
> -    abort ();
> +    gcc_unreachable ();
>
>    /* If the stack increment doesn't match the size of the saved registers,
>       something has gone horribly wrong.  */
> -  if (offset != nregs * reg_size)
> -    abort ();
> +  gcc_assert (offset == padfirst + nregs * reg_size + padlast);
>
> -  offset = 0;
> +  offset = padfirst;
>    lastreg = 0;
>    /* The remaining insns will describe the stores.  */
>    for (i = 1; i <= nregs; i++)
> @@ -28742,14 +28757,12 @@ arm_unwind_emit_sequence (FILE * asm_out
>        /* Expect (set (mem <addr>) (reg)).
>           Where <addr> is (reg:SP) or (plus (reg:SP) (const_int)).  */
>        e = XVECEXP (p, 0, i);
> -      if (GET_CODE (e) != SET
> -         || !MEM_P (XEXP (e, 0))
> -         || !REG_P (XEXP (e, 1)))
> -       abort ();
> -
> -      reg = REGNO (XEXP (e, 1));
> -      if (reg < lastreg)
> -       abort ();
> +      gcc_assert (GET_CODE (e) == SET
> +                 && MEM_P (SET_DEST (e))
> +                 && REG_P (SET_SRC (e)));
> +
> +      reg = REGNO (SET_SRC (e));
> +      gcc_assert (reg >= lastreg);
>
>        if (i != 1)
>         fprintf (asm_out_file, ", ");
> @@ -28762,23 +28775,22 @@ arm_unwind_emit_sequence (FILE * asm_out
>
>  #ifdef ENABLE_CHECKING
>        /* Check that the addresses are consecutive.  */
> -      e = XEXP (XEXP (e, 0), 0);
> +      e = XEXP (SET_DEST (e), 0);
>        if (GET_CODE (e) == PLUS)
> -       {
> -         offset += reg_size;
> -         if (!REG_P (XEXP (e, 0))
> -             || REGNO (XEXP (e, 0)) != SP_REGNUM
> -             || !CONST_INT_P (XEXP (e, 1))
> -             || offset != INTVAL (XEXP (e, 1)))
> -           abort ();
> -       }
> -      else if (i != 1
> -              || !REG_P (e)
> -              || REGNO (e) != SP_REGNUM)
> -       abort ();
> +       gcc_assert (REG_P (XEXP (e, 0))
> +                   && REGNO (XEXP (e, 0)) == SP_REGNUM
> +                   && CONST_INT_P (XEXP (e, 1))
> +                   && offset == INTVAL (XEXP (e, 1)));
> +      else
> +       gcc_assert (i == 1
> +                   && REG_P (e)
> +                   && REGNO (e) == SP_REGNUM);
> +      offset += reg_size;
>  #endif
>      }
>    fprintf (asm_out_file, "}\n");
> +  if (padfirst)
> +    fprintf (asm_out_file, "\t.pad #%d\n", padfirst);
>  }
>
>  /*  Emit unwind directives for a SET.  */
> --- gcc/testsuite/gcc.target/arm/pr59575.c.jj   2014-01-23 15:54:25.959922593 +0100
> +++ gcc/testsuite/gcc.target/arm/pr59575.c      2014-01-23 15:54:12.000000000 +0100
> @@ -0,0 +1,15 @@
> +/* PR target/59575 */
> +/* { dg-do compile } */
> +/* { dg-options "-Os -g -march=armv7-a" } */
> +
> +void foo (int *);
> +int *bar (int, long long, int);
> +
> +void
> +test (int *p)
> +{
> +  if (p)
> +    foo (p);
> +  else if (p = bar (0, 1, 2))
> +    foo (p);
> +}
>
>         Jakub
Nick Clifton - Feb. 6, 2014, 3:40 p.m.
Hi Jakub,

> 2014-01-30  Jakub Jelinek  <jakub@redhat.com>
>
> 	PR target/59575
> 	* config/arm/arm.c (emit_multi_reg_push): Add dwarf_regs_mask argument,
> 	don't record in REG_FRAME_RELATED_EXPR registers not set in that
> 	bitmask.
> 	(arm_expand_prologue): Adjust all callers.
> 	(arm_unwind_emit_sequence): Allow saved, but not important for unwind
> 	info, registers also at the lowest numbered registers side.  Use
> 	gcc_assert instead of abort, and SET_SRC/SET_DEST macros instead of
> 	XEXP.
>
> 	* gcc.target/arm/pr59575.c: New test.

Approved - please apply.

Cheers
   Nick

Patch

--- gcc/config/arm/arm.c.jj	2014-01-29 10:21:11.448031616 +0100
+++ gcc/config/arm/arm.c	2014-01-29 15:32:22.246381587 +0100
@@ -177,7 +177,7 @@  static rtx arm_expand_builtin (tree, rtx
 static tree arm_builtin_decl (unsigned, bool);
 static void emit_constant_insn (rtx cond, rtx pattern);
 static rtx emit_set_insn (rtx, rtx);
-static rtx emit_multi_reg_push (unsigned long);
+static rtx emit_multi_reg_push (unsigned long, unsigned long);
 static int arm_arg_partial_bytes (cumulative_args_t, enum machine_mode,
 				  tree, bool);
 static rtx arm_function_arg (cumulative_args_t, enum machine_mode,
@@ -19573,28 +19573,33 @@  arm_emit_strd_push (unsigned long saved_
 /* Generate and emit an insn that we will recognize as a push_multi.
    Unfortunately, since this insn does not reflect very well the actual
    semantics of the operation, we need to annotate the insn for the benefit
-   of DWARF2 frame unwind information.  */
+   of DWARF2 frame unwind information.  DWARF_REGS_MASK is a subset of
+   MASK for registers that should be annotated for DWARF2 frame unwind
+   information.  */
 static rtx
-emit_multi_reg_push (unsigned long mask)
+emit_multi_reg_push (unsigned long mask, unsigned long dwarf_regs_mask)
 {
   int num_regs = 0;
-  int num_dwarf_regs;
+  int num_dwarf_regs = 0;
   int i, j;
   rtx par;
   rtx dwarf;
   int dwarf_par_index;
   rtx tmp, reg;
 
+  /* We don't record the PC in the dwarf frame information.  */
+  dwarf_regs_mask &= ~(1 << PC_REGNUM);
+
   for (i = 0; i <= LAST_ARM_REGNUM; i++)
-    if (mask & (1 << i))
-      num_regs++;
+    {
+      if (mask & (1 << i))
+	num_regs++;
+      if (dwarf_regs_mask & (1 << i))
+	num_dwarf_regs++;
+    }
 
   gcc_assert (num_regs && num_regs <= 16);
-
-  /* We don't record the PC in the dwarf frame information.  */
-  num_dwarf_regs = num_regs;
-  if (mask & (1 << PC_REGNUM))
-    num_dwarf_regs--;
+  gcc_assert ((dwarf_regs_mask & ~mask) == 0);
 
   /* For the body of the insn we are going to generate an UNSPEC in
      parallel with several USEs.  This allows the insn to be recognized
@@ -19660,14 +19665,13 @@  emit_multi_reg_push (unsigned long mask)
 					   gen_rtvec (1, reg),
 					   UNSPEC_PUSH_MULT));
 
-	  if (i != PC_REGNUM)
+	  if (dwarf_regs_mask & (1 << i))
 	    {
 	      tmp = gen_rtx_SET (VOIDmode,
 				 gen_frame_mem (SImode, stack_pointer_rtx),
 				 reg);
 	      RTX_FRAME_RELATED_P (tmp) = 1;
-	      XVECEXP (dwarf, 0, dwarf_par_index) = tmp;
-	      dwarf_par_index++;
+	      XVECEXP (dwarf, 0, dwarf_par_index++) = tmp;
 	    }
 
 	  break;
@@ -19682,7 +19686,7 @@  emit_multi_reg_push (unsigned long mask)
 
 	  XVECEXP (par, 0, j) = gen_rtx_USE (VOIDmode, reg);
 
-	  if (i != PC_REGNUM)
+	  if (dwarf_regs_mask & (1 << i))
 	    {
 	      tmp
 		= gen_rtx_SET (VOIDmode,
@@ -20689,7 +20693,7 @@  arm_expand_prologue (void)
 	  /* Interrupt functions must not corrupt any registers.
 	     Creating a frame pointer however, corrupts the IP
 	     register, so we must push it first.  */
-	  emit_multi_reg_push (1 << IP_REGNUM);
+	  emit_multi_reg_push (1 << IP_REGNUM, 1 << IP_REGNUM);
 
 	  /* Do not set RTX_FRAME_RELATED_P on this insn.
 	     The dwarf stack unwinding code only wants to see one
@@ -20750,7 +20754,8 @@  arm_expand_prologue (void)
 	      if (cfun->machine->uses_anonymous_args)
 		{
 		  insn
-		    = emit_multi_reg_push ((0xf0 >> (args_to_push / 4)) & 0xf);
+		    = emit_multi_reg_push ((0xf0 >> (args_to_push / 4)) & 0xf,
+					   (0xf0 >> (args_to_push / 4)) & 0xf);
 		  emit_set_insn (gen_rtx_REG (SImode, 3), ip_rtx);
 		  saved_pretend_args = 1;
 		}
@@ -20794,7 +20799,8 @@  arm_expand_prologue (void)
       /* Push the argument registers, or reserve space for them.  */
       if (cfun->machine->uses_anonymous_args)
 	insn = emit_multi_reg_push
-	  ((0xf0 >> (args_to_push / 4)) & 0xf);
+	  ((0xf0 >> (args_to_push / 4)) & 0xf,
+	   (0xf0 >> (args_to_push / 4)) & 0xf);
       else
 	insn = emit_insn
 	  (gen_addsi3 (stack_pointer_rtx, stack_pointer_rtx,
@@ -20819,6 +20825,8 @@  arm_expand_prologue (void)
 
   if (live_regs_mask)
     {
+      unsigned long dwarf_regs_mask = live_regs_mask;
+
       saved_regs += bit_count (live_regs_mask) * 4;
       if (optimize_size && !frame_pointer_needed
 	  && saved_regs == offsets->saved_regs - offsets->saved_args)
@@ -20845,25 +20853,22 @@  arm_expand_prologue (void)
 	  && current_tune->prefer_ldrd_strd
           && !optimize_function_for_size_p (cfun))
         {
+	  gcc_checking_assert (live_regs_mask == dwarf_regs_mask);
           if (TARGET_THUMB2)
-            {
-              thumb2_emit_strd_push (live_regs_mask);
-            }
+	    thumb2_emit_strd_push (live_regs_mask);
           else if (TARGET_ARM
                    && !TARGET_APCS_FRAME
                    && !IS_INTERRUPT (func_type))
-            {
-              arm_emit_strd_push (live_regs_mask);
-            }
+	    arm_emit_strd_push (live_regs_mask);
           else
             {
-              insn = emit_multi_reg_push (live_regs_mask);
+	      insn = emit_multi_reg_push (live_regs_mask, live_regs_mask);
               RTX_FRAME_RELATED_P (insn) = 1;
             }
         }
       else
         {
-          insn = emit_multi_reg_push (live_regs_mask);
+	  insn = emit_multi_reg_push (live_regs_mask, dwarf_regs_mask);
           RTX_FRAME_RELATED_P (insn) = 1;
         }
     }
@@ -28691,32 +28696,43 @@  arm_unwind_emit_sequence (FILE * asm_out
   int reg_size;
   unsigned reg;
   unsigned lastreg;
+  unsigned padfirst = 0, padlast = 0;
   rtx e;
 
   e = XVECEXP (p, 0, 0);
-  if (GET_CODE (e) != SET)
-    abort ();
+  gcc_assert (GET_CODE (e) == SET);
 
   /* First insn will adjust the stack pointer.  */
-  if (GET_CODE (e) != SET
-      || !REG_P (XEXP (e, 0))
-      || REGNO (XEXP (e, 0)) != SP_REGNUM
-      || GET_CODE (XEXP (e, 1)) != PLUS)
-    abort ();
+  gcc_assert (GET_CODE (e) == SET
+	      && REG_P (SET_DEST (e))
+	      && REGNO (SET_DEST (e)) == SP_REGNUM
+	      && GET_CODE (SET_SRC (e)) == PLUS);
 
-  offset = -INTVAL (XEXP (XEXP (e, 1), 1));
+  offset = -INTVAL (XEXP (SET_SRC (e), 1));
   nregs = XVECLEN (p, 0) - 1;
+  gcc_assert (nregs);
 
-  reg = REGNO (XEXP (XVECEXP (p, 0, 1), 1));
+  reg = REGNO (SET_SRC (XVECEXP (p, 0, 1)));
   if (reg < 16)
     {
+      /* For -Os dummy registers can be pushed at the beginning to
+	 avoid separate stack pointer adjustment.  */
+      e = XVECEXP (p, 0, 1);
+      e = XEXP (SET_DEST (e), 0);
+      if (GET_CODE (e) == PLUS)
+	padfirst = INTVAL (XEXP (e, 1));
+      gcc_assert (padfirst == 0 || optimize_size);
       /* The function prologue may also push pc, but not annotate it as it is
 	 never restored.  We turn this into a stack pointer adjustment.  */
-      if (nregs * 4 == offset - 4)
-	{
-	  fprintf (asm_out_file, "\t.pad #4\n");
-	  offset -= 4;
-	}
+      e = XVECEXP (p, 0, nregs);
+      e = XEXP (SET_DEST (e), 0);
+      if (GET_CODE (e) == PLUS)
+	padlast = offset - INTVAL (XEXP (e, 1)) - 4;
+      else
+	padlast = offset - 4;
+      gcc_assert (padlast == 0 || padlast == 4);
+      if (padlast == 4)
+	fprintf (asm_out_file, "\t.pad #4\n");
       reg_size = 4;
       fprintf (asm_out_file, "\t.save {");
     }
@@ -28727,14 +28743,13 @@  arm_unwind_emit_sequence (FILE * asm_out
     }
   else
     /* Unknown register type.  */
-    abort ();
+    gcc_unreachable ();
 
   /* If the stack increment doesn't match the size of the saved registers,
      something has gone horribly wrong.  */
-  if (offset != nregs * reg_size)
-    abort ();
+  gcc_assert (offset == padfirst + nregs * reg_size + padlast);
 
-  offset = 0;
+  offset = padfirst;
   lastreg = 0;
   /* The remaining insns will describe the stores.  */
   for (i = 1; i <= nregs; i++)
@@ -28742,14 +28757,12 @@  arm_unwind_emit_sequence (FILE * asm_out
       /* Expect (set (mem <addr>) (reg)).
          Where <addr> is (reg:SP) or (plus (reg:SP) (const_int)).  */
       e = XVECEXP (p, 0, i);
-      if (GET_CODE (e) != SET
-	  || !MEM_P (XEXP (e, 0))
-	  || !REG_P (XEXP (e, 1)))
-	abort ();
-
-      reg = REGNO (XEXP (e, 1));
-      if (reg < lastreg)
-	abort ();
+      gcc_assert (GET_CODE (e) == SET
+		  && MEM_P (SET_DEST (e))
+		  && REG_P (SET_SRC (e)));
+
+      reg = REGNO (SET_SRC (e));
+      gcc_assert (reg >= lastreg);
 
       if (i != 1)
 	fprintf (asm_out_file, ", ");
@@ -28762,23 +28775,22 @@  arm_unwind_emit_sequence (FILE * asm_out
 
 #ifdef ENABLE_CHECKING
       /* Check that the addresses are consecutive.  */
-      e = XEXP (XEXP (e, 0), 0);
+      e = XEXP (SET_DEST (e), 0);
       if (GET_CODE (e) == PLUS)
-	{
-	  offset += reg_size;
-	  if (!REG_P (XEXP (e, 0))
-	      || REGNO (XEXP (e, 0)) != SP_REGNUM
-	      || !CONST_INT_P (XEXP (e, 1))
-	      || offset != INTVAL (XEXP (e, 1)))
-	    abort ();
-	}
-      else if (i != 1
-	       || !REG_P (e)
-	       || REGNO (e) != SP_REGNUM)
-	abort ();
+	gcc_assert (REG_P (XEXP (e, 0))
+		    && REGNO (XEXP (e, 0)) == SP_REGNUM
+		    && CONST_INT_P (XEXP (e, 1))
+		    && offset == INTVAL (XEXP (e, 1)));
+      else
+	gcc_assert (i == 1
+		    && REG_P (e)
+		    && REGNO (e) == SP_REGNUM);
+      offset += reg_size;
 #endif
     }
   fprintf (asm_out_file, "}\n");
+  if (padfirst)
+    fprintf (asm_out_file, "\t.pad #%d\n", padfirst);
 }
 
 /*  Emit unwind directives for a SET.  */
--- gcc/testsuite/gcc.target/arm/pr59575.c.jj	2014-01-23 15:54:25.959922593 +0100
+++ gcc/testsuite/gcc.target/arm/pr59575.c	2014-01-23 15:54:12.000000000 +0100
@@ -0,0 +1,15 @@ 
+/* PR target/59575 */
+/* { dg-do compile } */
+/* { dg-options "-Os -g -march=armv7-a" } */
+
+void foo (int *);
+int *bar (int, long long, int);
+
+void
+test (int *p)
+{
+  if (p)
+    foo (p);
+  else if (p = bar (0, 1, 2))
+    foo (p);
+}