This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
[PATCH] Fix ARM dwarf2cfi ICE and unwind info issues (PR target/59575)
- From: Jakub Jelinek <jakub at redhat dot com>
- To: Richard Earnshaw <richard dot earnshaw at arm dot com>, Ramana Radhakrishnan <ramana dot radhakrishnan at arm dot com>, Nick Clifton <nickc at redhat dot com>
- Cc: gcc-patches at gcc dot gnu dot org
- Date: Thu, 30 Jan 2014 21:38:19 +0100
- Subject: [PATCH] Fix ARM dwarf2cfi ICE and unwind info issues (PR target/59575)
- Authentication-results: sourceware.org; auth=none
- Reply-to: Jakub Jelinek <jakub at redhat dot com>
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.
--- 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