Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Memory mapped I/O vs. volatile

206 views
Skip to first unread message

John R. Levine

unread,
Aug 24, 1991, 9:41:58 AM8/24/91
to
In article <GLEW.91Au...@pdx007.intel.com> gl...@pdx007.intel.com (Andy Glew) writes:

> mem->reg.byte0 = 0; mem->reg.byte1 = 0;
>[might be] combined by the compiler into a 16 bit store...

>ANSI C volatile has several target audiences, including writers of parallel
>applications, signal handlers, and I/O drivers.
> Now, you might declare byte0 and byte1 above as volatile. But the
>writer of parallel programs probably would like the compiler to
>combine the writes.
> So, what does ANSI do?

In my draft of the standard, section 2.1.2.3 says that "At sequence points,
volatile objects are stable in the sense that previous evaluations are
complete and subsequent evaluations have not yet occurred." Footnote 64 to
section 3.5.3 says "A volatile declaration may be used to describe an object
corresponding to a memory-mapped input/output port on 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."

I have long claimed, albeit not always very coherently, that volatile is a
snare and a delusion. It does seem to be useful for variables shared between
a mainline routine and a signal running on the same CPU, but for memory mapped
I/O and variables shared between multiple processors its simplistic memory
model just isn't adequate.

For memory-mapped I/O, the example above shows a typical problem. Other
messages have noted that obscure implementation details, e.g. a clear
instruction that does a read/modify/write, can cause major trouble with I/O
ports that do something on each reference. It also seems to me that given
that memory-mapped I/O code is inherently machine specific, it is silly to
invest a lot of effort in defining a putatively portable way to write it. I'd
be happier with some pragmas that told the local compiler about the warts of
the devices in a way that was adequate to produce reliable code.

