Problem with optimisation

Asked by Jonathan Dumaresq

Hi,

We have a problem with a simple code. This code is reduced until the bug still exist.

here the problem code.

uint16_t methodShouldReturn1050(void)
{
 uint8_t Index = 0;
 uint16_t number = 0xFFFF;
 uint8_t found = 0;

 uint16_t numberPool[3] = {300, 315, 330};

  while((numberPool[Index] != 0xFFFF) && (Index < 3) && (!found))
  {

   if(690 < numberPool[Index])
   {
    found = 1;
   }
   Index++;
  }

  if(!found)
  {
   number = 1050;
  }

 return number;
}

This code look strange but this code must return the value 1050. BUT starting using version 4.8 of GCC, this code doesn't return 1050. It return garbage. some time if returning 65535, and other time 65311 etc...

Here is my main

int main(void)
{
 printf("\x1b[2J\n");
 printf("test bug\n");
 printf("value = %d\n", methodShouldReturn1050()); //this doesn't print 1050 in optimised version.

 while(1);
}

we have modified ou syscall to send the value on USART3.

If we don't use optimisation -O0, the return value is 1050.

Here my compiling line

arm-none-eabi-g++ -mcpu=cortex-m4 -mthumb -mfloat-abi=hard -mfpu=fpv4-sp-d16 -O0 -fmessage-length=0 -fsigned-char -ffunction-sections -fdata-sections -Wall -g3 -DSTM32F4XX -DUSE_STDPERIPH_DRIVER -DSTM32F427X -std=gnu++11 -fno-exceptions -fno-rtti -fno-use-cxa-atexit -fno-threadsafe-statics -MMD -MP -MF"SRC/main.d" -MT"SRC/main.o" -c -o "SRC/main.o" "../SRC/main.cpp"

here my link line

arm-none-eabi-g++ -mcpu=cortex-m4 -mthumb -mfloat-abi=hard -mfpu=fpv4-sp-d16 -O0 -fmessage-length=0 -fsigned-char -ffunction-sections -fdata-sections -Wall -g3 -T "stm32.ld" -Xlinker --gc-sections -Wl,-Map,"testBugsArm.map" --specs=nosys.specs -o "testBugsArm.elf" ./SRC/main.o

Here the result from the asm listing file

 8011076: b003 add sp, #12
 8011078: bdf0 pop {r4, r5, r6, r7, pc}
 801107a: bf00 nop
 801107c: 40020c00 .word 0x40020c00
 8011080: 40004800 .word 0x40004800

08011084 <methodShouldReturn1050()>:
_Z22methodShouldReturn1050v():
C:\Projects/../SRC/main.cpp:36

