diff mbox

[ARM] PR target/70830: Avoid POP-{reglist}^ when returning from interrupt handlers

Message ID 573AF507.6070602@foss.arm.com
State New
Headers show

Commit Message

Kyrill Tkachov May 17, 2016, 10:40 a.m. UTC
On 13/05/16 12:05, Kyrill Tkachov wrote:
> Hi Christophe,
>
> On 12/05/16 20:57, Christophe Lyon wrote:
>> On 12 May 2016 at 11:48, Ramana Radhakrishnan <ramana.gcc@googlemail.com> wrote:
>>> On Thu, May 5, 2016 at 12:50 PM, Kyrill Tkachov
>>> <kyrylo.tkachov@foss.arm.com> wrote:
>>>> Hi all,
>>>>
>>>> In this PR we deal with some fallout from the conversion to unified
>>>> assembly.
>>>> We now end up emitting instructions like:
>>>>    pop {r0,r1,r2,r3,pc}^
>>>> which is not legal. We have to use an LDM form.
>>>>
>>>> There are bugs in two arm.c functions: output_return_instruction and
>>>> arm_output_multireg_pop.
>>>>
>>>> In output_return_instruction the buggy hunk from the conversion was:
>>>>            else
>>>> -           if (TARGET_UNIFIED_ASM)
>>>>                sprintf (instr, "pop%s\t{", conditional);
>>>> -           else
>>>> -             sprintf (instr, "ldm%sfd\t%%|sp!, {", conditional);
>>>>
>>>> The code was already very obscurely structured and arguably the bug was
>>>> latent.
>>>> It emitted POP only when TARGET_UNIFIED_ASM was on, and since
>>>> TARGET_UNIFIED_ASM was on
>>>> only for Thumb, we never went down this path interrupt handling code, since
>>>> the interrupt
>>>> attribute is only available for ARM code. After the removal of
>>>> TARGET_UNIFIED_ASM we ended up
>>>> using POP unconditionally. So this patch adds a check for IS_INTERRUPT and
>>>> outputs the
>>>> appropriate LDM form.
>>>>
>>>> In arm_output_multireg_pop the buggy hunk was:
>>>> -  if ((regno_base == SP_REGNUM) && TARGET_THUMB)
>>>> +  if ((regno_base == SP_REGNUM) && update)
>>>>       {
>>>> -      /* Output pop (not stmfd) because it has a shorter encoding.  */
>>>> -      gcc_assert (update);
>>>>         sprintf (pattern, "pop%s\t{", conditional);
>>>>       }
>>>>
>>>> Again, the POP was guarded on TARGET_THUMB and so would never be taken on
>>>> interrupt handling
>>>> routines. This patch guards that with the appropriate check on interrupt
>>>> return.
>>>>
>>>> Also, there are a couple of bugs in the 'else' branch of that 'if':
>>>> * The "ldmfd%s" was output without a '\t' at the end which meant that the
>>>> base register
>>>> name would be concatenated with the 'ldmfd', creating invalid assembly.
>>>>
>>>> * The logic:
>>>>
>>>>        if (regno_base == SP_REGNUM)
>>>>        /* update is never true here, hence there is no need to handle
>>>>           pop here.  */
>>>>      sprintf (pattern, "ldmfd%s", conditional);
>>>>
>>>>        if (update)
>>>>      sprintf (pattern, "ldmia%s\t", conditional);
>>>>        else
>>>>      sprintf (pattern, "ldm%s\t", conditional);
>>>>
>>>> Meant that for "regno == SP_REGNUM && !update" we'd end up printing
>>>> "ldmfd%sldm%s\t"
>>>> to pattern. I didn't manage to reproduce that condition though, so maybe it
>>>> can't ever occur.
>>>> This patch fixes both these issues nevertheless.
>>>>
>>>> I've added the testcase from the PR to catch the fix in
>>>> output_return_instruction.
>>>> The testcase doesn't catch the bugs in arm_output_multireg_pop, but the
>>>> existing tests
>>>> gcc.target/arm/interrupt-1.c and gcc.target/arm/interrupt-2.c would have
>>>> caught them
>>>> if only they were assemble tests rather than just compile. So this patch
>>>> makes them
>>>> assembly tests (and reverts the scan-assembler checks for the correct LDM
>>>> pattern).
>>>>
>>>> Bootstrapped and tested on arm-none-linux-gnueabihf.
>>>> Ok for trunk and GCC 6?
>>>>
>> Hi Kyrill,
>>
>> Did you test --with-mode=thumb?
>> When using arm mode, I see regressions:
>>
>>    gcc.target/arm/neon-nested-apcs.c (test for excess errors)
>>    gcc.target/arm/nested-apcs.c (test for excess errors)
>
> It's because I have a local patch in my binutils that makes gas warn on the
> deprecated sequences that these two tests generate (they use the deprecated -mapcs option),
> so these tests were already showing the (test for excess errors) FAIL for me,
> so I they didn't appear in my tests diff for this patch. :(
>
> I've reproduced the failure with a clean tree.
> Where before we generated:
>     ldm    sp, {fp, sp, pc}
> now we generate:
>     pop    {fp, sp, pc}
>
> which are not equivalent (pop performs a write-back) and gas warns:
> Warning: writeback of base register when in register list is UNPREDICTABLE
>
> I'm testing a patch to fix this.
> Sorry for the regression.

