> David Brown wrote:
>
> >This is a mistake many people make when they are new to embedded
> >development, or new to using an optomising C compiler for embedded
> >development.  The code snippet "int x = 1; while (x) ; " tells the
>compiler
> >to enter an infinite loop.  You've already told the compiler what value
>"x"
> >has - why should it bother re-reading it all the time?  That's what
> >optomisation is for - being smart and saving the processor some effort.
> >What you need to do is declare your variable to be "volatile" - that
>tells
> >the compiler the you really do want it to read the memory each loop.
>
> I know this is in the documentation, and there is a workaround, but I
> for one would be glad to see the back of this. I've used over a dozen C
> compilers for embedded work over the years, and this is the only one
> that does this optimisation, after all, short informal delays are the
> bread and ripping of embedded work.
>

I think you are dangerously wrong there - so much so that I'm giving a much
longer argument than might be necessary, for the benifit of others who think
the same as you.

First, the fact is that a good number of C compilers will do this same
optomisation, although some only do so at a higher level of optomisation.
For example, IAR's msp430 compiler will do exactly the same, at a high
enough optomisation level.  Any compiler that does not do this is not a good
optomising compiler.  msp430-gcc may be unusual in that it always does it,
even with no optomisation turned on, but that is fair enough.  Choosing a
low optomisation setting means you want the compiler to be less efficient
for the purposes of smaller code size, easier debugging, or faster
compilation - it says nothing about how you want the code to work.

Any case in which the optomisation carried out by the compiler affects the
logic or results of the program is an incorrect program (or a bug in the
compiler, of course :-).  This cannot be stressed too much - if your program
works with low optomisations, and fails with higher optomisations, then you
have a flaw in your program.  If it runs fine when compiled with one
compiler and fails with another compiler (baring compiler-specific
extensions), then you have a flaw in your program.

When you write the code for your short, informal delay loop such as:

    void pause(void) {
        int x = 100;
        while (x--) ;
    }

what you are telling the compiler here is "x is an integer variable.
Nothing else can possibly influence this variable in any way, or even know
of its existance, since it is a local variable.  Count down x until it
reaches 0."  The compiler thinks "I can do this in my head - x ends up at 0.
In fact, you are not doing anything with x later on anyway - there is no
need to bother with it at all".  Implementing "pause" as an empty loop is a
perfectly correct interpretation of the C code as written.  In fact, running
the loop 10 times, or 1000 times, then exiting is also a correct
interpretation.  You have written a function that has no effect.  You might
think you have written a function that wastes a bit of time, but the C
language has no concept of time - "wasting time" is meaningless.  So given
the choice, a good C compiler will skip the time wasting code.

Note that it will not help much making "x" a global variable.  This will
tell the compiler that at the start of the function, it must read in x, and
at the end of the function, it must write out x again - while inside the
function, however, the compiler can read or write x as often or as seldom as
it likes.  In this case, the function "pause" would reduce simply to "x =
0".

There is just one technique that can be correctly used to get the desired
effect, and that is to make use of "volatile" (you might think that calling
an external function within the loop would work, but that might be
eliminated by a compiler that uses link-time code generation).  The word
"volatile" tells the compiler that the given element must be accessed
exactly as written in the code, neither more nor less.  Thus the easiest
correct solution is to make "int x" into "volatile int x" - this will work
whether the variable is local or global.

An alternative solution is to put a non-eliminatable function inside the
while loop - most embedded compilers have a "nop()" function or similar
which is guarenteed not to be eliminated.  Other ways to make a function
"non-eliminatable" are to use a volatile variable inside the function -
although the function could well be inlined and everything but this volatile
access eliminated.  If you use the second technique, it is worth noting that
the compiler is still free to eliminate or change x in any way it wants -
just as long as the function (or the non-eliminatable parts of it) are
carried out the correct number of times, in the correct order.  For example,
the loop could be unrolled in bunches of 10, with a counter running from 9
down to 0.


It is interesting to note how msp430-gcc optomises such loops, depending on
the compiler flags and the sizes of the loops.  For example, with a loop
with "x = 10", the compiler will eliminate the code at -O2 and above but not
at -O1.  But if "x = 100", the code will not be eliminated unless
"-funroll-loops" is on - a loop of size 100 is too big for automatic
unrolling.  Once unrolled, the code is then eliminated.


> It leaves me worried about what else the compiler might be optimising
> out. I've always thought that, as engineers, we are grown up enough to
> be in charge of the process, not it in charge of us.
>
> Paul Burke
>

As for what else the compiler might optomise out - just think about what it
*could* optomise out, and code accordingly.  It is also worth using the full
set of compiler warning flags (at least "-W -Wall") to track dead code and
unnecessary variables.  I use -O2 as standard - the only reason I don't
use -O3 is that it is easier to read the generated assembly with -O2.

As engineers, we should write what we mean when programming - not what we
think a simpler, older compiler might think we mean.

mvh.

David.



Reply via email to