On multiprocessors, write-behind caches make volatile variables nearly
useless. On a multiprocessor IBM 370, for example, a processor is allowed to
delay memory writes arbitrarily long as viewed by other processors. (It has
to be internally consistent, but you don't need volatile for that.) You can
force out delayed writes with a pipe-drain no-op, but that is very slow. It
looks to me like a conforming implementation on a 370 should put a pipe-drain
after every store to a volatile variable. I doubt users would be very happy
about that. They'd need some way to identify synchronization points, perhaps
with a pragma or a SYNCPOINT() macro, but with explicit syncpoints there's no
need even to store shared variables after every reference, much less force
them to memory.

--
John R. Levine, IECC-by-the-sea, Harvey Cedars NJ "Where TAT-9 comes ashore"
jo...@iecc.cambridge.ma.us, {ima|spdcc|world}!iecc!johnl

Blair P. Houghton

unread,
Aug 24, 1991, 3:58:16 PM8/24/91
to
I don't think it's that complex, really, it just means that
the latches should be latched when the access is made, and
the access should be made only when the latches are latched
(the latches being virtual or the side-effect of predetermined
synchrony, not necessarily hardware that latches.)

The appelation 'volatile' certainly isn't concerned with
protocol on the bus.

The "don't optimize-out volatiles" feature merely prevents
the ANSI C implementation from throwing away variables
which are assigned-to/read-from and are never involved in a
corresponding read-from/assigned-to operation.

--Blair
"Slippery little suckers."
-Julia Roberts

Doug Gwyn

unread,
Aug 24, 1991, 8:45:17 PM8/24/91
to
In article <1991Aug24.1...@iecc.cambridge.ma.us> jo...@iecc.cambridge.ma.us (John R. Levine) writes:
-In article <GLEW.91Au...@pdx007.intel.com> gl...@pdx007.intel.com (Andy Glew) writes:
->ANSI C volatile has several target audiences, including writers of parallel
->applications, signal handlers, and I/O drivers.
-I have long claimed, albeit not always very coherently, that volatile is a
-snare and a delusion. It does seem to be useful for variables shared between
-a mainline routine and a signal running on the same CPU, but for memory mapped
-I/O and variables shared between multiple processors its simplistic memory
-model just isn't adequate.
-For memory-mapped I/O, the example above shows a typical problem. ...

I don't know why you're even discussing this. "volatile" qualification
was NOT INTENDED to serve as a mechanism in support of parallel threads
of execution accessing shared data. It was NOT INTENDED to guarantee
highly implementation-specific semantics concerning bus access etc.
Faulting it because it does not serve a function it was never designed
to serve is rather a waste of time. Such applications require a
different form of support, presumably via one or more conforming
extensions beyond the basic C standard.

John R. Levine

unread,
Aug 25, 1991, 11:43:44 AM8/25/91
to
In article <17...@smoke.brl.mil> gw...@smoke.brl.mil (Doug Gwyn) writes:
>In article <1991Aug24.1...@iecc.cambridge.ma.us> jo...@iecc.cambridge.ma.us (John R. Levine) writes:
>>I have long claimed, albeit not always very coherently, that volatile is a
>>snare and a delusion. It does seem to be useful for variables shared
>>between a mainline routine and a signal running on the same CPU, but for
>>memory mapped I/O and variables shared between multiple processors its
>>simplistic memory model just isn't adequate.

>
>I don't know why you're even discussing this. "volatile" qualification
>was NOT INTENDED to serve as a mechanism in support of parallel threads
>of execution accessing shared data. It was NOT INTENDED to guarantee
>highly implementation-specific semantics concerning bus access etc.
>Faulting it because it does not serve a function it was never designed
>to serve is rather a waste of time. Such applications require a
>different form of support, presumably via one or more conforming
>extensions beyond the basic C standard.

I agree that they require a different form of support, but the standard and
rationale make it pretty clear that "volatile" was indeed intended for both
I/O ports and shared data. A footnote in standard section 3.5.3, at least in
my December 1988 draft, reads: "A volatile declaration may be used to describe
an object corresponding to a memory-mapped input/output port ..." In the
rationale for that section, it says: "A static volatile object is an
appropriate model for a memory-mapped I/O register" and "A volatile object is
an appropriate model for a variable shared among multiple processes." If you
need extra implementation-specific stuff every time you use a volatile
variable, it doesn't seem to have been a very good addition to the language.
(Well, OK, a plain volatile is probably useful for variables shared between a
mainline and a signal handler, although given that things like i++ have race
problems on RISCs, I'm not sure I believe even that.)

In fairness, the rationale also mentions that implementations should document
things like whether they use read-modify-write instructions on volatile
variables, but if you don't like the choice that your implementor made, you
are stuck.

As Ken Thompson said in his paper on the Plan 9 C compiler, explaining its
divergences from ANSI: "Volatile seems to have no meaning, so it is hard to
tell if ignoring it is a departure from the standard."

To come back to comp.arch a little bit, I wonder if we understand memory
architectures well enough yet to be able to specify the needed semantics for
I/O port access and shared memory access (different issues, since the hardware
tends to synchronize them differently) in a coherent way. For I/O registers,
for example, is there some set of parameters such as access size, bus cycle
type, delay between access and whatever that would adequately describe an I/O
port so that an aggressive optimizing compiler would reliably know what it
could and could not do and still have its references to the port work? No
doubt a you can come up with a smallish set for any particular implementation,
the real question is whether there is a reasonable set that is useful for a
broad range of computers.

PS: Here ends the flaming, at least at my end.

Doug Gwyn

unread,
Aug 25, 1991, 3:38:30 PM8/25/91
to
In article <1991Aug25....@iecc.cambridge.ma.us> jo...@iecc.cambridge.ma.us (John R. Levine) writes:
>In article <17...@smoke.brl.mil> gw...@smoke.brl.mil (Doug Gwyn) writes:
>>... "volatile" qualification >was NOT INTENDED to serve as a mechanism

>>in support of parallel threads of execution accessing shared data. It
>>was NOT INTENDED to guarantee highly implementation-specific semantics
>>concerning bus access etc.
>I agree that they require a different form of support, but the standard and
>rationale make it pretty clear that "volatile" was indeed intended for both
>I/O ports and shared data.

Okay, I overstated it slightly. I meant that we thought that "volatile"
qualification might be NECESSARY for such applications; however, I don't
think the majority of X3J11 thought that it would really be SUFFICIENT
for them. As you pointed out (not quoted here), full semantics for such
things are quite complex and no simple universal language feature could
possibly cover all the bases.

>As Ken Thompson said in his paper on the Plan 9 C compiler, explaining its
>divergences from ANSI: "Volatile seems to have no meaning, so it is hard to
>tell if ignoring it is a departure from the standard."

Ken is very smart but also very opinionated. "Volatile" does have a
meaning, or we wouldn't have bothered with it. It can be safely ignored
by a conforming implementation only if the implementation treats ALL
object accesses as volatile-qualified; however, that precludes many
common optimization techniques. Just because "volatile" does not mean
what one might wish about memory-mapped I/O access does not imply that
it has no other meaning.

The main areas of the C standard where volatile qualification is relevant
are in the definitions of sig_atomic_t and setjmp().

Steve Correll

unread,
Aug 26, 1991, 4:30:35 PM8/26/91
to
>In article <GLEW.91Au...@pdx007.intel.com> gl...@pdx007.intel.com (Andy Glew) writes:
>
> mem->reg.byte0 = 0; mem->reg.byte1 = 0;
>[might be] combined by the compiler into a 16 bit store...
> So, what does [the] ANSI [volatile declaration] do?

In article <1991Aug24.1...@iecc.cambridge.ma.us> jo...@iecc.cambridge.ma.us (John R. Levine) writes:
>In my draft of the standard, section 2.1.2.3 says that "At sequence points,
>volatile objects are stable in the sense that previous evaluations are
>complete and subsequent evaluations have not yet occurred."

Because the end of the first statement constitutes a "sequence point", I
think you can make a pretty good argument that a compiler which combines
the two volatile byte stores into a 16-bit store has violated the ANSI
standard by either leaving the first evaluation incomplete or performing
the subsequent evaluation early. But the other criticisms of "volatile"
remain valid. System-specific needs call for system-specific solutions,
such as a pragma which says, "this is an I/O operation" and a code
generator which understands the requirements of I/O operations on the target
machine.

eric smith

unread,
Aug 26, 1991, 6:19:34 PM8/26/91
to

gw...@smoke.brl.mil (Doug Gwyn) writes:

>I don't know why you're even discussing this. "volatile" qualification
>was NOT INTENDED to serve as a mechanism in support of parallel threads
>of execution accessing shared data. It was NOT INTENDED to guarantee
>highly implementation-specific semantics concerning bus access etc.
>Faulting it because it does not serve a function it was never designed
>to serve is rather a waste of time. Such applications require a
>different form of support, presumably via one or more conforming
>extensions beyond the basic C standard.

Yes, that was my point when I posted the article that I think started
this thread. The point was that the use of "volatile" was not
sufficient to solve all the problems caused by using compiler
optimizations on kernel code. The example given was that of an SIO
chip which expected to receive a zero, and the compiler translated
"sio->reg = 0" into "xorb reg,reg" (I now believe that was the instruction
used). Instead of just a write operation, this instruction generated
a read/modify/write, which confused the SIO chip. Thus an example of a
situation not handled simply by defining a structure to be "volatile".

-----
Eric Smith
er...@sco.com
er...@infoserv.com
CI$: 70262,3610

Phil Ronzone

unread,
Sep 1, 1991, 2:57:03 PM9/1/91
to
In article <13...@scolex.sco.COM> er...@sco.COM (eric smith) writes:
>Yes, that was my point when I posted the article that I think
>started this thread. The point was that the use of "volatile"
>was not sufficient to solve all the problems caused by using
>compiler optimizations on kernel code. The example given was
>that of an SIO chip which expected to receive a zero, and the
>compiler translated "sio->reg = 0" into "xorb reg,reg" (I now
>believe that was the instruction used). Instead of just a
>write operation, this instruction generated a
>read/modify/write, which confused the SIO chip. Thus an
>example of a situation not handled simply by defining a
>structure to be "volatile".

There are problems which volatile still can not address.

For example, the 68000 and the NEC 765 floppy controller chip:

void reset_device()
{
char *iomap;

iomap = (char *)0x1000400;

*(iomap+2) = 0;
....
}

Doesn't work if you are attempting to send a zero byte to the 765
registers. The rational C compiler promptly generates

clr.b (a0)

which sends out on the memory bus READ-HALFWORD, WRITE-HALFWORD, instead of
writing a byte with the byte strobe lines set to the upper or lower
byte lane.

No C compiler construct can really handle that and the other zillions of
oddities that will occur with memory mapped I/O ports.

I DO want

*(iomap+2) = 0;
*(iomap+2) = 0;
*(iomap+2) = 0;
*(iomap+2) = 0;
...

not to be optimized out, which appears that volatile does just fine.
--
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
ph...@netcom.com (Phil Ronzone)

The difference between the USA and the USSR? Well, we have a communist
party and they don't ....

These opinions are MINE, and you can't have 'em! (But I'll rent 'em cheap ...)

John F Carr

unread,
Sep 1, 1991, 8:38:14 PM9/1/91
to
In article <1991Sep01.1...@netcom.COM> ph...@netcom.COM (Phil Ronzone) writes:

["*(volatile pointer) = 0" compiles to "clr.b (a0)"]

>which sends out on the memory bus READ-HALFWORD, WRITE-HALFWORD, instead of
>writing a byte with the byte strobe lines set to the upper or lower
>byte lane.

>No C compiler construct can really handle that and the other zillions of
>oddities that will occur with memory mapped I/O ports.

I disagree. gcc attempts to handle volatile such that memory mapped I/O
works. On a 68000 (but not 68020+), gcc will use move instead of clr to
store a 0 to a volatile memory location. gcc will not change the width of
a volatile memory reference, nor change the order of volatile memory
references. You need assembly code with gcc if a device has timing
constraints in the microsecond range (i.e. you need to wait between
accesses), but the more common memory mapped I/O problems are solved with
gcc "volatile".

ANSI C doesn't require that gcc act this way, but I think it is a good
implementation of "volatile".

--
John Carr (j...@athena.mit.edu)

Doug Gwyn

unread,
Sep 2, 1991, 6:13:25 PM9/2/91
to
In article <1991Sep2.0...@athena.mit.edu> j...@athena.mit.edu (John F Carr) writes:
>In article <1991Sep01.1...@netcom.COM> ph...@netcom.COM (Phil Ronzone) writes:
>>No C compiler construct can really handle that and the other zillions of
>>oddities that will occur with memory mapped I/O ports.
>I disagree. gcc attempts to handle volatile such that memory mapped I/O
>works.

I agree with Phil -- while the compiler can ATTEMPT to cover the most common
problems in using memory-mapped I/O from a HLL, the HLL (C in this case)
simply does not have enough facilities to express ALL the semantics that may
be relevant. On some architectures, for example, different modes of storing
into a "memory" location have different bus characteristics, which might
matter to a peripheral controller. Some DEC Unibus interfaces were very
sensitive to such "cycle" and "width" issues, and PDP-11 models sometimes
varied in these characteristics for nominally the SAME opcodes. I wrote a
fair number of PDP-11 device drivers, both in in MACRO-11 (assembler) and C,
and encountered several such situations. It really is not feasible for a
HLL compiler to go beyond a certain point of reasonableness in generating
code for such accesses. It sounds from your description that GCC is near
that point of maximum reasonableness, which improves the odds that it can
be used relatively painlessly to write device drivers, but does not and
in general cannot guarantee it.

AT&T's PCC (some PDP-11 editions, at least) recognized patent accesses to
addresses in the "I/O page" range and tried to avoid optimizing those.

>You need assembly code with gcc if a device has timing constraints in the

>microsecond range (i.e. you need to wait between accesses), ...

In my PDP-11 DZ11 (serial port multiplexer) driver, I found that the
receiver interrupt response code was not functioning up to expectations;
after fetching a character from the "silo" (input queue) and stashing it
in a kernel buffer, in order to avoid returning from the interrupt just
to have another interrupt occur, it tested a DZ11 register to see if any
more data happened to be ready for input. The problem was, the DZ11 silo
took some time (perhaps 20 usec, I forget the exact amount) before it was
ready to declare that the next character was available, even when it was
present in the silo. It so happened that the loop in my device driver
that tested the silo was faster than the silo itself, so it was never
seeing the next available character, so in fact it did return from the
interrupt just to get another immediately when data was coming in at
maximal rates. My solution to this was to add a "useless" instruction
(I think I shifted a register variable a couple of places), which on the
CPU model I was using consumed just enough time to allow the probe for
more silo data to work as intended. (This is also an example of a case
in which "busy waiting" style of delay is appropriate.) Fortunately I
was using Dennis Ritchie's C compiler, which avoided hyperoptimizing and
thereby eliminating code for the "useless" operation. In general it is
hard to get optimizing compilers to generate precisely the desired code
in such situations, and reverting to assembler for the critical code may
be the best choice.

Jim Cathey

unread,
Sep 3, 1991, 2:33:52 PM9/3/91
to
In article <1991Sep2.0...@athena.mit.edu> j...@athena.mit.edu (John F Carr) writes:
>In article <1991Sep01.1...@netcom.COM> ph...@netcom.COM (Phil Ronzone) writes:
>I disagree. gcc attempts to handle volatile such that memory mapped I/O
>works. On a 68000 (but not 68020+), gcc will use move instead of clr to
>store a 0 to a volatile memory location. gcc will not change the width of

Incidentally, the 68010 (not 68020) first fixed that bug of the 68000,
a CLR instruction does what is sensible. My drivers are filled with

zero = 0;
...
duart->wr.reg = zero;

in order to get around the CLR problem, because I had to code for both
68000 and 68010 CPUs. (The old compiler's optimizer wasn't smart enough
to turn this into CLR.)

+----------------+
! II CCCCCC ! Jim Cathey
! II SSSSCC ! ISC-Bunker Ramo
! II CC ! TAF-C8; Spokane, WA 99220
! IISSSS CC ! UUCP: uunet!isc-br!jimc (ji...@isc-br.isc-br.com)
! II CCCCCC ! (509) 927-5757
+----------------+
"PC's --- the junk bonds of the computer industry"

Norman Diamond

unread,
Sep 4, 1991, 4:09:09 AM9/4/91
to
I've added comp.std.c back into the distribution for this.

In article <1991Aug25....@iecc.cambridge.ma.us> jo...@iecc.cambridge.ma.us (John R. Levine) writes:
>If you need extra implementation-specific stuff every time you use a volatile
>variable, it doesn't seem to have been a very good addition to the language.

I have to agree with this. Although it is an exaggeration to say that
volatile has no meaning, it is fair to say that volatile has very little
useful application. The following, of course, it it:

>(Well, OK, a plain volatile is probably useful for variables shared between a
>mainline and a signal handler, although given that things like i++ have race
>problems on RISCs, I'm not sure I believe even that.)

Well, if a signal handler fetches the old value out of your static
sig_atomic_t variable i, the effect is undefined. While in the handler,
the standard only defines the effect of storing a value into i.

Incidentally, regarding the race condition of i++, CISCs could have the
same problem as RISCs. IBM 360, for example. (Also, even a machine with
read-modify-write would have the same race condition in a multiprocessing
configuration, although here we're just back to the general problem that
volatile failed to address.)
--
Norman Diamond dia...@jit081.enet.dec.com
If this were the company's opinion, I wouldn't be allowed to post it.
signature, n.: acm special interest group on studies of the real world.

Chris Craddock

unread,
Sep 4, 1991, 8:36:51 PM9/4/91
to
Norman Diamond writes:
|> [stuff deleted]

|> Incidentally, regarding the race condition of i++, CISCs could have the
|> same problem as RISCs. IBM 360, for example. (Also, even a machine with
^^^^^^^

|> read-modify-write would have the same race condition in a multiprocessing
|> configuration, although here we're just back to the general problem that
|> volatile failed to address.)
|> --
|> Norman Diamond dia...@jit081.enet.dec.com
|> If this were the company's opinion, I wouldn't be allowed to post it.
|> signature, n.: acm special interest group on studies of the real world.

The 360 hasn't been made since the late 1960's. The 370 which replaced
it added the Compare and Swap (CS), and Compare Double and Swap (CDS)
instructions. These are used to manage the concurrent update problem
in shared memory multi-processors.

It works just fine, not fast, but fine.

Chris.
--
"Garbage. This show would insult a 6 year old! and I should know"
- Calvin

cr...@hal.com (408) 379-7000 x1163

Doug Gwyn

unread,
Sep 5, 1991, 5:45:25 PM9/5/91
to
In article <1991Sep4.0...@tkou02.jit.dec.com> dia...@jit533.enet@tkou02.enet.dec.com (Norman Diamond) writes:
>I have to agree with this. Although it is an exaggeration to say that
>volatile has no meaning, it is fair to say that volatile has very little
>useful application.

So far as I am aware, "volatile" has exactly two standardly (i.e. portably)
useful sets of semantics, one for sig_atomic_t as you mentioned and the
other for autos in scope for setjmp(). Its other applications would also
depend on implementation-specific characteristics beyond what is specified
in the C standard.

John R. Levine

unread,
Sep 5, 1991, 3:02:02 PM9/5/91
to
In article <1991Sep5.0...@hal.com> cr...@hal.com (Chris Craddock) writes:

>Norman Diamond wrote, commenting on something I wrote:
>|> Incidentally, regarding the race condition of i++, CISCs could have the
>|> same problem as RISCs. IBM 360, for example. ...

> The 360 hasn't been made since the late 1960's. The 370 which replaced it
> added the Compare and Swap (CS), and Compare Double and Swap (CDS)
> instructions. These are used to manage the concurrent update problem in
> shared memory multi-processors.

Indeed, that's the nub of the "volatile" problem. CS is a dandy
synchronization mechanism, much more powerful than the 360's test and set
or the unconditional exchange used in other systems. (We had a discussion
in comp.arch a few months ago about the relative power of synchronization
mechanisms, that's not the issue here.) There is no way in standard C to
express a compare and swap or any other such mechanism. In the most
conservative case, a compiler might use compare and swap every time it
changed a volatile variable, but that's incredible overkill if you're just
using it to pass a flag to a signal handler.

To return this to comp.arch, I wonder whether we understand the various
synchronization primitives and memory architectures to design language
features along the line of volatile that are reasonably portable and
adequate to handle synchronization of multiprocessors and of memory
mapped I/O devices.

David Adrien Tanguay

unread,
Sep 5, 1991, 6:49:35 AM9/5/91
to
dia...@jit533.enet@tkou02.enet.dec.com (Norman Diamond) writes:
>I've added comp.std.c back into the distribution for this.
>I have to agree with this. Although it is an exaggeration to say that
>volatile has no meaning, it is fair to say that volatile has very little
>useful application. The following, of course, it it:
>
> [shared variables in signal handlers]

The way things are defined, volatile is also useful with setjmp/longjmp.
I think I would prefer a sync_and_longjmp().
--
David Tanguay Thinkage, Ltd., Kitchener, Ontario, Canada [43.47N 80.52W]
d...@Thinkage.on.ca uunet!thinkage!dat

Henry Spencer

unread,
Sep 5, 1991, 1:11:45 PM9/5/91
to
In article <1991Sep01.1...@netcom.COM> ph...@netcom.COM (Phil Ronzone) writes:
>There are problems which volatile still can not address.
>For example, the 68000 and the NEC 765 floppy controller chip:
> ...
>Doesn't work if you are attempting to send a zero byte to the 765
>registers. The rational C compiler promptly generates
>
> clr.b (a0)

This means either (a) this compiler has a definition of `volatile' which
is unsuited to your purposes, or (b) its implementation of `volatile'
contains a bug. The meaning of `volatile' is necessarily somewhat
implementation-specific. Quality documentation for a compiler on such
a machine will specify whether it generates read-modify-write operations
for such code. (Yes, this may mean that you have to tell it which exact
flavor of 68k processor you have; you probably want to do that anyway
so it can do flavor-specific optimizations.) If it does, then I'd call
it a pretty sleazy compiler and unsuited to writing device drivers in
particular. If it doesn't, then `volatile' *does* address the problem,
and in fact solves it.

Don't confuse bugs in your compiler or its design with flaws in the concept.
It *is* true that there are problems that `volatile' does not address, but
this is not among them.
--
Any program that calls itself an OS | Henry Spencer @ U of Toronto Zoology
(e.g. "MSDOS") isn't one. -Geoff Collyer| he...@zoo.toronto.edu utzoo!henry

Doug Gwyn

unread,
Sep 6, 1991, 4:20:21 PM9/6/91
to
In article <1991Sep05....@iecc.cambridge.ma.us> jo...@iecc.cambridge.ma.us (John R. Levine) writes:
>To return this to comp.arch, I wonder whether we understand the various
>synchronization primitives and memory architectures to design language
>features along the line of volatile that are reasonably portable and
>adequate to handle synchronization of multiprocessors and of memory
>mapped I/O devices.

I asked Dennis Ritchie about this many years ago and he told me he
hadn't figured out a good solution (within the context of C). I never
have either. As I see it, the main problem is that the hardware
support being provided by various vendors (a) varies radically, so
that a single universal model will not be a good fit for many systems
and (b) is NOT what one ideally would want for the purpose.

Certainly that would argue that standardization of such facilities is
incredibly premature.

John Bowler

unread,
Sep 6, 1991, 1:41:25 PM9/6/91
to
In article <1991Sep5.1...@zoo.toronto.edu> he...@zoo.toronto.edu (Henry Spencer) writes:
>In article <1991Sep01.1...@netcom.COM> ph...@netcom.COM (Phil Ronzone) writes:
>>... The rational C compiler promptly generates

>>
>> clr.b (a0)
>
>This means either (a) this compiler has a definition of `volatile' which
>is unsuited to your purposes, or (b) its implementation of `volatile'
>contains a bug. The meaning of `volatile' is necessarily somewhat
>implementation-specific. Quality documentation for a compiler on such
>a machine will specify whether it generates read-modify-write operations
>for such code. (Yes, this may mean that you have to tell it which exact
>flavor of 68k processor you have; you probably want to do that anyway
>so it can do flavor-specific optimizations.) If it does, then I'd call
>it a pretty sleazy compiler and unsuited to writing device drivers in
>particular.

This seems somewhat overstated; there are some combinations of architectures
and operations which require the compiler to do a read, modify, write
sequence of operations. The compiler writer is entitled to assume that the
IO code writer reads the compiler documentation and understands the limits
of the architecture. For a real example consider:-

volatile struct { int a:2; int b:4; int b:2 } device_reg;

... device_reg.b = 5; ...

Ok, it's a silly example, no competent device driver writer would expect
this to work in any particular way; but other operations do not necessarily
work in the obvious way:-

volatile short *device_ptr;

... *device_ptr = 22; ...

has to be done as two byte write operations on the ARM (a short is
conventionally 16 bits, but there are no short load/store operations).

Personally I would say that it is perfectly reasonable for a compiler
implementation to be unsuitable for writing device drivers, as long as this
is clear from the documentation.

I don't seen volatile as fulfilling the requirements of device driver
writers - after all, some systems use special instructions which no sensible
compiler will generate unless told to explicitly. Surely all volatile
prevents is the ``relocation'' of lvalues in certain specific cases? In
particular it prevents a compiler from temporarily storing an lvalue in a
register (or, indeed, any location other than the real one), so:-

int a; /* Compiler allocates space for a on the stack (eg) */

...
a = 56; /* Compiler can decide to store a in a machine
register... */

func(); /* a is not aliased (ie not address taken) so it
* can ``stay'' in the register */
/* Maybe the compiler decides to write a back to the stack now */

is an illegal interpretation when ``volatile int a;''.

Why, I wonder, should anyone think that this useful functionality is going
to solve all the problems of access to memory mapped IO? Indeed, this only
prevents those relocations (...there must be a proper word for this...) over
which the compiler has control. It can do nothing if a the hardware allows
the same lvalue to exist in two places at once, as happens, for example,
with writeback hardware caches on MP systems.

John Bowler (jbo...@acorn.co.uk)

Henry Spencer

unread,
Sep 7, 1991, 7:07:42 PM9/7/91
to
In article <96...@acorn.co.uk> jo...@acorn.co.uk (John Bowler) writes:
>>This means either (a) this compiler has a definition of `volatile' which
>>is unsuited to your purposes, or (b) its implementation of `volatile'
>>contains a bug. The meaning of `volatile' is necessarily somewhat
>>implementation-specific. Quality documentation for a compiler on such
>>a machine will specify whether it generates read-modify-write operations
>>for such code...

>
>This seems somewhat overstated; there are some combinations of architectures
>and operations which require the compiler to do a read, modify, write
>sequence of operations...

"Warning: cannot implement operation without read-modify-write sequence."
If the programmer is asking for something that cannot be done, he should
be told about the situation.

>The compiler writer is entitled to assume that the
>IO code writer reads the compiler documentation and understands the limits

>of the architecture...

Quite true. On the other hand, the compiler writer is legitimately charged
with being as helpful to the IO code writer as possible, assuming that this
is an intended use of the particular compiler. Quietly generating surprising
code, when the writer has used `volatile' to request an absence of surprises,
is a bug, not a feature.

>Personally I would say that it is perfectly reasonable for a compiler
>implementation to be unsuitable for writing device drivers, as long as this
>is clear from the documentation.

This I agree with. See my point (a), reproduced above.

>I don't seen volatile as fulfilling the requirements of device driver
>writers - after all, some systems use special instructions which no sensible
>compiler will generate unless told to explicitly. Surely all volatile
>prevents is the ``relocation'' of lvalues in certain specific cases?

You're confusing guaranteed behavior with permissible behavior. The main
point of `volatile' is to provide a standard way for the writer to tell the
compiler "no surprises on this one, please". The nature of surprises,
and how far they can be avoided, is implementation-specific. `Volatile'
prevents whatever the compiler writer wants it to prevent; how well that
matches what the code writer wants is his business when he's shopping for
a compiler.

(There are one or two places where ANSI C actually does make `volatile'
guarantee specific behavior in a portable way; I ignore those for the
purposes of this discussion, since they are relatively minor points.)

>... it prevents a compiler from temporarily storing an lvalue in a
>register (or, indeed, any location other than the real one)...

Unless your code does something that involves undefined or implementation-
defined behavior, there is no way you can tell whether the compiler is
doing this. `volatile' would not be needed just for this (modulo those
one or two minor points alluded to above).

>Why, I wonder, should anyone think that this useful functionality is going
>to solve all the problems of access to memory mapped IO?

Nobody would. However, it can make a major contribution to solving some
of the most important ones, if the compiler implementor wants it to.
People who claim "volatile doesn't do that" are ignoring the experience of
people like Mips, who have found that it is very useful for exactly that.

John Bowler

unread,
Sep 9, 1991, 2:50:40 PM9/9/91
to
In article <1991Sep7.2...@zoo.toronto.edu> he...@zoo.toronto.edu (Henry Spencer) writes:
>In article <96...@acorn.co.uk> jo...@acorn.co.uk (John Bowler) writes:
>>>This means either (a) this compiler has a definition of `volatile' which
>>>is unsuited to your purposes, or (b) its implementation of `volatile'
>>>contains a bug. The meaning of `volatile' is necessarily somewhat
>>>implementation-specific. Quality documentation for a compiler on such
>>>a machine will specify whether it generates read-modify-write operations
>>>for such code...
>>
>>This seems somewhat overstated; there are some combinations of architectures
>>and operations which require the compiler to do a read, modify, write
>>sequence of operations...
>
>"Warning: cannot implement operation without read-modify-write sequence."
>If the programmer is asking for something that cannot be done, he should
>be told about the situation.

Why, in the following fragment (headers omitted):-

int func(void) {
volatile struct {int a:2, b:4, c:2; } mystruct;
jmp_buf state;

mystruct.b = 22;
setjmp(&state);
..<uses of mystruct/function calls>..
if (condition) mystruct.b = 23;
..<uses of mystruct/function calls>..


should the programmer wish the assignment to mystruct.b to necessarily
be performed without a read-modify-write sequence? The standard says
(section 4.6.2.1, page 120 lines 11-14, description of longjmp):-

``All accessible objects have values as of the time longjmp
was called, except that the values of objects of automatic
storage duration that are local to the function containing
the invocation of the corresponding setjmp macro that do not
have volatile-qualified type and have been changed between the
setjmp invocation and the longjmp call are indeterminate.''

Which is clear and unambiguous - ``mystruct'' must be declared volatile.
(This fixes various bugs/botches/features of earlier C compilers which
rendered automatic variables unuseable in a portable fashion within
procedures which called setjmp.)

>>The compiler writer is entitled to assume that the
>>IO code writer reads the compiler documentation and understands the limits
>>of the architecture...
>
>Quite true. On the other hand, the compiler writer is legitimately charged
>with being as helpful to the IO code writer as possible, assuming that this
>is an intended use of the particular compiler.

In principle I agree.

>Quietly generating surprising
>code, when the writer has used `volatile' to request an absence of surprises,
>is a bug, not a feature.

But I do not agree that this is (always) a sensible interpretation of
volatile. In particular code like the above example may well be compiled
by the same compiler. volatile is performing two functions, quoting section
3.5.3 (type qualifiers) page 65, lines 16-19:-

``An object that has volatile-qualified type may be modified
in ways unknown to the implementation or have other unknown
side effects. Therefore any expression referring to such
an object shall be evaluated strictly according to the rules
of the abstract machine, as described in section 2.1.2.3.''

The above code is one example of ``modification in a way unknown to the
implementation'' (what is modified is the processors flow of control, but
the same effect is achieved). Another example is asynchronous modification
of a static variable by a signal handler (...really another example of
alteration of processor flow of control - through the code of the signal
handler!) As far as I can see 2.1.2.3 makes no statement about *how*
assignment is performed, or of atomicity of assignment.

Indeed, the section on signal (4.7, lines 4-7, page 121) does not use
volatile:-

``The type defined is

sig_atomic_t

which is an integral type of an object that can be accessed as an
atomic entity, even in the presence of asynchronous interrupts.''

So, as a device writer, I might reasonably expect:-

sig_atomic_t *device_ptr;

... *device_ptr = 42; ...

to result in an atomic update, but, even then, a read-modify-write
instuction could be used (eg test-and-set).

Now, when I want to write a device driver with a compiler which provides
sufficient support I expect, following from the way the standard does
it for signal(), to be given an appropriate type. Either the documentation
(operating system or compiler as appropriate) says (eg):-

``To access the 16 bit memory mapped IO device space use
a type of volatile int''

or, if this is inappropriate:-

``To access the 16 bit memory mapped IO device space use
a type of _device16 and include <sys/device.h>''

I don't mind the non-portability. I gave up on that as soon as I wrote:-

static _device16 *myptr = 0xffff0008;

and I can veneer this with a portable typedef based interface, if I really
want to.

>>I don't seen volatile as fulfilling the requirements of device driver
>>writers - after all, some systems use special instructions which no sensible
>>compiler will generate unless told to explicitly. Surely all volatile
>>prevents is the ``relocation'' of lvalues in certain specific cases?
>
>You're confusing guaranteed behavior with permissible behavior. The main
>point of `volatile' is to provide a standard way for the writer to tell the
>compiler "no surprises on this one, please".

I don't agree. That is *not* the main point of volatile; the purpose of
volatile is defined and guaranteed by the standard. These are not ``minor
points'' (as you suggest later) - they were a cause of problems in a wide
range of code which does not access ``memory-mapped input/output ports''
(this is in the *footnote* on page 65).

>The nature of surprises,
>and how far they can be avoided, is implementation-specific. `Volatile'
>prevents whatever the compiler writer wants it to prevent; how well that
>matches what the code writer wants is his business when he's shopping for
>a compiler.

Here too I disagree. `An object that has volatile-qualified type may be
modified in ways unknown to the implementation' (which I regard as the
important part of the definition) `or have other unknown side effects'
(which, surely, is the cause of this thread - this clause can mean all
things to all people).

>>... it prevents a compiler from temporarily storing an lvalue in a
>>register (or, indeed, any location other than the real one)...
>
>Unless your code does something that involves undefined or implementation-
>defined behavior, there is no way you can tell whether the compiler is
>doing this. `volatile' would not be needed just for this (modulo those
>one or two minor points alluded to above).

Well, we disagree on the use of the word ``minor''. How about ``volatile
is needed to prevent the writer of conformant ANSI code from detecting
that the compiler is performing optimisations''. To give one example:-

#include <signal.h>
static /*volatile*/ sig_atomic_t a;

void sfunc(int sig) { a = 22; }

int main(void) {
signal(SIGINT, sfunc);
a = 23;
while (a==23);
}

Now, this is *almost* strictly ANSI conformant (I think :-). On a given ANSI
conformant implementation it might or might not terminate. It depends on:-

1) Whether the implementation permits SIGINT to be generate asynchronously
(the one I am using does).
2) Whether the compiler chooses to ``optimise'' the fatuous while loop
(the one I am using does - the program does not terminate on SIGINT).

*EXCEPT* of course, section 4.7, page 122 starting at line 111, which says,
shortened:-

``If the signal occurs other than as a result of calling the
abort or raise function, the behaviour is *undefined* if the
signal handler...refers to any object with static storage
duration other than by assigning a value to a static storage
duration variable of type volatile sig_atomic_t.''

So, I can tell if the compiler is doing this optimisation; and I can
tell even in the case where I write the conformant program with:-

static volatile sig_atomic_t a;

but of course, in this case, if I can tell then the system has a bug. All
your statement is saying is that the ANSI standard is well written - it
*does* permit the compiler to do optimisations in such a way that a
conformant program cannot detect the optimisation. But that *is* a
justification for this restricted interpretation of volatile - this
is the only interpretation which is *necessary* to provide a conformant
and genuinely useful optimising compiler.

>>[my comment about solving all the problems of device writing with volatile]


>Nobody would. However, it can make a major contribution to solving some
>of the most important ones, if the compiler implementor wants it to.
>People who claim "volatile doesn't do that" are ignoring the experience of
>people like Mips, who have found that it is very useful for exactly that.

Ok. How about ``the volatile in the ANSI C standard doesn't do that''.
That doesn't mean that any particular compiler can't, or that another
compiler (also suitable for writing device drivers) should be regarded
as inferior because it uses another method.

John Bowler (jbo...@acorn.co.uk)

Jim Cathey

unread,
Sep 10, 1991, 11:50:42 AM9/10/91
to
In article <1991Sep4.0...@tkou02.jit.dec.com> dia...@jit533.enet@tkou02.enet.dec.com (Norman Diamond) writes:
>Incidentally, regarding the race condition of i++, CISCs could have the
>same problem as RISCs. IBM 360, for example. (Also, even a machine with
>read-modify-write would have the same race condition in a multiprocessing
>configuration, although here we're just back to the general problem that
>volatile failed to address.)

Unless, of course, that the processor(s) involved implement a bus lock
on RMW instructions, which is the only thing that makes such instructions
at all useful in a multiprocessor installation. That's up to the hardware
designer, though.

The 68000 family, for example, has RMW instructions at the programmer's
level (add, subtract, rotate), and RMW instructions at the hardware
level (TAS, CAS, CAS2). Only the hardware-level instructions can be
used for multiprocessor synchronization, and only if the hardware
design obeys the bus lock that's requested. The other instructions can
be used by single threads (as they are uninterruptable at the software
level --- i.e. regular interrupts) provided a bus error is avoided on
the W part of the cycle (protection violation). Such an error
situation is very contrived, and probably not seen in practice. No C
compiler I know of uses TAS and friends for anything.

John R. Levine

unread,
Sep 11, 1991, 9:58:21 PM9/11/91
to
In article <1991Sep7.2...@zoo.toronto.edu> he...@zoo.toronto.edu (Henry Spencer) writes:

I certainly wouldn't. Unfortunately there is a footnote in the ANSI C
standard and a section in the rationale that specifically say that
volatile is suitable for memory mapped I/O.

>People who claim "volatile doesn't do that" are ignoring the experience of
>people like Mips, who have found that it is very useful for exactly that.

That's nice. They are fortunate that they have a combination of hardware
and software with clean enough semantics that volatile is adequate to
describe I/O registers. Some of us old fogeys wrote PDP-11 device drivers
using the Ritchie C compiler which didn't even have unions, much less
volatile. That only shows that some implementations of C are useful for
writing I/O drivers.

The rest of us have to contend with devices that have only partially
memory-like semantics and do something unfortunate on a read-modify-write
generated by a clear instruction, or that do various funky things on byte
accesses to a word register. In situations like that, volatile is
essentially worthless since code that uses it depends critically on warts
of the local implementation. It really doesn't matter whether you use
"volatile" or "foonly" to describe your device registers, since the code
will be equally compiler specific and unportable either way. At least if
you use "foonly" the code won't silently fail when run through another
compiler.

As I mentioned before, volatile variables in portable programs are useful
for dealing with setjmp() and possibly useful for communicating with
signal handlers, modulo update races. For I/O registers, I think it would
have been more sensible to have a "device" declarator with completely
implementation-defined semantics rather than trying to overload volatile
and cross our fingers and hope.

Henry Spencer

unread,
Sep 12, 1991, 1:03:05 PM9/12/91
to
In article <1991Sep12.0...@iecc.cambridge.ma.us> jo...@iecc.cambridge.ma.us (John R. Levine) writes:
>>People who claim "volatile doesn't do that" are ignoring the experience of
>>people like Mips, who have found that it is very useful for exactly that.
>
>... They are fortunate that they have a combination of hardware

>and software with clean enough semantics that volatile is adequate to
>describe I/O registers...

>The rest of us have to contend with devices that have only partially
>memory-like semantics and do something unfortunate on a read-modify-write
>generated by a clear instruction, or that do various funky things on byte
>accesses to a word register. In situations like that, volatile is
>essentially worthless since code that uses it depends critically on warts
>of the local implementation...

John, such code *always* depends on details of the local implementation,
by definition -- the hardware cannot help but be implementation-specific!
There is little possibility of writing portable code that manipulates a
specific piece of hardware.

My point was that *if* your compiler is done right, volatile *can* be
very helpful for this, and there is operational experience to prove this.
Yes, even with ill-behaved devices. A done-right compiler does not emit
a read-modify-write instruction for a write to a volatile. A done-right
compiler does not generate a byte access if you asked for a word-long
volatile. If your compiler is not done right, buy one that is.

You are always at the mercy of your compiler writer. If you assume
sufficient brain damage on his part, no feature of the compiler will
ever be useful. The solution is not to buy brain-damaged compilers.
The problems you cite are compiler problems, not language defects.

>As I mentioned before, volatile variables in portable programs are useful
>for dealing with setjmp() and possibly useful for communicating with

>signal handlers, modulo update races...

Quite true. What does this have to do with writing I/O drivers? Are
you assuming, for some reason, that you must use the same compiler (and
the same compiler options) for both? Why?

Actually, there isn't even a problem if you do use the same compiler and
options for both. There is no dire conflict between these three uses.
Device registers have more stringent requirements than the other two
applications, and it may be useful to have a compiler option that says
"I'm not using device registers, so you can ease up a bit", but this
is hardly a crippling problem.

>have been more sensible to have a "device" declarator with completely
>implementation-defined semantics rather than trying to overload volatile
>and cross our fingers and hope.

"volatile" was invented for device registers. The mistake was to overload
it for setjmp and signal handlers. A wise compiler writer, writing for
the systems-programming market, will know this and do things right.
--
Programming graphics in X is like | Henry Spencer @ U of Toronto Zoology
finding sqrt(pi) using Roman numerals. | he...@zoo.toronto.edu utzoo!henry

Henry Spencer

unread,
Sep 12, 1991, 1:12:37 PM9/12/91
to
In article <96...@acorn.co.uk> jo...@acorn.co.uk (John Bowler) writes:
>>"Warning: cannot implement operation without read-modify-write sequence."
>>If the programmer is asking for something that cannot be done, he should
>>be told about the situation.
>
>Why, in the following fragment (headers omitted):-
> volatile struct {int a:2, b:4, c:2; } mystruct;
> setjmp(&state);

>should the programmer wish the assignment to mystruct.b to necessarily
>be performed without a read-modify-write sequence?

Because he's passed a pointer to that structure to another process, which
is running on another processor and is perfectly capable of sneaking its
own r-m-w in between the read and the write.

If he hasn't taken the address, or if the environment the compiler is
generating code for is single-threaded, then of course there is no reason
to worry about it and no reason to generate such a warning, because a
r-m-w reference to a local variable isn't going to cause problems.

>(This fixes various bugs/botches/features of earlier C compilers which
>rendered automatic variables unuseable in a portable fashion within
>procedures which called setjmp.)

Actually, you don't need volatile to fix this, just sensible compiler
writers. Any compiler that is smart enough to be promoting things to
registers without being asked is also smart enough to notice the use
of setjmp() and refrain from doing so. (ANSI C carefully restricts
use of setjmp() to make this possible, in fact, but they didn't feel
brave enough to draw the obvious conclusion that volatile is not
needed for this application.)

John Bowler

unread,
Sep 13, 1991, 3:44:44 PM9/13/91
to
In article <1991Sep12....@zoo.toronto.edu> he...@zoo.toronto.edu (Henry Spencer) writes:
>In article <96...@acorn.co.uk> jo...@acorn.co.uk (John Bowler) writes:
>>>"Warning: cannot implement operation without read-modify-write sequence."
>>>If the programmer is asking for something that cannot be done, he should
>>>be told about the situation.
>>
>>Why, in the following fragment (headers omitted):-
>> volatile struct {int a:2, b:4, c:2; } mystruct;
>> setjmp(&state);
>>should the programmer wish the assignment to mystruct.b to necessarily
^^^^^^^^^^^

>>be performed without a read-modify-write sequence?
>
>Because he's passed a pointer to that structure to another process, which
>is running on another processor and is perfectly capable of sneaking its
>own r-m-w in between the read and the write.

I included that ``necessarily'' with malice aforethought. You give a reason
why a programmer *might* want to avoid r-m-w. That does not mean that the
programmer does, yet the programmer *must* put volatile on the struct
declaration to have valid ANSI (ISO?) C code.

To deal with the example you give (the MP asynchronous update problem) many
(possibly most) systems must generate code which bypasses any per-processor
caches. Certainly avoiding r-m-w is not necessary to deal with the problem
- r-m-w bus locked access is quite standard on systems designed for MP use
(compare&swap, test&set). As has been pointed out recently on comp.arch
however *some* systems (the 68k was the example) provide two sorts of
instructions doing exactly the same thing at the level of the individual
thread, but one of which bypasses the cache or uses bus locking. The
``safe'' instructions can be abysmally slow compared to the real ones - on
MP systems they often involve considerable synchronisation delay in
additional to the obvious overhead of accessing real memory.

IMHO compilers should never generate these slower instructions unless told
to do so very explicitly (eg __avoid_the_cache, __use_rmw, or, preferably
__testnset(&var, val), __writepassingcache(&var, val)).

There really are at least two requirements here. I don't much care what
the mechanisms which deal with them are, so long as these mechanisms are
independent of each other and the one which deals with the problem which
can be dealt with portably is specified portably.

>If he hasn't taken the address, or if the environment the compiler is
>generating code for is single-threaded, then of course there is no reason
>to worry about it and no reason to generate such a warning, because a
>r-m-w reference to a local variable isn't going to cause problems.

Personally I would rather the compiler didn't know that the environment
I use is single threaded, then it won't make assumptions which become
invalid when it ceases to be.

>>(This fixes various bugs/botches/features of earlier C compilers which
>>rendered automatic variables unuseable in a portable fashion within
>>procedures which called setjmp.)
>
>Actually, you don't need volatile to fix this, just sensible compiler
>writers. Any compiler that is smart enough to be promoting things to
>registers without being asked is also smart enough to notice the use
>of setjmp() and refrain from doing so. (ANSI C carefully restricts
>use of setjmp() to make this possible, in fact, but they didn't feel
>brave enough to draw the obvious conclusion that volatile is not
>needed for this application.)

Yes. We did this to support pcc style code from a basically ANSI compiler.
The general and correct solution is to recognise that control flow after a
call to setjmp is indeterminate (basically put a label on the setjmp return
and assume any function call might be a branch to that label). I can't
remember if we ever got it to work, it was certainly didn't the first time.
The easier but non-optimal solution is to hack a ``volatile'' onto any local
variable without the ``register'' attribute. This is what you suggest -
but notice that it requires that the compiler writer actually implement
volatile (with this meaning) even though explicit volatile is no longer
required in the setjmp case!

It also causes problems for compliance with other standards. XPG3 has the
sigsetjmp/siglongjump interface which has to be treated in the same way,
BSD has _setjmp. In the end the compiler writer adds a general purpose,
but implementation specific facility, ``#pragma unknown_control_flow(blah
blah blah)'' maybe? Then you have to go away and deal with signals too -
notice that, in this case, ANSI made the additional facility required
(sig_atomic_t) be part of the library spec rather than the language.

In the end both I and John Levine are saying that volatile has one meaning
and that the other meanings should be implementation specific extensions,
whereas you are saying that volatile has another meaning and that the
``unexpected modification'' meaning should be handled in an implementation
specific way (eg by recognising setjmp/longjmp).

John Bowler (jbo...@acorn.co.uk)

Henry Spencer

unread,
Sep 16, 1991, 3:18:40 PM9/16/91
to
In article <97...@acorn.co.uk> jo...@acorn.co.uk (John Bowler) writes:
>In the end both I and John Levine are saying that volatile has one meaning
>and that the other meanings should be implementation specific extensions...

I'm saying that volatile was meant specifically for handling things like
device registers, a conclusion which is trivial to draw if you either
(a) know its history or (b) read the ANSI C Rationale (which, incidentally,
explicitly mentions both the byte-operation and r-m-w issues). "Whatever
decisions are adopted on such issues must be documented, as volatile access
is implementation-defined." (Rationale 3.5.3.) The additional meanings
are trivial bonuses (or trivial misuses, depending on your viewpoint).

>...whereas you are saying ... that the


>``unexpected modification'' meaning should be handled in an implementation
>specific way (eg by recognising setjmp/longjmp).

Please don't put words in my mouth. What I'm saying is that the committee
made a mistake in using volatile for dealing with setjmp/longjmp, as the
desired behavior can be implemented without it (and in fact generally must
be, to avoid breaking vast masses of existing code). It is, unfortunately,
true that portable code must now use volatile for this purpose, given the
committee's mistake and the unlimited stupidity of some compiler writers.
My point is that this is a side issue; this was not the purpose of volatile.

0 new messages