> I'm willing to admit that I don't fully understand where volatile goes. Not
> only would it be very noble of someone to explain this to us, but I'm
> certain that he would be rained with fame, women, and money for his efforts.

It goes like this: the C language is inherently single-threaded.
It does not understand multiple threads or interrupt handlers.

The standard is very clear that the compiler is allowed to assume that
nobody is changing any variables except the executing code.  So when
I write

        foo = 42;
        bar = foo;

If I don't refer to foo again, the compiler is quite free to eliminate
it entirely and just generate
        bar = 42;

Further, if the rest of the code ends up using the vale of bar/2 a lot,
the compiler could just let bar = 21 and eliminate all those divisions.

To take a simple example, if you write a standard loop in C:

for (i = 0; i < 10; i++)
        foo();

gcc actually generates:
bar:
        call    #foo
        call    #foo
        call    #foo
        call    #foo
        call    #foo
        call    #foo
        call    #foo
        call    #foo
        call    #foo
        call    #foo
        ret

Look ma, no variable i at all!

If I make the loop a little bigger:

unsigned x = 0
for (i = 0; i < 100; i++)
        x += foo(x);
return x;

Then I get

bar:
        push    r11
        push    r10
        mov     #0, r10 
        mov     #99, r11 
.L6:
        mov     r10, r15 
        call    #foo
        add     r15, r10
        add     #-1, r11
        jge     .L6
        mov     r10, r15 
        pop     r10
        pop     r11
        ret

The closest thing to a variable i is r11, which counts from 99 down to 0
rather than 0 up to 99.

The compiler can do this because it knows that nobody, including the
called function foo, is allowed to see or change i, so it can convert
it to a different form.


This sort of thing is harder to do with global variables, because the
compiler can't see the entire program as it's compiling (but there do
exist compilers which do such "interprocedural" optimization at link
time, so it's not impossible), but the compiler can still load
the global variable when it enters a function and store it once when
it leaves and not touch it in memory in the mean time.  One thing
it does particularly often is change the order in which it reads from
or writes to such variables.


Now, any form of multithreading, including interrupt handlers or
hardware peripherals that rewrite status registers, breaks this
assumption all over the place.  In order to make it possible to write
such code, the ANSI C standard includes the "volatile" keyword.

"volatile" says "someone else may read or write this variable at any
time, so don't get clever.  Every time I refer to the variable in my
source code, you have to actually go to the given memory address and
do an actual load or store."

The compiler is still allowed to optimize access to any other variables,
but doesn't assume that the "volatile" variable has the same value
when loaded as was last stored.


If you have a variable that is used by an interrupt handler, you must
declare it volatile in the non-interrupt code that reads or writes it,
or what the interrupt handler sees and when it sees it won't be
well-defined.  Likewise, if it's written by the interrupt handler,
it has to be volatile or there's no guarantee the main code will see the
change.

This pessimism, however, comes at a significant performance cost.
*Don't* use voltatile anywhere you don't need it.  If a "variable"
never actually varies, or is only accessed from one thread, leaving
volatile off generates smaller and faster code.  Local variables,
for example, are almost never volatile.

Hardware registers act like interrupt handlers, too - the "interrupt
handler" just executes on custom hardware rather than in the
main processor.


One possible analogy is like going to the bathroom in the dark.  I can get
out of bed and find the bathroom without turning the lights on because I
know where everything is, so I don't have to see the furniture to avoid
tripping on it.  The furniture is where I last left it.  But if I live
with someone who likes rearranging the furniture, I'd better turn on
the lights or I'm going to get in trouble.


Here's one example of volatile usage that isn't an interrupt handler,
but is an excellent example of its intended use.  Sometimes, people like
to include identifcation strings (like the famous RCS $Id: ...$ headers)
in an executable or ROM image.  And there are utilities like "rcsid" or
"strings" that search through the executable and pull out those strings.
A lot of source files start with

static char const rcsid[] = "$Id: file.c,v 1.32 2005/11/04 ...";

It's static so the same name ("rcsid") can be used in each file without
the compiler complaining.

But some compilers are smart enough to notice that nothing inside file.c
actually refers to that array, and since it's static, no other source
files can refer to it, so they leave it out!

But the whole point of the string is that it's read by something *other*
than the program - they're read by something that examines the non-running
program's executable.

This is exactly what volatile is for.  You can write

static char const volatile rcsid[] = "$Id: file.c,v 1.32 2005/11/04 ...";

and the compiler knows it's not allowed to delete it just because it
can't figure out who's reading it.

Reply via email to