On Thu, Jan 30, 2014 at 8:38 PM, Jakub Jelinek <ja...@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 <ja...@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