uint16_t methodShouldReturn1050(void)
{
 8011084: b410 push {r4}
C:\Projects/../SRC/main.cpp:41
 uint8_t Index = 0;
 uint16_t number = 0xFFFF;
 uint8_t found = 0;

 uint16_t numberPool[3] = {300, 315, 330};
 8011086: 4a18 ldr r2, [pc, #96] ; (80110e8 <methodShouldReturn1050()+0x64>)
C:\Projects/../SRC/main.cpp:36
  /* Enable USART */
  USART_Cmd(USART3, ENABLE);
}

uint16_t methodShouldReturn1050(void)
{
 8011088: b083 sub sp, #12
C:\Projects/../SRC/main.cpp:41
 uint8_t Index = 0;
 uint16_t number = 0xFFFF;
 uint8_t found = 0;

 uint16_t numberPool[3] = {300, 315, 330};
 801108a: 6810 ldr r0, [r2, #0]
 801108c: 9000 str r0, [sp, #0]
C:\Projects/../SRC/main.cpp:44

  while((numberPool[Index] != 0xFFFF) && (Index < 3) && (!found))
 801108e: f8bd 3000 ldrh.w r3, [sp]
C:\Projects/../SRC/main.cpp:41
{
 uint8_t Index = 0;
 uint16_t number = 0xFFFF;
 uint8_t found = 0;

 uint16_t numberPool[3] = {300, 315, 330};
 8011092: 8892 ldrh r2, [r2, #4]
 8011094: f8ad 2004 strh.w r2, [sp, #4]
C:\Projects/../SRC/main.cpp:44

  while((numberPool[Index] != 0xFFFF) && (Index < 3) && (!found))
 8011098: f64f 70ff movw r0, #65535 ; 0xffff
 801109c: 4283 cmp r3, r0
 801109e: d01f beq.n 80110e0 <methodShouldReturn1050()+0x5c>
C:\Projects/../SRC/main.cpp:47
  {

   if(690 < numberPool[Index])
 80110a0: f240 22b2 movw r2, #690 ; 0x2b2
C:\Projects/../SRC/main.cpp:44
 uint8_t found = 0;

 uint16_t numberPool[3] = {300, 315, 330};

  while((numberPool[Index] != 0xFFFF) && (Index < 3) && (!found))
 80110a4: f8bd 1002 ldrh.w r1, [sp, #2]
C:\Projects/../SRC/main.cpp:47
  {

   if(690 < numberPool[Index])
 80110a8: 4293 cmp r3, r2
 80110aa: bf94 ite ls
 80110ac: 2300 movls r3, #0
 80110ae: 2301 movhi r3, #1
C:\Projects/../SRC/main.cpp:44
 uint8_t found = 0;

 uint16_t numberPool[3] = {300, 315, 330};

  while((numberPool[Index] != 0xFFFF) && (Index < 3) && (!found))
 80110b0: 4281 cmp r1, r0
C:\Projects/../SRC/main.cpp:49
  {

   if(690 < numberPool[Index])
   {
    found = 1;
 80110b2: 461c mov r4, r3
C:\Projects/../SRC/main.cpp:44
 uint8_t found = 0;

 uint16_t numberPool[3] = {300, 315, 330};

  while((numberPool[Index] != 0xFFFF) && (Index < 3) && (!found))
 80110b4: d00c beq.n 80110d0 <methodShouldReturn1050()+0x4c>
 80110b6: b93b cbnz r3, 80110c8 <methodShouldReturn1050()+0x44>
 80110b8: f8bd 3004 ldrh.w r3, [sp, #4]
C:\Projects/../SRC/main.cpp:49
  {

   if(690 < numberPool[Index])
   {
    found = 1;
 80110bc: 4291 cmp r1, r2
 80110be: bf94 ite ls
 80110c0: 2400 movls r4, #0
 80110c2: 2401 movhi r4, #1
C:\Projects/../SRC/main.cpp:44
 uint8_t found = 0;

 uint16_t numberPool[3] = {300, 315, 330};

  while((numberPool[Index] != 0xFFFF) && (Index < 3) && (!found))
 80110c4: 4283 cmp r3, r0
 80110c6: d003 beq.n 80110d0 <methodShouldReturn1050()+0x4c>
C:\Projects/../SRC/main.cpp:60
  {
   number = 1050;
  }

 return number;
}
 80110c8: b003 add sp, #12
 80110ca: f85d 4b04 ldr.w r4, [sp], #4
 80110ce: 4770 bx lr
C:\Projects/../SRC/main.cpp:56
   Index++;
  }

  if(!found)
  {
   number = 1050;
 80110d0: f64f 73ff movw r3, #65535 ; 0xffff
 80110d4: f240 401a movw r0, #1050 ; 0x41a
 80110d8: 2c00 cmp r4, #0
 80110da: bf18 it ne
 80110dc: 4618 movne r0, r3
 80110de: e7f3 b.n 80110c8 <methodShouldReturn1050()+0x44>
 80110e0: f240 401a movw r0, #1050 ; 0x41a
 80110e4: e7f0 b.n 80110c8 <methodShouldReturn1050()+0x44>
 80110e6: bf00 nop
 80110e8: 08015b98 .word 0x08015b98

Is there really a problem with the optimiser or I missing something here ?

Regards

Jonathan

Question information

Language:
English Edit question
Status:
Solved
For:
GNU Arm Embedded Toolchain Edit question
Assignee:
No assignee Edit question
Solved by:
Jonathan Dumaresq
Solved:
Last query:
Last reply:
Revision history for this message
Jonathan Dumaresq (jdumaresq) said :
#1

Only changing the -O0 to -O3, to get the problem.

Regards

Jonathan

Revision history for this message
Marc Singer (eleventen) said :
#2

I copied the function to a file and added an Emacs local variable for the compile line. I'm not seeing a problem with the code in this case. From the assembly output, it seems clear that the result will either be 0xffff or 1050.

Could the problem be in the lines that print the output to the UART instead?

#include <stdint.h>

uint16_t methodShouldReturn1050(void)
{
 uint8_t Index = 0;
 uint16_t number = 0xFFFF;
 uint8_t found = 0;

 uint16_t numberPool[3] = {300, 315, 330};

  while((numberPool[Index] != 0xFFFF) && (Index < 3) && (!found)) {

   if(690 < numberPool[Index]) {
     found = 1; }
   Index++;
  }

  if(!found) {
    number = 1050; }

  return number;
}

/*
   Local Variables:
   compile-command: "/opt/arm/arm-none-eabi-g++ -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=fpv4-sp-d16 -O0 -fmessage-length=0 -fsigned-char -ffunction-sections -fdata-sections -std=gnu++11 -g3 -c -mthumb -fno-exceptions -fno-rtti -fno-use-cxa-atexit -fno-threadsafe-statics -o opterr.o -O3 -Wa,-ahndl=opterr.lst opterr.c"
   End:

*/

Revision history for this message
Jonathan Dumaresq (jdumaresq) said :
#3

Hi,

I use the printf from the newLib. I just output the value on the usart.

Jonathan

Revision history for this message
Jonathan Dumaresq (jdumaresq) said :
#4

Hi,

just to be sure, I change the output detection to

 if(methodShouldReturn1050() == 1050)
 {
  printf("value = 1050\n");
 }
 else
 {
  printf("value != 1050\n");
 }

I got value != 1050

Jonathan

Revision history for this message
Thomas Preud'homme (thomas-preudhomme) said :
#5

Hi Jonathan,

I believe the reason is that you test first the value of the array before checking if you are out of range. This is undefined behavior and GCC thus think the return value could rightly be anything. If you switch the first 2 conditions the code generated seems correct.

Note that next stable GCC release (GCC 5) has been improved to throw a warning in such case (see entry for Wno-aggressive-loop-optimizations in [1]).

[1] https://gcc.gnu.org/onlinedocs/gcc/Warning-Options.html#Warning-Options

Best regards.

Revision history for this message
Jonathan Dumaresq (jdumaresq) said :
#6

Hi Thomas,

I have seen this problem with gcc 4.8 too. The version 4.7.4 looks ok.

What do you recomment ? I have added this option but no warning appears. Is only available with gcc 5.0 ?

Is it possible to disable this feature of the optimisation ?

Regards

Jonathan

Revision history for this message
Thomas Preud'homme (thomas-preudhomme) said :
#7

Hi Jonathan,

The option I gave is only to enable/disable warning for such optimization. It is normal for you not to see a difference since no warning is output for GCC 4.9. However the optimization already exist for GCC 4.9 and you can disable it with -fno-aggressive-loop-optimizations. Does your problem go away when using this option?

Note that for portability it would be safer to rewrite the code to avoid undefined behavior but I understand that it might not be practical or even possible.

Best regards.

Revision history for this message
Jonathan Dumaresq (jdumaresq) said :
#8

Hi Thomas,

Yes the problem go away with this option.

This is a very simplistic example from what we have in our big project. The numberPool is changing to something that is giving true to this statement (numberPool[Index] != 0xFFFF).

I wonder why i don't observe the problem on msys2 with gcc 4.8.1.

Jonathan