ANSI C, optimization, `volatile' declarations
J.S.Schwarz
jss at sftri.UUCP
Tue Oct 16 06:29:27 AEST 1984
> Ok, given the `volatile' keyword, what are its semantics? ...
> Volatility declarations are very tricky in a language such as C!
Yes the semantics are tricky and the current draft is not
completely clear. I have been thinking about this problem
for several months and I believe there is an approach that,
while not resolving all confusion, provides a framework
in which most of the question can be consistently resolved.
First, it must be clearly understood that "volatility" is a
property of compile time expressions, not runtime data.
Whenever such an expression occurs the compiler is obliged
to generate code that implements the "raw" semantics of C.
It must generate exactly as many fetches/stores as are
required by the semantics of C for the expression as a
whole. With one exception, whether fetches/stores are
required in evaluating subexpressions are controlled by
whether the subexpressions are themselves volatile. The
exception is that evaluation of "&foo" where foo is a simple
variable is never permitted to fetch "foo".
How do we know if an expression has the "volatility"
property? The easiest answer is to make it part of the
type.
To examine some of the implications of this approach,
consider
char nonvolatile_global;
volatile char volatile_global;
struct members {
char nonvolatile_member ;
char volatile_member ;
} ;
volatile struct members volatile_head, *ptr ;
struct members nonvolatile_head ;
Let us look at some expressions that might be used as a
function argument and ask what fetches the compiler is
required to generate.
------------------
volatile_global
volatile_head
*ptr
These imply a fetch of the data. In the case of
volatile_head and *ptr, this means all data contained in
the structure.
------------------
volatile_head.volatile_member
ptr->volatile_member
My reading of the current draft would require that the
whole struct be fetched in both cases and that the char be
extracted from the value without an additional access.
------------------
nonvolatile_head.volatile_member
A single fetch of the single char is required.
------------------
(volatile char)nonvolatile_global
This expression does not require a fetch. The expression
is volatile so we must conform to the C semantics in
evaluating it. That means we must evaluate
"nonvolatile_global" and then convert the value. But the
cast applies to the value, not to the "name" and thus we do
not have to do a fetch of the name. But evaluating
"nonvalatile_global" does not force a fetch since that
expression is not volatile.
------------------
*(volatile char*)&nonvolatile_global
*&volatile_global
These slightly "tricky" expressions require exactly one
fetch. The first is volatile because of the cast and the
semantics of C requires a fetch. The standard is not
explicit whether the the subexpression of a "&" is
evaluated. (Except in the presence of "volatile" it doesn't
matter) I think that the reasonable interpretation is that
only as much evaluation is performed as is needed to
determine the pointer. In particular "&volatile_global"
does not permit an evaluation of "volatile_global". I
believe that this case is important enough that an explicit
provision should be made that evaluation of "&foo" never
fetches foo, whether or not foo is declared volatile.
------------------
Some of the above expressions are also lvalues. Let us
examine them in the context of a left hand side of an
expression:
------------------
volatile_global
volatile_head
*ptr
*(volatile char*)&nonvolatile_global
In these cases stores are requied exactly where fetches
were above.
------------------
volatile_head.volatile_member
ptr->volatile_member
I am not completely sure, but my reading of the draft is
that only the char is required to be stored not the entire
structure, and no fetches are allowed. A plausible
alternative reaing would be that the structure as a whole
must be fetched and the only the char stored.
------------------
A problem remains. Does the use of "volatile" imply
anything about the absence of fetches/stores not implied
by the "raw" semantics of C. This is harder to answer for
two reasons. Firstly there are many cases where an area of
memory can be touched in nonobvious ways. Secondly, machine
architectures may make it impossilbe. For example, on some
machines it may be impossible to fetch/store a char without
touching neighboring values. Thus stores of
head.nonvolatile_member may unavoidably imply stores of
head.volatile_member. Similar problems arise with regard
to the atomicity of fetches/stores of volatile data. I
think the best that can be asked for is a "good faith"
effort on the part of the compiler, and a requirement that
its behavior be documented.
In the end it seems likely that the standard will not be
completely clear about all cases. This is probably
inevitable in a standard that is not based on a formal
semantics. Nonetheless, I am completely convinced that the
introduction of the "volatile" specifier is a good idea.
Portability of any program that uses a "shared variable" is
chancy at best whether or not a declaration is present. The
more concerned about portability the programmer is, the
more conservative s/he ought to be about the use of "shared
variables". Two sensible rules might be: declare "volatile
long" a variable that is used to communicate between a
function that catches signals and the rest of a program,
and never declare volatile members. This is very much in
the "spirit" of C, which in part requires that portability
is the result of care by the programmer, not a magical
consequence of using C. In the absence of "volatile"
programmers might be able to force the fetches/stores they
require by such hacks as using expressions that are too
complicated for the optimizer to understand, turning off
optimization, using "asm" or modifying the compiler. But
these hacks are likely to be much less portable than
volatile declarations, even if there is some variation in
the interpretation of the doubtful cases discussed above.
Jerry Schwarz
ihnp4!btlunix!jss
Bell Labs, 190 River Road, Summit N.J., 07901
P.S.
The latest draft of the ANSI proposal is available from
Jack Warner
ihnp4!btlunix!jlw
Bell Labs, Room F-325, 190 River Road, Summit, N.J. 07901
More information about the Comp.lang.c
mailing list