Here is the fix.
I had remove the update from the condition for the "pop" erroneously. Of course, if we're not
updating the SP we can't use POP that has an implicit writeback.

Bootstrapped on arm-none-linux-gnueabihf. Tested with -mthumb and -marm.

Ok for trunk and GCC 6?

Thanks,
Kyrill

2016-05-17  Kyrylo Tkachov  <kyrylo.tkachov@arm.com>

     PR target/70830
     * config/arm/arm.c (arm_output_multireg_pop): Guard "pop" on update.

Comments

Kyrill Tkachov May 24, 2016, 12:48 p.m. UTC | #1
Ping.
https://gcc.gnu.org/ml/gcc-patches/2016-05/msg01211.html

Thanks,
Kyrill

On 17/05/16 11:40, Kyrill Tkachov wrote:
>
> On 13/05/16 12:05, Kyrill Tkachov wrote:
>> Hi Christophe,
>>
>> On 12/05/16 20:57, Christophe Lyon wrote:
>>> On 12 May 2016 at 11:48, Ramana Radhakrishnan <ramana.gcc@googlemail.com> wrote:
>>>> On Thu, May 5, 2016 at 12:50 PM, Kyrill Tkachov
>>>> <kyrylo.tkachov@foss.arm.com> wrote:
>>>>> Hi all,
>>>>>
>>>>> In this PR we deal with some fallout from the conversion to unified
>>>>> assembly.
>>>>> We now end up emitting instructions like:
>>>>>    pop {r0,r1,r2,r3,pc}^
>>>>> which is not legal. We have to use an LDM form.
>>>>>
>>>>> There are bugs in two arm.c functions: output_return_instruction and
>>>>> arm_output_multireg_pop.
>>>>>
>>>>> In output_return_instruction the buggy hunk from the conversion was:
>>>>>            else
>>>>> -           if (TARGET_UNIFIED_ASM)
>>>>>                sprintf (instr, "pop%s\t{", conditional);
>>>>> -           else
>>>>> -             sprintf (instr, "ldm%sfd\t%%|sp!, {", conditional);
>>>>>
>>>>> The code was already very obscurely structured and arguably the bug was
>>>>> latent.
>>>>> It emitted POP only when TARGET_UNIFIED_ASM was on, and since
>>>>> TARGET_UNIFIED_ASM was on
>>>>> only for Thumb, we never went down this path interrupt handling code, since
>>>>> the interrupt
>>>>> attribute is only available for ARM code. After the removal of
>>>>> TARGET_UNIFIED_ASM we ended up
>>>>> using POP unconditionally. So this patch adds a check for IS_INTERRUPT and
>>>>> outputs the
>>>>> appropriate LDM form.
>>>>>
>>>>> In arm_output_multireg_pop the buggy hunk was:
>>>>> -  if ((regno_base == SP_REGNUM) && TARGET_THUMB)
>>>>> +  if ((regno_base == SP_REGNUM) && update)
>>>>>       {
>>>>> -      /* Output pop (not stmfd) because it has a shorter encoding.  */
>>>>> -      gcc_assert (update);
>>>>>         sprintf (pattern, "pop%s\t{", conditional);
>>>>>       }
>>>>>
>>>>> Again, the POP was guarded on TARGET_THUMB and so would never be taken on
>>>>> interrupt handling
>>>>> routines. This patch guards that with the appropriate check on interrupt
>>>>> return.
>>>>>
>>>>> Also, there are a couple of bugs in the 'else' branch of that 'if':
>>>>> * The "ldmfd%s" was output without a '\t' at the end which meant that the
>>>>> base register
>>>>> name would be concatenated with the 'ldmfd', creating invalid assembly.
>>>>>
>>>>> * The logic:
>>>>>
>>>>>        if (regno_base == SP_REGNUM)
>>>>>        /* update is never true here, hence there is no need to handle
>>>>>           pop here.  */
>>>>>      sprintf (pattern, "ldmfd%s", conditional);
>>>>>
>>>>>        if (update)
>>>>>      sprintf (pattern, "ldmia%s\t", conditional);
>>>>>        else
>>>>>      sprintf (pattern, "ldm%s\t", conditional);
>>>>>
>>>>> Meant that for "regno == SP_REGNUM && !update" we'd end up printing
>>>>> "ldmfd%sldm%s\t"
>>>>> to pattern. I didn't manage to reproduce that condition though, so maybe it
>>>>> can't ever occur.
>>>>> This patch fixes both these issues nevertheless.
>>>>>
>>>>> I've added the testcase from the PR to catch the fix in
>>>>> output_return_instruction.
>>>>> The testcase doesn't catch the bugs in arm_output_multireg_pop, but the
>>>>> existing tests
>>>>> gcc.target/arm/interrupt-1.c and gcc.target/arm/interrupt-2.c would have
>>>>> caught them
>>>>> if only they were assemble tests rather than just compile. So this patch
>>>>> makes them
>>>>> assembly tests (and reverts the scan-assembler checks for the correct LDM
>>>>> pattern).
>>>>>
>>>>> Bootstrapped and tested on arm-none-linux-gnueabihf.
>>>>> Ok for trunk and GCC 6?
>>>>>
>>> Hi Kyrill,
>>>
>>> Did you test --with-mode=thumb?
>>> When using arm mode, I see regressions:
>>>
>>>    gcc.target/arm/neon-nested-apcs.c (test for excess errors)
>>>    gcc.target/arm/nested-apcs.c (test for excess errors)
>>
>> It's because I have a local patch in my binutils that makes gas warn on the
>> deprecated sequences that these two tests generate (they use the deprecated -mapcs option),
>> so these tests were already showing the (test for excess errors) FAIL for me,
>> so I they didn't appear in my tests diff for this patch. :(
>>
>> I've reproduced the failure with a clean tree.
>> Where before we generated:
>>     ldm    sp, {fp, sp, pc}
>> now we generate:
>>     pop    {fp, sp, pc}
>>
>> which are not equivalent (pop performs a write-back) and gas warns:
>> Warning: writeback of base register when in register list is UNPREDICTABLE
>>
>> I'm testing a patch to fix this.
>> Sorry for the regression.
>
> Here is the fix.
> I had remove the update from the condition for the "pop" erroneously. Of course, if we're not
> updating the SP we can't use POP that has an implicit writeback.
>
> Bootstrapped on arm-none-linux-gnueabihf. Tested with -mthumb and -marm.
>
> Ok for trunk and GCC 6?
>
> Thanks,
> Kyrill
>
> 2016-05-17  Kyrylo Tkachov  <kyrylo.tkachov@arm.com>
>
>     PR target/70830
>     * config/arm/arm.c (arm_output_multireg_pop): Guard "pop" on update.
>
Kyrill Tkachov June 2, 2016, 8:46 a.m. UTC | #2
Ping.

