views:

802

answers:

7

Consider the following:

volatile uint32_t i;

How do I know if gcc did or did not treat i as volatile? It would be declared as such because no nearby code is going to modify it, and modification of it is likely due to some interrupt.

I am not the world's worst assembly programmer, but I play one on TV. Can someone help me to understand how it would differ?

If you take the following stupid code:

#include <stdio.h>
#include <inttypes.h>

volatile uint32_t i;

int main(void)
{
        if (i == 64738)
                return 0;
        else
                return 1;
}

Compile it to object format and disassemble it via objdump, then do the same after removing 'volatile', there is no difference (according to diff). Is the volatile declaration just too close to where its checked or modified or should I just always use some atomic type when declaring something volatile? Do some optimization flags influence this?

Note, my stupid sample does not fully match my question, I realize this. I'm only trying to find out if gcc did or did not treat the variable as volatile, so I'm studying small dumps to try to find the difference.

+8  A: 

Try setting the variable outside a loop and reading it inside the loop. In a non-volatile case, the compiler might (or might not) shove it into a register or make it a compile time constant or something before the loop, since it "knows" it's not going to change, whereas if it's volatile it will read it from the variable space every time through the loop.

Basically, when you declare something as volatile, you're telling the compiler not to make certain optimizations. If it decided not to make those optimizations, you don't know that it didn't do them because it was declared volatile, or just that it decided it needed those registers for something else, or it didn't notice that it could turn it into a compile time constant.

Paul Tomblin
+4  A: 

Read the standard before you misquote or downvote. Here's a quote from n2798:

7.1.6.1 The cv-qualifiers

7 Note: volatile is a hint to the implementation to avoid aggressive optimization involving the object because the value of the object might be changed by means undetectable by an implementation. See 1.9 for detailed semantics. In general, the semantics of volatile are intended to be the same in C++ as they are in C.

The keyword volatile acts as a hint. Much like the register keyword. However, volatile asks the compiler to keep all its optimizations at bay. This way, it won't keep a copy of the variable in a register or a cache (to optimize speed of access) but rather fetch it from the memory everytime you request for it.

Since there is so much of confusion: some more. The C99 standard does in fact say that a volatile qualified object must be looked up every time it is read and so on as others have noted. But, there is also another section that says that what constitutes a volatile access is implementation defined. So, a compiler, which knows the hardware inside out, will know, for example, when you have an automatic volatile qualified variable and whose address is never taken, that it will not be put in a sensitive region of memory and will almost certainly ignore the hint and optimize it away.

This keyword finds usage in setjmp and longjmp type of error handling. The only thing you have to bear in mind is that: You supply the volatile keyword when you think the variable may change. That is, you could take an ordinary object and manage with a few casts.

Another thing to keep in mind is the definition of what constitutes a volatile access is left by standard to the implementation.

If you really wanted different assembly compile with optimization

dirkgently
Definitely not a hint. See C and C++ standards on "observable behavior". "float" is a hint in comparison to volatile.
MSalters
Exactly. Read the whole thing -- the standard defines what constitutes a volatile access to the implementation.
dirkgently
@MSalters: Updated with quote.
dirkgently
Yes, compilers are free to ignore the hint.
Max Lybbert
Johannes Schaub - litb
so i think the compiler can't do any optimizations in fact because what is needed depends on the program and its purpose. so if you have a local variable "i" that's volatile, int a = (1, i, 2); value of i is thrown away, but it's still read from memory.
Johannes Schaub - litb
Then why does the standard say "volatile is a hint" and not "volatile is a command"?
Max Lybbert
+7  A: 

Many compilers in some situations don't treat volatile the way they should. See this paper if you deal much with volatiles to avoid nasty surprises: Volatiles are Miscompiled, and What to Do about It. It also contains the pretty good description of the volatile backed with the quotations from the standard.

To be 100% sure, and for such a simple example check out the assembly output.

Anonymous
The assembly output with or without the volatile keyword is the same on this simple sample, which makes me wonder if gcc just obeys the declaration or decides if the variable is not going to be modified. So, how can I tell from the assembly output?
Tim Post
Try throwing in some optimisation flags, small difference should appear.
Anonymous
THANKS! :) I have to obey the CFLAGS that a user sets when compiling, so I'm pulling my hair out to find out of something volatile was treated as ordinary.
Tim Post
+3  A: 

As far as I know, volatile helps the optimizer. For example, if your code looked like this:

int foo() {
    int x = 0;
    while (x);
    return 42;
}

The "while" loop would be optimized out of the binary.

But if you define 'x' as being volatile (ie, volatile int x;), then the compiler will leave the loop alone.

David Wolever
+2  A: 

It should always treat it as volatile.

The reason the code is the same is that volatile just instructs the compiler to load the variable from memory each time it accesses it. Even with optimization on, the compiler still needs to load i from memory once in the code you've written, because it can't infer the value of i at compile time. If you access it repeatedly, you'll see a difference.

Jesse Rusak
It should, but compilers are allowed to ignore the hint. I don't think GCC ignores the hint, but they are allowed to.
Max Lybbert
+1  A: 

Any modern compiler has multiple stages. One of the fairly easy yet interesting questions is whether the declaration of the variable itself was parsed correctly. This is easy because the C++ name mangling should differ depending on the volatile-ness. Hence, if you compile twice, once with volatile defined away, the symbol tables should differ slightly.

MSalters
+2  A: 

Your little sample is inadequate to show anything. The difference between a volatile variable and one that isn't is that each load or store in the code has to generate precisely one load or store in the executable for a volatile variable, whereas the compiler is free to optimize away loads or stores of non-volatile variables. If you're getting one load of i in your sample, that's what I'd expect for volatile and non-volatile.

To show a difference, you're going to have to have redundant loads and/or stores. Try something like

int i = 5;
int j = i + 2;
i = 5;
i = 5;
printf("%d %d\n", i, j);

changing i between non-volatile and volatile. You may have to enable some level of optimization to see the difference.

The code there has three stores and two loads of i, which can be optimized away to one store and probably one load if i is not volatile. If i is declared volatile, all stores and loads should show up in the object code in order, no matter what the optimization. If they don't, you've got a compiler bug.

David Thornley