2

I have an application which I compile using GCC v6.3, when I compile it with GCC v10.3 I find some functions use more stack size than what GCC v6.3 uses, for example this function uses zero stack for GCC v6.3 but uses 8 bytes for GCC v10.3

int func(int *x, int y, int z, int a)
{
   switch (y) {
       case 1:
           *x = a;
           y = z;
           return y;
   }
   return 77;               // UPDATE
}

GCC v6.3 assembly:

   int func(int *x, int y, int z, int a)
{
   switch (y) {
   0:   2901            cmp     r1, #1
   2:   d102            bne.n   a <func+0xa>
       case 1:
           *x = a;
   4:   6003            str     r3, [r0, #0]
           y = z;
           return y;
   6:   0010            movs    r0, r2
   }

   return 77;
}
   8:   4770            bx      lr
   return 77;
   a:   204d            movs    r0, #77 ; 0x4d
   c:   e7fc            b.n     8 <func+0x8>

and GCC v10.3 assembly:

int func(int *x, int y, int z, int a)
{
   0:   b510            push    {r4, lr}
   2:   0004            movs    r4, r0
           *x = a;
           y = z;
           return y;
   }

   return 77;
   4:   204d            movs    r0, #77 ; 0x4d
   switch (y) {
   6:   2901            cmp     r1, #1
   8:   d101            bne.n   e <func+0xe>
           return y;
   a:   0010            movs    r0, r2
           *x = a;
   c:   6023            str     r3, [r4, #0]
}
   e:   bd10            pop     {r4, pc}

In GCCv10.3, it push {r4, lr} which is not the case in GCC v6.3 so why does this happen?, it costs the application more stack area over the old compiler, so how to avoid it to get less stack size? also, why did it stack the lr although it's a leaf function? plus, why does it return z in all cases although the c code returns it inside the case condition? (UPDATE: This has been resolved by adding a return to the end of the function)

Notes:

  1. This function is a dummy one just to reproduce the issue, so don't consider rewriting it.
  2. Building flags are: arm-none-eabi-gcc -O0 -c -std=c99 -fmessage-length=0 -fomit-frame-pointer -Wno-aggressive-loop-optimizations -Werror -Werror=strict-prototypes -pedantic-errors -Wconversion -pedantic -Wall -Wextra -Wno-unused-function -Wextra -Wpointer-arith -Wsign-compare -Wswitch -Wno-maybe-uninitialized -fno-strict-aliasing -fshort-wchar -mfix-cortex-m3-ldrd -gdwarf-3 -gstrict-dwarf -mabi=aapcs -mthumb -mcpu=Cortex-M0 -g3 -Os -mthumb -ffunction-sections -fdata-sections -MMD -MP -MF"xyz.d" -MT"xyz.o"
  • 6
    You're using `-O0`? So, who cares? Retry with `-O3`? – Craig Estey Dec 15 '21 at 17:17
  • we use -Os, we favor size over performance, so we can't use -O3 – Mohamed Yacout Dec 15 '21 at 17:26
  • 1
    Oh you have **both** `-O0` and `-Os` in the arguments ... make sure the latter applies. – Jester Dec 15 '21 at 17:29
  • Checking on godbolt, looks like gcc started generating weird code from version 9. – Jester Dec 15 '21 at 17:34
  • Exactly that we noticed as well! This weird behavior started with version 9, earlier versions works fine. – Mohamed Yacout Dec 15 '21 at 17:38
  • Consider filing a bug report against gcc. – fuz Dec 15 '21 at 17:45
  • 1
    _"why does it return z in all cases although the c code returns it inside the case condition"_ Because by not returning _anything_ in the other cases, you've introduced undefined behavior? – Michael Dec 15 '21 at 17:49
  • ah, seems correct, returning a value at the function end eliminates this case, I thought the compiler should have give a warning rather than generating a weird code just to avoid the undefined behavior! Any way this should answer one of the questions but the stack usage issue still exist though – Mohamed Yacout Dec 15 '21 at 18:06
  • You really don't get a warning about the failure to return a value? I do, and thanks to `-Werror` it's even fatal. `error: control reaches end of non-void function` – Nate Eldredge Dec 15 '21 at 19:45
  • 3
    @Michael: It's actually only UB if the caller uses the value (C17 6.9.1 p12). If the caller always ignores the return value when it passes `y=1`, there's no UB. So the compiler still has to generate sensible code for this function; if `y != 1` it needs to just return without doing anything (which it does). In that case, the value left in `r0` can be arbitrary since it must not be used. – Nate Eldredge Dec 15 '21 at 19:57
  • 1
    I updated the code to return a value at the end of the function to avoid this issue. – Mohamed Yacout Dec 15 '21 at 20:21
  • @Michael: In C++ it's UB to fall off the end of a non-`void` function. In C, it's only UB if the caller uses it, for historical reasons. (Early C didn't have `void`.) GCC should still warn about that even in C mode. – Peter Cordes Dec 16 '21 at 02:53
  • 1
    As fuz said, the right place for a report like this is GCC's bug tracker, with the `missed-optimization` keyword. Unless there's some way to hand-hold GCC into not sucking for this by tweaking the source, or a different `-mtune=` option you could try, other than the one implied by `-mcpu=cortex-m0`... And BTW, the last `-O` options specified wins, so `-O0 -Os` is the same as `-Os`. It's confusing to include `-O0` as an early option in your SO question, though; best to take it out. – Peter Cordes Dec 16 '21 at 02:56

0 Answers0