'volatile' semantics

I believe David is right.

cf.

formatting link
and the original paper "Volatiles Are Miscompiled, and What to Do about It"
formatting link

"A compiler may not move accesses to volatile variables across sequence points. No guarantees are made about the atomicity of any given volatile access, about the ordering of multiple volatile accesses between two consecutive sequence points, or about the ordering of volatile and non-volatile accesses. For example, the following code illustrates a common mistake in which a volatile variable is used to signal a condition about a non-volatile data structure, perhaps to another thread:

volatile int buffer_ready; char buffer[BUF_SIZE]; void buffer_init( ) { int i; for (i=0; i

Reply to
Noob
Loading thread data ...

It was invented for server class PowerPC chips to give a lower cost (in cycles) synchronization for that specific case. General processor/memory/cache synchronization is more expensive.

As I recall Intel and AMD chips also have more than one synchronization mechanize and you have to use the right one (memory/cache vs mem mapped I/O for example).

Reply to
Dennis

He sent me a link both to some audio and to the text, I am now eieio aware, I guess :D .

Dimiter

Reply to
Didi

In practice: probably. According to the letter of the standard: no. The compiler would be perfectly within its rights to optimise the above to:

extern volatile bool testPin;

void speedTest(void) { testPin = 0; }

In your example, the "volatile" qualifier on "sum" has no significance according to the standard. It might have the desired effect upon certain compilers, but that isn't guaranteed by the standard.

If you want to reliably prevent the loop from being optimised away, you should include a call to a function with external linkage within the loop body. The compiler cannot optimise away such calls.

The key point here is 5.1.2.3#10, commonly known as the "as-if rule":

[#10] Alternatively, an implementation might perform various optimizations within each translation unit, such that the actual semantics would agree with the abstract semantics only when making function calls across translation unit boundaries. In such an implementation, at the time of each function entry and function return where the calling function and the called function are in different translation units, the values of all externally linked objects and of all objects accessible via pointers therein would agree with the abstract semantics. Furthermore, at the time of each such function entry the values of the parameters of the called function and of all objects accessible via pointers therein would agree with the abstract semantics. In this type of implementation, objects referred to by interrupt service routines activated by the signal function would require explicit specification of volatile storage, as well as other implementation-defined restrictions.
Reply to
Nobody

Only in terms of the "abstract semantics". The "actual semantics" (i.e. the resulting object code) only have to agree in terms of *observable* behaviour, which basically means when crossing the boundary between translation units.

See 5.1.2.3#10 (cited by me elsewhere in this thread).

Reply to
Nobody

Para 9? (I'm working from the 2nd ed, Dec 99). In the (non-normative?) example explanation there, the statement: "In this type of implementation, objects referred to by interrupt service routines activated by the signal function would require explicit specification of volatile storage, as well as other implementation-defined restrictions" would seem to indicate that, even on a fully optimized compiler, both accesses to testPin are required (and in order) since there might be magic happening somewhere outside the current execution thread's knowledge.

--
Rich Webb     Norfolk, VA
Reply to
Rich Webb

Of course you mean

void speedTest(void) { testPin = 1; testPin = 0; }

Otherwise you'd be losing one of the operations on testPin, which is volatile.

--

Tim Wescott
Wescott Design Services
http://www.wescottdesign.com

Do you need to implement control loops in software?
"Applied Control Theory for Embedded Systems" was written for you.
See details at http://www.wescottdesign.com/actfes/actfes.html
Reply to
Tim Wescott

Is "volatile" ignored (by the standard) on local variables? It is certainly the case that many compilers will put "sum" on the stack and treat it as volatile. But I would be sceptical to relying on that behaviour - I have the feeling that this is an area where the standards are not as clear as they could be, and that compilers are not consistent in their interpretation. I think compilers here will err on the side of caution by putting "sum" on the stack and treating it as volatile - after all, that is certainly allowed by the standards, even if it is perhaps not required.

Reply to
David Brown

That's a list of the sequence points. The compiler has to generate code that acts /as if/ it obeyed sequence points - it does not have to generate code that /actually/ obeys sequence points.

"Side effects" can refer to changes that are only seen within the program (such as changing a static variable). "Visible results", as I use the term here, refers to effects that are visible from /outside/ the program. That's the only relevant point.

Think of a program as a black box. It has some inputs, and some outputs. You provide it with a sequence of inputs (noting here that C has no concept of timing, only ordering of events), and it will generate a sequence of outputs. The C standards define how the compiler should interpret the source code so that for a given sequence of inputs, it will produce a clearly defined sequence of outputs - this is based on the rules for "volatile", synchronisation at sequence points, etc. The "as if" rule says that the compiler can generate whatever code it wants, as long as the sequence of outputs is the same for the same set of inputs as if the compiler had naively followed the C rules.

Incorrect.

Think about your statement here in terms of my "black box" description. If I make several black boxes - one that does the calculation explicitly between the two volatile writes, and others that do it before or after the volatile writes, or omit the loop entirely - can you tell the difference between them? Remember, there is no "time" in C. The black boxes are indistinguishable, and the compiler is free to use whichever one it wants.

In general, the compiler can freely mess around with local variables, as these are invisible to the outside world. File and function static variables are also good candidates for optimisations - as long as the compiler can prove by pointer and alias analysis that no references to these objects can "escape". If you never take the address of a static or local variable, then the compiler knows everything about it - no function can (legally) read or modify its value.

Reply to
David Brown

AFAIU, you can ask to revert to Google's previous interface. If you do, message threading is "unbroken".

Reply to
Noob

Right, but my initial thought was "so what?". At the time, I actually meant what I wrote, but in retrospect I agree that the standard tries quite hard to specify your version.

In terms of the abstract semantics, it's impossible to write a program which can actually determine whether the "testPin=1;" was omitted or whether it (e.g. a signal handler) simply never gets to observe the value of testPin during the miniscule window where it's set to 1.

And the as-if rule only says the code has to behave according to the abstract semantics. This lead me to believe that the first statement could be omitted.

6.7.3#6 tries to specify the general meaning of "volatile", but it doesn't help that it finishes with:

What constitutes an access to an object that has volatile-qualified type is implementation-defined.

However, looking at footnote 103:

103A volatile declaration may be used to describe an object corresponding to a memory-mapped input/output port or an object accessed by an asynchronously interrupting function. Actions on objects so declared shall not be ``optimized out'' by an implementation or reordered except as permitted by the rules for evaluating expressions.

That clarifies that 6.7.3#6 is at least attempting to specify the "actual semantics", i.e. that it's overriding the as-if rule with respect to "volatile" objects.

However, that's a bit of a fudge given that the standard doesn't otherwise have much to say about actual semantics. Which is possibly why it was relegated to a footnote; it sounds like "we'd like to formally specify this, but we really haven't got a clue how to do so".

Reply to
Nobody

The bit about "other implementation-defined restrictions" could require that, but in fact it's footnote 103 which appears to override 5.1.2.3#10:

103A volatile declaration may be used to describe an object corresponding to a memory-mapped input/output port or an object accessed by an asynchronously interrupting function. Actions on objects so declared shall not be ``optimized out'' by an implementation or reordered except as permitted by the rules for evaluating expressions.

The part about signal handlers only means that, if you don't use "volatile" the compiler might optimise out a read of a variable which is modified by a signal handler, e.g.

volatile sig_atomic_t x = 0; while (!x) { ... };

where the "..." is guaranteed not to modify x.

Without the volatile qualifier, the above is equivalent to:

volatile sig_atomic_t x = 0; if (!x) while (1) { ... };

If x was zero the first time, and it isn't modified in the body, then it will always be zero.

...

But my point was that the statement "The certain points are sequence points" was incorrect within the context that it was made.

Sequence points relate to the abstract semantics. The only points where actual semantics have to agree with abstract semantics are when crossing translation unit boundaries (which includes signal handlers, which are always invoked from some unspecified "other" translation unit) plus the hand-wavy exemption for volatiles. These are far less common than sequence points.

Bear in mind that all writes are sequence points, and reads of volatile objects are also sequence points. If you're writing to an object, and it actually matters whether or not it the object is volatile, sequence points aren't the issue.

If you look at the object code generated by an optimising compiler, code gets moved around sequence points, sequence points get eliminated entirely. The only constraint is that "external" functions get called in the correct order with the correct arguments and with the correct values in "visible" storage.

Sequence points simply allow the standard to specify the value of abstract entities (e.g. variables) at specific "points" in the abstract program. At each sequence point, an (abstract) object has a specified value determined by the values of objects at the previous sequence point.

They don't specify concrete behaviour, e.g. what gets stored in memory or in what order.

Concrete behaviour is only specified at all in order to facilitate separate compilation (multiple translation units). Otherwise, the standard would effectively only specify the program's "output" (e.g. the data written to files and the program's exit status), and the compiler would be free to perform whole-program optimisation as it saw fit.

Reply to
Nobody

No, that's not correct - again, C has no concept of time. "testPin" is volatile - the code /must/ write 1 to it, then 0. It may turn out that there are cpu cache-combining effects that hide the "1", or the hardware is too slow to react to the small spike - but the compiler can't know that.

It is /precisely/ this rule that means the two statements /must/ be generated. The abstract semantics have no timings, only orderings - it does not matter if there is a year or a nanosecond between the two statements.

Reply to
David Brown

Is EIEIO better than SEX (*) ? .. .. .. .. .. .. .. (*) Sign EXtension - DEC PDP-11, Mot 6809, possibly others.

-- Roberto Waltman

[ Please reply to the group. Return address is invalid ]
Reply to
Roberto Waltman

Anybody can sneak a three-letter puerile mnemonic into an instruction set.

--
Tim Wescott
Control system and signal processing consulting
www.wescottdesign.com
Reply to
Tim

If testPin maps to an actual I/O pin, however, and the usual hardware functionality is there, then you would see the pin go high for at least one processor clock, then go low.

It could not be -- or perhaps _should_ not be, either in the case of a pin that gets strobed nor in the case of hardware which does something with each write. (This latter is actually what spawned my original question -- I was struggling with hardware that writes out to a FIFO with each write operation, and reads from a FIFO with each read. The problem turned out to not be a problem with volatile, but it took me a while to realize that).

Yup. The marketplace seems to have painted itself into a corner with the joy of optimization vs. the pain of hardware interfaces that must work correctly.

--

Tim Wescott
Wescott Design Services
http://www.wescottdesign.com

Do you need to implement control loops in software?
"Applied Control Theory for Embedded Systems" was written for you.
See details at http://www.wescottdesign.com/actfes/actfes.html
Reply to
Tim Wescott

Perhaps it's not /better/, but it is certainly more imaginative!

Reply to
David Brown

I would think it is at least one bus clock, even.

--
Gemaakt met Opera's revolutionaire e-mailprogramma:  
http://www.opera.com/mail/
(Remove the obvious prefix to reply.)
Reply to
Boudewijn Dijkstra

Probably, if your brain isn't temporarily stuck back in the days when microprocessor and memory were going the same speed :(.

--

Tim Wescott
Wescott Design Services
http://www.wescottdesign.com

Do you need to implement control loops in software?
"Applied Control Theory for Embedded Systems" was written for you.
See details at http://www.wescottdesign.com/actfes/actfes.html
Reply to
Tim Wescott

ElectronDepot website is not affiliated with any of the manufacturers or service providers discussed here. All logos and trade names are the property of their respective owners.