[PATCH] Fix ARM dwarf2cfi ICE and unwind info issues (PR target/59575)
Ramana Radhakrishnan
ramana.gcc@googlemail.com
Thu Feb 6 15:25:00 GMT 2014
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
More information about the Gcc-patches
mailing list