This discussion has been locked.
You can no longer post new replies to this discussion. If you have a question you can start a new discussion

ARM Compiler creates a bug in code - how to fix?

Note: This was originally posted on 17th September 2009 at http://forums.arm.com

I wouldn't have thought ARMCC could create an explicit bug, but it seems here it is.

In this case, what it's doing is placing an item on the stack in a position which will certainly corrupt the stack.

Looking through the disassembly for the offending function, I can see that the first line (as expected) is

PUSH     {r2-r4,r6-r11,lr}

But then later in the code, and with NO other stack operations in the meanwhile (nor any function calls or other branches out of the current function context) it has

ADD      r2,r0,r4,LSL #2
LDR      r0,|L1.3728|
STR      r2,[sp,#4]

As I understand it, since the stack is full-descending, this means that the value of r2 currently in the stack will get corrupted, because the STR instruction will store the (changed) current r2 value in the position occupied by the original r2. The function in concern is declared

void receivePacket(void)

so it's not expecting to return values in r2 or take arguments in r2.

Why would armcc do something so glaringly illegal? Is there something I can do to fix this?

As an aside, I'll mention that this bad stacking is actually occuring in the setup for a subroutine call, that happens a few lines lower down. The routine in concern uses r1, r2 and r3 for its arguments and returns in r0 and r1. As it happens, the value that it's stacking from r2 is actually the address that will take the return value in r0 - really, what the compiler *should* do, I think, is place the address in some register rx (x > 3) then do an STR r0, [rx, #0] on return.
Parents
  • Note: This was originally posted on 18th September 2009 at http://forums.arm.com

    This would seem to me at least reasonably sensible - i.e. no need to shuffle registers around if you're going to pass them right into a subroutine anyway. That, however, is clearly not the sense in which ARM actually uses these registers and I wish they would use more explicit language to describe exactly what is, and is not, allowed.


    Yep, that's entirely sensible, and precisely what APCS lets you do.

    What is means by "only in between subroutine calls" is that as soon as you call a subroutine that subroutine is free to clobber r0-r3 and r12 (in accordance with the APCS). So if the caller has stored any intermediate value in any of these clobberable registers, and it wants to use the value after the subroutine it either has to move the value in to r4-r11 or r13-14 or write it to the stack.  So you cannot store intermediates "across subroutine calls" in the parameter passing or IP register ... otherwise you risk loosing the value.

    But if that's the case, why is the compiler stacking registers r2 and r3 in the first place? If these can be corrupted arbitrarily then it is a complete waste of time to stack them under essentially any situation.


    Most modern compilers operate on a "move the stack pointer once per function" (ignoring stack push and pop wrapping subroutines), so will essentially reserve enough space on the stack to ensure that any intermediate space on the stack for storing scratch values is allocated on function entry and freed on function exit. In your case, as you pointed out in your first post, the compiler stores r2 back to the stack as a scratch variable; the initial "push" of r2 and r3 is just to reserve the space without needing a second instruction to increment SP. 

    The APCS also requires the stack to be 8 byte aligned on function call, so r3 is probably pushed just to ensure an even number of registers are pushed, although your function may use that space for scratch stack too...
Reply
  • Note: This was originally posted on 18th September 2009 at http://forums.arm.com

    This would seem to me at least reasonably sensible - i.e. no need to shuffle registers around if you're going to pass them right into a subroutine anyway. That, however, is clearly not the sense in which ARM actually uses these registers and I wish they would use more explicit language to describe exactly what is, and is not, allowed.


    Yep, that's entirely sensible, and precisely what APCS lets you do.

    What is means by "only in between subroutine calls" is that as soon as you call a subroutine that subroutine is free to clobber r0-r3 and r12 (in accordance with the APCS). So if the caller has stored any intermediate value in any of these clobberable registers, and it wants to use the value after the subroutine it either has to move the value in to r4-r11 or r13-14 or write it to the stack.  So you cannot store intermediates "across subroutine calls" in the parameter passing or IP register ... otherwise you risk loosing the value.

    But if that's the case, why is the compiler stacking registers r2 and r3 in the first place? If these can be corrupted arbitrarily then it is a complete waste of time to stack them under essentially any situation.


    Most modern compilers operate on a "move the stack pointer once per function" (ignoring stack push and pop wrapping subroutines), so will essentially reserve enough space on the stack to ensure that any intermediate space on the stack for storing scratch values is allocated on function entry and freed on function exit. In your case, as you pointed out in your first post, the compiler stores r2 back to the stack as a scratch variable; the initial "push" of r2 and r3 is just to reserve the space without needing a second instruction to increment SP. 

    The APCS also requires the stack to be 8 byte aligned on function call, so r3 is probably pushed just to ensure an even number of registers are pushed, although your function may use that space for scratch stack too...
Children
No data