Thanks,
Kyrill

On 24/05/16 13:48, Kyrill Tkachov wrote:
> Ping.
> https://gcc.gnu.org/ml/gcc-patches/2016-05/msg01211.html
>
> Thanks,
> Kyrill
>
> On 17/05/16 11:40, Kyrill Tkachov wrote:
>>
>> On 13/05/16 12:05, Kyrill Tkachov wrote:
>>> Hi Christophe,
>>>
>>> On 12/05/16 20:57, Christophe Lyon wrote:
>>>> On 12 May 2016 at 11:48, Ramana Radhakrishnan <ramana.gcc@googlemail.com> wrote:
>>>>> On Thu, May 5, 2016 at 12:50 PM, Kyrill Tkachov
>>>>> <kyrylo.tkachov@foss.arm.com> wrote:
>>>>>> Hi all,
>>>>>>
>>>>>> In this PR we deal with some fallout from the conversion to unified
>>>>>> assembly.
>>>>>> We now end up emitting instructions like:
>>>>>>    pop {r0,r1,r2,r3,pc}^
>>>>>> which is not legal. We have to use an LDM form.
>>>>>>
>>>>>> There are bugs in two arm.c functions: output_return_instruction and
>>>>>> arm_output_multireg_pop.
>>>>>>
>>>>>> In output_return_instruction the buggy hunk from the conversion was:
>>>>>>            else
>>>>>> -           if (TARGET_UNIFIED_ASM)
>>>>>>                sprintf (instr, "pop%s\t{", conditional);
>>>>>> -           else
>>>>>> -             sprintf (instr, "ldm%sfd\t%%|sp!, {", conditional);
>>>>>>
>>>>>> The code was already very obscurely structured and arguably the bug was
>>>>>> latent.
>>>>>> It emitted POP only when TARGET_UNIFIED_ASM was on, and since
>>>>>> TARGET_UNIFIED_ASM was on
>>>>>> only for Thumb, we never went down this path interrupt handling code, since
>>>>>> the interrupt
>>>>>> attribute is only available for ARM code. After the removal of
>>>>>> TARGET_UNIFIED_ASM we ended up
>>>>>> using POP unconditionally. So this patch adds a check for IS_INTERRUPT and
>>>>>> outputs the
>>>>>> appropriate LDM form.
>>>>>>
>>>>>> In arm_output_multireg_pop the buggy hunk was:
>>>>>> -  if ((regno_base == SP_REGNUM) && TARGET_THUMB)
>>>>>> +  if ((regno_base == SP_REGNUM) && update)
>>>>>>       {
>>>>>> -      /* Output pop (not stmfd) because it has a shorter encoding.  */
>>>>>> -      gcc_assert (update);
>>>>>>         sprintf (pattern, "pop%s\t{", conditional);
>>>>>>       }
>>>>>>
>>>>>> Again, the POP was guarded on TARGET_THUMB and so would never be taken on
>>>>>> interrupt handling
>>>>>> routines. This patch guards that with the appropriate check on interrupt
>>>>>> return.
>>>>>>
>>>>>> Also, there are a couple of bugs in the 'else' branch of that 'if':
>>>>>> * The "ldmfd%s" was output without a '\t' at the end which meant that the
>>>>>> base register
>>>>>> name would be concatenated with the 'ldmfd', creating invalid assembly.
>>>>>>
>>>>>> * The logic:
>>>>>>
>>>>>>        if (regno_base == SP_REGNUM)
>>>>>>        /* update is never true here, hence there is no need to handle
>>>>>>           pop here.  */
>>>>>>      sprintf (pattern, "ldmfd%s", conditional);
>>>>>>
>>>>>>        if (update)
>>>>>>      sprintf (pattern, "ldmia%s\t", conditional);
>>>>>>        else
>>>>>>      sprintf (pattern, "ldm%s\t", conditional);
>>>>>>
>>>>>> Meant that for "regno == SP_REGNUM && !update" we'd end up printing
>>>>>> "ldmfd%sldm%s\t"
>>>>>> to pattern. I didn't manage to reproduce that condition though, so maybe it
>>>>>> can't ever occur.
>>>>>> This patch fixes both these issues nevertheless.
>>>>>>
>>>>>> I've added the testcase from the PR to catch the fix in
>>>>>> output_return_instruction.
>>>>>> The testcase doesn't catch the bugs in arm_output_multireg_pop, but the
>>>>>> existing tests
>>>>>> gcc.target/arm/interrupt-1.c and gcc.target/arm/interrupt-2.c would have
>>>>>> caught them
>>>>>> if only they were assemble tests rather than just compile. So this patch
>>>>>> makes them
>>>>>> assembly tests (and reverts the scan-assembler checks for the correct LDM
>>>>>> pattern).
>>>>>>
>>>>>> Bootstrapped and tested on arm-none-linux-gnueabihf.
>>>>>> Ok for trunk and GCC 6?
>>>>>>
>>>> Hi Kyrill,
>>>>
>>>> Did you test --with-mode=thumb?
>>>> When using arm mode, I see regressions:
>>>>
>>>>    gcc.target/arm/neon-nested-apcs.c (test for excess errors)
>>>>    gcc.target/arm/nested-apcs.c (test for excess errors)
>>>
>>> It's because I have a local patch in my binutils that makes gas warn on the
>>> deprecated sequences that these two tests generate (they use the deprecated -mapcs option),
>>> so these tests were already showing the (test for excess errors) FAIL for me,
>>> so I they didn't appear in my tests diff for this patch. :(
>>>
>>> I've reproduced the failure with a clean tree.
>>> Where before we generated:
>>>     ldm    sp, {fp, sp, pc}
>>> now we generate:
>>>     pop    {fp, sp, pc}
>>>
>>> which are not equivalent (pop performs a write-back) and gas warns:
>>> Warning: writeback of base register when in register list is UNPREDICTABLE
>>>
>>> I'm testing a patch to fix this.
>>> Sorry for the regression.
>>
>> Here is the fix.
>> I had remove the update from the condition for the "pop" erroneously. Of course, if we're not
>> updating the SP we can't use POP that has an implicit writeback.
>>
>> Bootstrapped on arm-none-linux-gnueabihf. Tested with -mthumb and -marm.
>>
>> Ok for trunk and GCC 6?
>>
>> Thanks,
>> Kyrill
>>
>> 2016-05-17  Kyrylo Tkachov  <kyrylo.tkachov@arm.com>
>>
>>     PR target/70830
>>     * config/arm/arm.c (arm_output_multireg_pop): Guard "pop" on update.
>>
>
Ramana Radhakrishnan June 2, 2016, 8:47 a.m. UTC | #3
On 17/05/16 11:40, Kyrill Tkachov wrote:
> 
> On 13/05/16 12:05, Kyrill Tkachov wrote:
>> Hi Christophe,
>>
>> On 12/05/16 20:57, Christophe Lyon wrote:
>>> On 12 May 2016 at 11:48, Ramana Radhakrishnan <ramana.gcc@googlemail.com> wrote:
>>>> On Thu, May 5, 2016 at 12:50 PM, Kyrill Tkachov
>>>> <kyrylo.tkachov@foss.arm.com> wrote:
>>>>> Hi all,
>>>>>
>>>>> In this PR we deal with some fallout from the conversion to unified
>>>>> assembly.
>>>>> We now end up emitting instructions like:
>>>>>    pop {r0,r1,r2,r3,pc}^
>>>>> which is not legal. We have to use an LDM form.
>>>>>
>>>>> There are bugs in two arm.c functions: output_return_instruction and
>>>>> arm_output_multireg_pop.
>>>>>
>>>>> In output_return_instruction the buggy hunk from the conversion was:
>>>>>            else
>>>>> -           if (TARGET_UNIFIED_ASM)
>>>>>                sprintf (instr, "pop%s\t{", conditional);
>>>>> -           else
>>>>> -             sprintf (instr, "ldm%sfd\t%%|sp!, {", conditional);
>>>>>
>>>>> The code was already very obscurely structured and arguably the bug was
>>>>> latent.
>>>>> It emitted POP only when TARGET_UNIFIED_ASM was on, and since
>>>>> TARGET_UNIFIED_ASM was on
>>>>> only for Thumb, we never went down this path interrupt handling code, since
>>>>> the interrupt
>>>>> attribute is only available for ARM code. After the removal of
>>>>> TARGET_UNIFIED_ASM we ended up
>>>>> using POP unconditionally. So this patch adds a check for IS_INTERRUPT and
>>>>> outputs the
>>>>> appropriate LDM form.
>>>>>
>>>>> In arm_output_multireg_pop the buggy hunk was:
>>>>> -  if ((regno_base == SP_REGNUM) && TARGET_THUMB)
>>>>> +  if ((regno_base == SP_REGNUM) && update)
>>>>>       {
>>>>> -      /* Output pop (not stmfd) because it has a shorter encoding.  */
>>>>> -      gcc_assert (update);
>>>>>         sprintf (pattern, "pop%s\t{", conditional);
>>>>>       }
>>>>>
>>>>> Again, the POP was guarded on TARGET_THUMB and so would never be taken on
>>>>> interrupt handling
>>>>> routines. This patch guards that with the appropriate check on interrupt
>>>>> return.
>>>>>
>>>>> Also, there are a couple of bugs in the 'else' branch of that 'if':
>>>>> * The "ldmfd%s" was output without a '\t' at the end which meant that the
>>>>> base register
>>>>> name would be concatenated with the 'ldmfd', creating invalid assembly.
>>>>>
>>>>> * The logic:
>>>>>
>>>>>        if (regno_base == SP_REGNUM)
>>>>>        /* update is never true here, hence there is no need to handle
>>>>>           pop here.  */
>>>>>      sprintf (pattern, "ldmfd%s", conditional);
>>>>>
>>>>>        if (update)
>>>>>      sprintf (pattern, "ldmia%s\t", conditional);
>>>>>        else
>>>>>      sprintf (pattern, "ldm%s\t", conditional);
>>>>>
>>>>> Meant that for "regno == SP_REGNUM && !update" we'd end up printing
>>>>> "ldmfd%sldm%s\t"
>>>>> to pattern. I didn't manage to reproduce that condition though, so maybe it
>>>>> can't ever occur.
>>>>> This patch fixes both these issues nevertheless.
>>>>>
>>>>> I've added the testcase from the PR to catch the fix in
>>>>> output_return_instruction.
>>>>> The testcase doesn't catch the bugs in arm_output_multireg_pop, but the
>>>>> existing tests
>>>>> gcc.target/arm/interrupt-1.c and gcc.target/arm/interrupt-2.c would have
>>>>> caught them
>>>>> if only they were assemble tests rather than just compile. So this patch
>>>>> makes them
>>>>> assembly tests (and reverts the scan-assembler checks for the correct LDM
>>>>> pattern).
>>>>>
>>>>> Bootstrapped and tested on arm-none-linux-gnueabihf.
>>>>> Ok for trunk and GCC 6?
>>>>>
>>> Hi Kyrill,
>>>
>>> Did you test --with-mode=thumb?
>>> When using arm mode, I see regressions:
>>>
>>>    gcc.target/arm/neon-nested-apcs.c (test for excess errors)
>>>    gcc.target/arm/nested-apcs.c (test for excess errors)
>>
>> It's because I have a local patch in my binutils that makes gas warn on the
>> deprecated sequences that these two tests generate (they use the deprecated -mapcs option),
>> so these tests were already showing the (test for excess errors) FAIL for me,
>> so I they didn't appear in my tests diff for this patch. :(
>>
>> I've reproduced the failure with a clean tree.
>> Where before we generated:
>>     ldm    sp, {fp, sp, pc}
>> now we generate:
>>     pop    {fp, sp, pc}
>>
>> which are not equivalent (pop performs a write-back) and gas warns:
>> Warning: writeback of base register when in register list is UNPREDICTABLE
>>
>> I'm testing a patch to fix this.
>> Sorry for the regression.
> 
> Here is the fix.
> I had remove the update from the condition for the "pop" erroneously. Of course, if we're not
> updating the SP we can't use POP that has an implicit writeback.
> 
> Bootstrapped on arm-none-linux-gnueabihf. Tested with -mthumb and -marm.
> 
> Ok for trunk and GCC 6?
> 
> Thanks,
> Kyrill
> 
> 2016-05-17  Kyrylo Tkachov  <kyrylo.tkachov@arm.com>
> 
>     PR target/70830
>     * config/arm/arm.c (arm_output_multireg_pop): Guard "pop" on update.
> 


