[net.lang.c] ANSI C, optimization, `volatile' declarations

mjs@rabbit.UUCP (M. J. Shannon, Jr.) (10/15/84)

<Eat this, Bug!>

> > [Compiler is optimizing out a wait-for-hardware-done loop.]
> > ...
> > 1.  Was this legal code generation?
> > 2.  Note that this compiler did "simple" optimizations as part of the code
> > 	generation. Is this legal?
> 
> As to whether it's legal by K&R, the only answer is "mumble".  This
> thorny issue was never addressed in the old days.  The draft ANSI standard
> has a "volatile" declaration that you can use to tell the compiler "don't
> get tricky with this variable, it may change underfoot".

First, I'd like to point out that the name of the standards organization
is ANSI: American National Standards Institute.  The name is unrelated to
the name of the character set most of us are using (ASCII: American
Standard Code for Information Interchange).  Now, with that out of the
way....

Ok, given the `volatile' keyword, what are its semantics?  If (on System V)
I have pointers into memory that is shared by other processes, must I
declare them `volatile struct foo *' and have the compiler deduce that all
members of such a structure are volatile (my opinion of correct behavior)?
Further, if the structure so referenced has pointers in it, are they
assumed to be volatile or non-volatile?  If my program manages linked lists
in shared memory, must the forward and backward pointers also be declared
volatile?  If so, this means that I may suffer from non-optimization of
linked lists (identically declared) in non-shared memory.  Volatility
declarations are very tricky in a language such as C!

Is a current draft available (sorry, I haven't been following this
discussion due to (what seems to me to be) an inordinate amount of
drivel)?  If so, how may I obtain it?
-- 
	Marty Shannon
UUCP:	{alice,rabbit,research}!mjs
	(rabbit is soon to die.  Does this mean alice is pregnant?  Yup!)
Phone:	201-582-3199

jss@sftri.UUCP (J.S.Schwarz) (10/15/84)

> 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