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 22nd September 2009 at http://forums.arm.com

    Now I think you've clarified that it will NOT necessarily stack r3?


    Correct. In this case foo() knows that only r0 and r1 contain valid values (it's parameters), and that it is free to clobber (or let subroutines clobber) r2, r3 and r12. So it can directly pass through to bar() without doing any stack operations.

    What's the theory behind doing that?


    The theory is really just implementation simplicity - if you have to spill registers to the stack you have to be able to use SP relative addressing to retrieve the value when you need it later in the function. If SP moves, tracking all possible offsets is a pretty complex task, and in reality doesn't buy you very much performance - so keep it simple stupid wins =).

    I think it also has some impact on exception handling in C++, but that's outside of my area of knowledge...

    If, on the other hand, the idea of this is to treat the stack as some sort of extension of the register space, so that each extended word corresponds to a fixed "stack" position, it isn't a stack anymore - at which point why not just read and write from heap or any other free memory area?


    Using stack means that your "register file" is effectively infinite, as you can extend it on demand for any function in the call stack. If you had a fixed number of "extended virtual registers" at a fixed location in memory you get the same problem you have with physical registers - they are shared across function calls, and if foo is using virtual register 64, then bar has to put is somewhere if it wants to use it - so it would still have to get pushed to the stack...
Reply
  • Note: This was originally posted on 22nd September 2009 at http://forums.arm.com

    Now I think you've clarified that it will NOT necessarily stack r3?


    Correct. In this case foo() knows that only r0 and r1 contain valid values (it's parameters), and that it is free to clobber (or let subroutines clobber) r2, r3 and r12. So it can directly pass through to bar() without doing any stack operations.

    What's the theory behind doing that?


    The theory is really just implementation simplicity - if you have to spill registers to the stack you have to be able to use SP relative addressing to retrieve the value when you need it later in the function. If SP moves, tracking all possible offsets is a pretty complex task, and in reality doesn't buy you very much performance - so keep it simple stupid wins =).

    I think it also has some impact on exception handling in C++, but that's outside of my area of knowledge...

    If, on the other hand, the idea of this is to treat the stack as some sort of extension of the register space, so that each extended word corresponds to a fixed "stack" position, it isn't a stack anymore - at which point why not just read and write from heap or any other free memory area?


    Using stack means that your "register file" is effectively infinite, as you can extend it on demand for any function in the call stack. If you had a fixed number of "extended virtual registers" at a fixed location in memory you get the same problem you have with physical registers - they are shared across function calls, and if foo is using virtual register 64, then bar has to put is somewhere if it wants to use it - so it would still have to get pushed to the stack...
Children
No data