LOSnel
LOSnel

Reputation: 171

GCC optimization issues and behavior

I have an optimisation issue with gcc running on Eclipse. I have a program which runs well with -O0 optimization level, but I need to turn ON other levels of optimization because my software uses too much flash memory space. But when I use -O1 optimization level (for instance) my code doesn't work anymore and I need to do strange modifications as described below with the UART peripheral managing functions.

Code running well with -O0

void function(...)
{
   // Send char one by one on UART peripheral
   UART_put_char(...)

   // Wait end of transmission on UART
   while(end_of_transmission_flag == flase);
}

void UART_TxCompleteCallback()
{
   // Change flag state
   end_of_transmission_flag = true;
}

Code with modification for -O1

void function(...)
{
   // Send char one by one on UART peripheral
   UART_put_char(...)

   // Wait end of transmission on UART
   while(end_of_transmission_flag == flase)
   {
      Dummy = Dummy == 0 ? 1 : 0;
   }
}


void UART_TxCompleteCallback()
{
   // Change flag state
   end_of_transmission_flag = true;
}

I would like to know why gcc is forcing me to do this kind of modifications, do I need to change my "way of think" about the design of my code. Or is there an explanation with this behavior ?

Thanks for your advices.

Upvotes: 1

Views: 1702

Answers (2)

With optimization turned on, the compiler tries to save as much as it can on redundant code. This line:

while(end_of_transmission_flag == false);

continuously checks a variable which is never modified (from the point of view of the compiler). Moreover, the line does nothing (again from the point of view of the compiler). In reality the line does something: it waits until the flag changes state. But the compiler is not smart enough to understand this. So you have to help it by declaring the flag variable as volatile. Doing this, you warn the compiler that the variable can change state behind the scene (i.e., in interrupt). The compiler now knows that it can not rely on the fact that the variable seems to be never modified - it can not "cache" its value to produce faster code.

Please note that, even so, the whole line still does nothing (there is no code to execute inside the loop); but normally, in this situation, the compiler understands that the loop must be executed anyway.

Your trick "Dummy = Dummy == 0 ? 1 : 0;" seems to have some effect on the compiler, and I can not explain it to myself. But, as you already thought, it seems really strange. The trick addresses the problem of a statement which does nothing, forcing the compiler to generate code inside the loop. But this is only half of the problem and, anyway, a very smart compiler could think "why should I compile code to assign a value to a variable which is never used? When such things happen, the compiler often warns you with something like "Warning: value assigned to xxxxx is never used".

To conclude, declare end_of_transmission_flag as volatile.

Just a thought aside: many programming languages are not born to cope well with hardware (port I/O, interrupts, DMA and so on), and C is no exception: it forces the programmer to strange declarations, pragmas, compilation flags and so on. On the other hand, a line like "while(end_of_transmission_flag == false);" is kind of ambiguous; the real line should read something like "wait_until_true(end_of_transmission_flag);". Things like this could be offered by the language, or by the environment (via macros, libraries, hardware support or whatever).

Upvotes: 0

OlivierM
OlivierM

Reputation: 3222

As said in the comments, you should declare end_of_transmission_flag as volatile to make sure the compiler does not remove the loop during its optimization. By declaring the variable volatile, the compiler knows the variable will be updated somewhere else.

But to debug your issue you should not change all the files with '-O1' in once, but you should try to move some file in -O1 and keep some file in -O0.

You can also disable GCC optimization locally:

#pragma GCC push_options
#pragma GCC optimize ("O0")

// your code

#pragma GCC pop_options

Be aware in embedded you must respect the order of the operation when you access hardware register. Compilation optimization sometimes/often change the order. Using volatile with the address of the registers ensures the order of the operation.

Upvotes: 3

Related Questions