Ok.

Thanks,
Ramana
Kyrill Tkachov June 2, 2016, 8:54 a.m. UTC | #4
On 02/06/16 09:47, Ramana Radhakrishnan wrote:
> On 17/05/16 11:40, Kyrill Tkachov wrote:
>> On 13/05/16 12:05, Kyrill Tkachov wrote:
>>> Hi Christophe,
>>>
>>> On 12/05/16 20:57, Christophe Lyon wrote:
>>>> On 12 May 2016 at 11:48, Ramana Radhakrishnan <ramana.gcc@googlemail.com> wrote:
>>>>> On Thu, May 5, 2016 at 12:50 PM, Kyrill Tkachov
>>>>> <kyrylo.tkachov@foss.arm.com> wrote:
>>>>>> Hi all,
>>>>>>
>>>>>> In this PR we deal with some fallout from the conversion to unified
>>>>>> assembly.
>>>>>> We now end up emitting instructions like:
>>>>>>     pop {r0,r1,r2,r3,pc}^
>>>>>> which is not legal. We have to use an LDM form.
>>>>>>
>>>>>> There are bugs in two arm.c functions: output_return_instruction and
>>>>>> arm_output_multireg_pop.
>>>>>>
>>>>>> In output_return_instruction the buggy hunk from the conversion was:
>>>>>>             else
>>>>>> -           if (TARGET_UNIFIED_ASM)
>>>>>>                 sprintf (instr, "pop%s\t{", conditional);
>>>>>> -           else
>>>>>> -             sprintf (instr, "ldm%sfd\t%%|sp!, {", conditional);
>>>>>>
>>>>>> The code was already very obscurely structured and arguably the bug was
>>>>>> latent.
>>>>>> It emitted POP only when TARGET_UNIFIED_ASM was on, and since
>>>>>> TARGET_UNIFIED_ASM was on
>>>>>> only for Thumb, we never went down this path interrupt handling code, since
>>>>>> the interrupt
>>>>>> attribute is only available for ARM code. After the removal of
>>>>>> TARGET_UNIFIED_ASM we ended up
>>>>>> using POP unconditionally. So this patch adds a check for IS_INTERRUPT and
>>>>>> outputs the
>>>>>> appropriate LDM form.
>>>>>>
>>>>>> In arm_output_multireg_pop the buggy hunk was:
>>>>>> -  if ((regno_base == SP_REGNUM) && TARGET_THUMB)
>>>>>> +  if ((regno_base == SP_REGNUM) && update)
>>>>>>        {
>>>>>> -      /* Output pop (not stmfd) because it has a shorter encoding.  */
>>>>>> -      gcc_assert (update);
>>>>>>          sprintf (pattern, "pop%s\t{", conditional);
>>>>>>        }
>>>>>>
>>>>>> Again, the POP was guarded on TARGET_THUMB and so would never be taken on
>>>>>> interrupt handling
>>>>>> routines. This patch guards that with the appropriate check on interrupt
>>>>>> return.
>>>>>>
>>>>>> Also, there are a couple of bugs in the 'else' branch of that 'if':
>>>>>> * The "ldmfd%s" was output without a '\t' at the end which meant that the
>>>>>> base register
>>>>>> name would be concatenated with the 'ldmfd', creating invalid assembly.
>>>>>>
>>>>>> * The logic:
>>>>>>
>>>>>>         if (regno_base == SP_REGNUM)
>>>>>>         /* update is never true here, hence there is no need to handle
>>>>>>            pop here.  */
>>>>>>       sprintf (pattern, "ldmfd%s", conditional);
>>>>>>
>>>>>>         if (update)
>>>>>>       sprintf (pattern, "ldmia%s\t", conditional);
>>>>>>         else
>>>>>>       sprintf (pattern, "ldm%s\t", conditional);
>>>>>>
>>>>>> Meant that for "regno == SP_REGNUM && !update" we'd end up printing
>>>>>> "ldmfd%sldm%s\t"
>>>>>> to pattern. I didn't manage to reproduce that condition though, so maybe it
>>>>>> can't ever occur.
>>>>>> This patch fixes both these issues nevertheless.
>>>>>>
>>>>>> I've added the testcase from the PR to catch the fix in
>>>>>> output_return_instruction.
>>>>>> The testcase doesn't catch the bugs in arm_output_multireg_pop, but the
>>>>>> existing tests
>>>>>> gcc.target/arm/interrupt-1.c and gcc.target/arm/interrupt-2.c would have
>>>>>> caught them
>>>>>> if only they were assemble tests rather than just compile. So this patch
>>>>>> makes them
>>>>>> assembly tests (and reverts the scan-assembler checks for the correct LDM
>>>>>> pattern).
>>>>>>
>>>>>> Bootstrapped and tested on arm-none-linux-gnueabihf.
>>>>>> Ok for trunk and GCC 6?
>>>>>>
>>>> Hi Kyrill,
>>>>
>>>> Did you test --with-mode=thumb?
>>>> When using arm mode, I see regressions:
>>>>
>>>>     gcc.target/arm/neon-nested-apcs.c (test for excess errors)
>>>>     gcc.target/arm/nested-apcs.c (test for excess errors)
>>> It's because I have a local patch in my binutils that makes gas warn on the
>>> deprecated sequences that these two tests generate (they use the deprecated -mapcs option),
>>> so these tests were already showing the (test for excess errors) FAIL for me,
>>> so I they didn't appear in my tests diff for this patch. :(
>>>
>>> I've reproduced the failure with a clean tree.
>>> Where before we generated:
>>>      ldm    sp, {fp, sp, pc}
>>> now we generate:
>>>      pop    {fp, sp, pc}
>>>
>>> which are not equivalent (pop performs a write-back) and gas warns:
>>> Warning: writeback of base register when in register list is UNPREDICTABLE
>>>
>>> I'm testing a patch to fix this.
>>> Sorry for the regression.
>> Here is the fix.
>> I had remove the update from the condition for the "pop" erroneously. Of course, if we're not
>> updating the SP we can't use POP that has an implicit writeback.
>>
>> Bootstrapped on arm-none-linux-gnueabihf. Tested with -mthumb and -marm.
>>
>> Ok for trunk and GCC 6?
>>
>> Thanks,
>> Kyrill
>>
>> 2016-05-17  Kyrylo Tkachov  <kyrylo.tkachov@arm.com>
>>
>>      PR target/70830
>>      * config/arm/arm.c (arm_output_multireg_pop): Guard "pop" on update.
>>
>
> Ok.

Thanks, I've committed it with r237027.
I'll backport to GCC 6 in a few days if there's no fallout.

Sorry again for the trouble,
Kyrill


> Thanks,
> Ramana
>
diff mbox

Patch

diff --git a/gcc/config/arm/arm.c b/gcc/config/arm/arm.c
index 2cc7f7b452a62f898346a51ca7ede0d19bcfcfad..68985547a634bb93ab59416e24aaa046ab99f6a6 100644
--- a/gcc/config/arm/arm.c
+++ b/gcc/config/arm/arm.c
@@ -17624,10 +17624,8 @@  arm_output_multireg_pop (rtx *operands, bool return_pc, rtx cond, bool reverse,
 
   conditional = reverse ? "%?%D0" : "%?%d0";
   /* Can't use POP if returning from an interrupt.  */
-  if ((regno_base == SP_REGNUM) && !(interrupt_p && return_pc))
-    {
-      sprintf (pattern, "pop%s\t{", conditional);
-    }
+  if ((regno_base == SP_REGNUM) && update && !(interrupt_p && return_pc))
+    sprintf (pattern, "pop%s\t{", conditional);
   else
     {
       /* Output ldmfd when the base register is SP, otherwise output ldmia.