A different view of volatile

Hank Dietz hankd at pur-ee.UUCP
Tue Apr 26 10:29:37 AEST 1988


In article <2003 at rtech.UUCP>, jas at llama.rtech.UUCP (Jim Shankland) writes:
> In article <13074 at brl-adm.ARPA> dsill at NSWC-OAS.arpa (Dave Sill) writes:
> >Terry Lambert writes:
> >>Basically, if it works without -O, it should work with -O, regardless of what
> >>the compiler writer's optimization does to achieve its goal.  If this makes
> >>writing compilers harder, so what?
> >
> >This bears repeating.  There should be no circumstances under which
> >the semantics of the language are changed by a flag to the compiler.

I agree.  The programmer should explicitly request "magic" which might
change the results of running a program; further, this request should
be within the language, not an ad-hoc command-line option.

> The alternatives that have been mentioned include:
> 
> 	(1) "Just don't do all that silly optimization".  Then, as Dennis

Actually, you can still do a lot of that optimization... it just takes more
compile time to do the better flow/dependence analysis to support it.

> 	(2) "If you need `volatile', don't use C."  I agree, it's

The un-solution.

> 	(3) "Let implementers of optimizing compilers come up with their
> 	own pragmas that can turn off certain optimizations where they
> 	will be destructive."  In other words, let each implementer

Another un-solution... this is the "programmer's job security enhancement"
choice.  Further, there is no guarantee that two compilers will not use the
exact same #pragma to mean two subtly different things.

Instead, why not use "register" to solve the problem....

Ever since K&R, code which takes the address of a register variable has been
non-portable in the best sense of the phrase:  where the code wouldn't work,
it would be flagged as a compile-time error.  Now, X3J11 says that it is
ALWAYS a compile-time error to take the address of a register variable.
In other words, if code is broken by register assignment, the compiler
can easily flag this condition at compile time.

Now, look at volatile.  Volatile says that an object either can change value
while we are not looking at it or that somebody else may notice when we
change the object's value.  This can only happen if two or more "processes"
(perhaps across multiple processors or DMA channels) simultaneously have
access to the same object.  It is easy for one process to know where an
object is (by simply declaring it), but I know of NO EXISTING C MECHANISM
for letting another process access that object OTHER THAN to pass the
explicitly-taken address of the object.  The same is true of DMA locations:
they are specific places in memory, and the only general way to overlay a
specific place is to set a pointer to that address.  In other words, AN
OBJECT WHICH HAS NO ADDRESS CANNOT BE VOLATILE:  A REGISTER OBJECT CANNOT BE
VOLATILE!

Why not make use of this SAFE, prior-art, compiler hint?  Currently, a C
compiler is correct iff it treats ALL non-register variables as volatile
(unless flow analysis can prove that the object address is never taken:
i.e., unless flow analysis can prove that it would have been safe to place
the variable in a register :-).  I propose that we simply extend register so
that, instead of merely being a flavor of auto storage class, it is an
attribute which can be applied to static and global data as well as to
expressions... the meaning is simply "this could be in a register" or,
phrasing it slightly differently, "this is NOT volatile."  This usage not
only breaks no (reasonable :-) existing code, but it also results in
compile-time errors where code would be broken, rather than resulting in the
mysterious execution caused by lack of a now-required-by-X3J11 volatile
declaration.

Oddly enough, the extended notion of register IS "noalias" with a somewhat
more flexible structure; an object has no aliases if one can only have one
name for it, and the inability to take the address of a register object
provides exactly this guarantee.

The semantics proposed are:

1. Use of "register" as the storage class specifier in a declaration
indicates that the object being allocated is of the default storage class,
but cannot have its address taken.  In other words, the declared items are
non-volatile and have the noalias property.

2. Use of "register" as an attribute of a storage class ("register" appearing
immediately after the storage class specifier in a declaration) forces the
exact same constraint: all thus declared objects carry the "register"
property.

3. Use of "register" as an attribute within a declaration after the type
specifier indicates that the appropriate typed entity has the register
property.  (This rule actually is how register declarations *should* have
been done, but it does not conflict with supporting the old-style register
declaration syntax for a while :-).

4. Use of "register" as an attribute cast on an expression, or as an
attribute cast within a declaration, forces the interpretation that the
expression value object has the "register" property.

5. When used as a cast or in a function argument list, the "register"
attribute does not propagate, whereas it does propagate when used as part of
a declaration.

Some simple examples:

/* a register global defined elsewhere */
register extern int a;

/* an array of 100 doubles, all conceptually accessible ONLY by use of the
   name d, with or without subscripts, but of static storage class.
*/
register static double d[100];

/* a pointer to a char object for which no other name exists...
   a pointer to a non-volatile, noalias, object.
*/
char register *p;

/* how the function strcpy should be declared (but need not be)... */
char *
strcpy(char register *p, register *q;)
{ ... }

/* how strcpy might be used... including a cast on malloc()... */
char register *p = (char register *) malloc(6);
p = strcpy(p, "hello");

Now, I'm not saying that the above syntax is perfect, and as DMR pointed-out
to me, having all those "register"s is certainly noisy, but by the rule of
"if it ain't broken, don't fix it" it seems to me that the above extension of
register -- especially rules 1. and 2. -- is much more reasonable.  Further,
although I have nothing against volatile, per se, isn't it better to go with
the more aggressive compiler optimization only when a person has specifically
hinted that it's ok, as opposed to the "volatile" scheme of aggressively
optimizing unless you're told no?  Besides, by now we all know how to spell
"register."

The views expressed above are overly written and under thought-out, but there
they are, so what is the consensus?  Is register as useful as I think it is?

						-Prof. Hank Dietz
						 School of EE
						 Purdue University
						 West Lafayette, IN  47907



More information about the Comp.lang.c mailing list