[comp.lang.c] *devaddr = 0 and volatile

chris@mimsy.UUCP (Chris Torek) (12/04/88)

>In article <21560@apple.Apple.COM> desnoyer@Apple.COM (Peter Desnoyers) writes:
>>pet peeve - It is a common thing to have to write a zero to a device
>>register. The obvious code, (* reg_addr) = 0, often results in:
>>  xor reg_addr, reg_addr

I would say not `often' but `sometimes'.

In article <13784@oberon.USC.EDU>, blarson@skat.usc.edu (Bob Larson) writes:
>... Most 68000 compilers would use the clr instruction, which does have
>the undesired side effect of doing an (ignored) read though.

(True only on the 000; the 010 and 020, and presumably 030, clr does not
read before write.)

>>Unfortunately I don't think specifying bus semantics is within the
>>purview of the ANSI committee ...

This part is true.

>>and volatile is not sufficient to force the desired activity.

>Fortunatly you are wrong, and volitile is sufficient.

Volatile (note the spelling: compilers will not accept the `volitile'
non-keyword) does imply---I will not use the word `guarantee' without
checking the exact wording---that using an XOR or read-before-write-CLR
instruction to clear a volatile location is incorrect.  Consider,
however, a machine which is only word-addressible, on which byte
references compile to shift-and-mask sequences.  I do not know what
the compiler must or even should do if given the following code:

	struct foodev { char c1, c2; };
	f() {
		register volatile struct foodev *p;
		...	/* set up p */
		p->c1 = 0;
	}

The only way to set p->c1=0 is for the compiler to use a sequence like

	load	0(rP),r1
	and	#ff00,r1,r1
	store	r1,0(rP)

I would suggest that the compiler complain about volatile references
that it cannot compile `properly'.
-- 
In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7163)
Domain:	chris@mimsy.umd.edu	Path:	uunet!mimsy!chris

gwyn@smoke.BRL.MIL (Doug Gwyn ) (12/04/88)

In article <14832@mimsy.UUCP> chris@mimsy.UUCP (Chris Torek) writes:
>I would suggest that the compiler complain about volatile references
>that it cannot compile `properly'.

Sounds like a reasonable suggestion to me.
After all, if the programmer specified "volatile" it is safe to
assume that he had a reason for doing so, and if it can't be honored
then something is probably going to break.

desnoyer@Apple.COM (Peter Desnoyers) (12/06/88)

In article <9059@smoke.BRL.MIL> gwyn@brl.arpa (Doug Gwyn (VLD/VMB) <gwyn>) writes:
>In article <14832@mimsy.UUCP> chris@mimsy.UUCP (Chris Torek) writes:
>>I would suggest that the compiler complain about volatile references
>>that it cannot compile `properly'.
>
>Sounds like a reasonable suggestion to me.
>After all, if the programmer specified "volatile" it is safe to
>assume that he had a reason for doing so, and if it can't be honored
>then something is probably going to break.

The uncompilable example given was 'volatile char foo' on a
word-oriented machine. If 'foo' is a hardware register, methinks a
compiler error message is insufficient. You really should fire the
person who designed such a f***-up. It ranks up there with write-only
memory. 1/2 :-) 

				Peter Desnoyers

tim@crackle.amd.com (Tim Olson) (12/06/88)

In article <21686@apple.Apple.COM> desnoyer@Apple.COM (Peter Desnoyers) writes:
| In article <9059@smoke.BRL.MIL> gwyn@brl.arpa (Doug Gwyn (VLD/VMB) <gwyn>) writes:
| >In article <14832@mimsy.UUCP> chris@mimsy.UUCP (Chris Torek) writes:
| >>I would suggest that the compiler complain about volatile references
| >>that it cannot compile `properly'.
| >
| >Sounds like a reasonable suggestion to me.
| >After all, if the programmer specified "volatile" it is safe to
| >assume that he had a reason for doing so, and if it can't be honored
| >then something is probably going to break.

I was speaking to Richard Relph about this subject, and he brought up
the problem of unaligned accesses through a pointer to a volatile object
which is larger than a char, but is not properly aligned [what a
mouthful!].  These also require multiple accesses to be performed
"correctly."  What should be done here?

	-- Tim Olson
	Advanced Micro Devices
	(tim@crackle.amd.com)

gwyn@smoke.BRL.MIL (Doug Gwyn ) (12/07/88)

In article <23706@amdcad.AMD.COM> tim@crackle.amd.com (Tim Olson) writes:
>I was speaking to Richard Relph about this subject, and he brought up
>the problem of unaligned accesses through a pointer to a volatile object
>which is larger than a char, but is not properly aligned [what a
>mouthful!].  These also require multiple accesses to be performed
>"correctly."  What should be done here?

You're already in the realm of entirely implementation-dependent behavior.
There is no way to obtain via pure C constructs such a pointer along with
a guarantee that it can be used to access something.  I would recommend
that the implementation warn about the attempted misaligned access in such
a case, IF it can readily determine that this is being attempted.  Whether
it should generate code to access the object piece by piece is debatable;
it's not required by the Standard and would impose a large burden to
support in most implementations.

"volatile" really is not an issue here.  Sequential access of multiple
portions of an object does not violate the requirements of the Standard
(section 2.1.2.3).  For objects specified as "volatile", failure to
access or multiple accesses -- when not specified as such for the
abstract machine in the source code -- would be a violation.

P.S. The above is my own interpretation, not necessarily X3J11's.

henry@utzoo.uucp (Henry Spencer) (12/07/88)

In article <23706@amdcad.AMD.COM> tim@crackle.amd.com (Tim Olson) writes:
>...the problem of unaligned accesses through a pointer to a volatile object
>which is larger than a char, but is not properly aligned...
>  What should be done here?

"Bus error -- core dumped."  :-)

More seriously, it's really not entirely clear just how strong a guarantee
of atomicity you get from X3J11's words about volatile.  I doubt that the
rules are intended to forbid pdp11 floating point, which necessarily uses
multiple accesses per object because no pdp11 has a 64-bit memory bus.
I think you have to consider volatile as meaning "no surprises" -- is
it really a surprise that a large unaligned object requires more than one
access?
-- 
SunOSish, adj:  requiring      |     Henry Spencer at U of Toronto Zoology
32-bit bug numbers.            | uunet!attcan!utzoo!henry henry@zoo.toronto.edu

rns@se-sd.sandiego.ncr.com (Rick Schubert) (12/08/88)

I probably shouldn't do this, since getting involved in such discussions
usually causes more grief than satisfaction, but I just attended a
motivational talk encouraging risk-taking, so here goes.

In article <14832@mimsy.UUCP> chris@mimsy.UUCP (Chris Torek) writes:
::In article <21560@apple.Apple.COM> desnoyer@Apple.COM (Peter Desnoyers) writes:
:::pet peeve - It is a common thing to have to write a zero to a device
:::register. The obvious code, (* reg_addr) = 0, often results in:
:::  xor reg_addr, reg_addr

:::and volatile is not sufficient to force the desired activity.
::Fortunatly you are wrong, and volitile is sufficient.
:Volatile (note the spelling: compilers will not accept the `volitile'
:non-keyword) does imply---I will not use the word `guarantee' without
:checking the exact wording---that using an XOR or read-before-write-CLR
:instruction to clear a volatile location is incorrect.

I won't make any guarantees either, but I believe that using an XOR is NOT
incorrect as far as the dpANS is concerned (although it would be a poor
quality of implementation).  Quoting from the May 13, 1988 Draft (page 62,
lines 10-11):
	What constitutes an access to an object that has volatile-qualified
	type is implementation-defined.
I don't think we (X3J11) changed this at the September, 1988 meeting.  My
recollection from past discussions on this is that we didn't think it would
be feasible to specify precisely how many times and in what ways a volatile
object should be accessed.  It's a very tricky area because volatile deals
with inherently machine-dependent constructs (e.g. device registers), whereas
the Standard is trying to specify portable (or machine-independent) behavior.
Since `access' depends on such things as the instruction set, object size,
and alignment, it is pretty much outside the domain of X3J11 to make such a
specification.

Now, in a practical sense, if a machine has a device register that, in some
situation needs to have the value 0 stored in it, and if reading its value
(1 or more times) before writing a 0 would cause undesirable behavior, then
there must be some instruction (or instruction sequence) to store a 0
without a read (or multiple writes).  If the compiler writer does not
generate such an instruction, but instead uses (for example) XOR because
XOR is faster, then the compiler writer made a poor (to put it mildly)
choice, but I don't think you could take that person before an official
X3J11 court and get a conviction.  XOR still can be used for non-volatile
objects, so your benchmarks won't have to suffer.

:Consider,
:however, a machine which is only word-addressible, on which byte
:references compile to shift-and-mask sequences.  I do not know what
:the compiler must or even should do if given the following code:
:	struct foodev { char c1, c2; };
:	f() {
:		register volatile struct foodev *p;
:		...	/* set up p */
:		p->c1 = 0;
:	}
:I would suggest that the compiler complain about volatile references
:that it cannot compile `properly'.
:-- 
:In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7163)
:Domain:	chris@mimsy.umd.edu	Path:	uunet!mimsy!chris

In article <23706@amdcad.AMD.COM> tim@crackle.amd.com (Tim Olson) writes:
:I was speaking to Richard Relph about this subject, and he brought up
:the problem of unaligned accesses through a pointer to a volatile object
:which is larger than a char, but is not properly aligned [what a
:mouthful!].  These also require multiple accesses to be performed
:"correctly."  What should be done here?
:	-- Tim Olson
:	Advanced Micro Devices

I think these 2 examples demonstrate why X3J11 didn't try to specify the
"correct"  or "proper" behavior in such circumstances.  I also don't think that
either situation would come up in practice.  Remember, `volatile' is primarily
for accessing the hardware in a hardware-specific way;
the hardware designers wouldn't [be so stupid as to] design an object that
must be accessed in a specific way (e.g. a byte that must be accessed without
touching the adjacent byte) without an instruction (or instruction sequence)
to access the object in that way.

Disclaimer 1: None of the above is an official X3J11 position.
Disclaimer 2: Notice the "I believe"s and "I think"s, and be gentle if I am
              wrong.
Disclaimer 3: Sorry if I got some of the secondary attributions wrong;  I had
              trouble following some of chains of "In article ... ... writes:"
Disclaimer 4: Spelling errors inside of quoted articles are not my fault;
              spelling errors outside of quoted articles are "spell"s fault.

-- Rick Schubert (rns@se-sd.sandiego.NCR.COM)

raeburn@athena.mit.edu (Ken Raeburn) (12/08/88)

In article <1988Dec6.180614.23949@utzoo.uucp> henry@utzoo.uucp (Henry Spencer) writes:
>I think you have to consider volatile as meaning "no surprises" -- is
>it really a surprise that a large unaligned object requires more than one
>access?

This sounds a lot more reasonable....

Chris Torek's article got me thinking.  On a machine that can't do
single-byte accesses, device-driver routines shouldn't have volatile
char structure components.  It wouldn't make sense.

(Getting slightly off the subject here....)

But what about variables (automatic, global, or static) that are used
in the neighborhood of setjmp/longjmp calls?  If I have a variable
that is a char or small enum (which my compiler might want to fit into
a char under normal circumstances), what happens when I declare them
volatile?  I think that, given sufficiently bizzarre machine
descriptions, possibly any type (except maybe int?) might require
multiple accesses (multiple reads, or reads-and-writes) for at least
some machine types.  The "no-surprise" guideline seems like the only
way to make sense of this across different machine types.  (My
impression is that the use of "volatile" for setjmp/longjmp is covered
under "portability" rather than "machine-specific device drivers", and
I should be able to do something reasonable here in a portable way.
Or should I?)

(Hope this hasn't gone by in discussions already... I only read news
off-and-on....)

Can the compiler allocate extra storage around a "volatile" variable
(that is not part of a structure and does not have external linkage)
and randomly smash it when this variable needs to be accessed?  Or is
it valid to just have it complain that this particular machine type
can't do that particular operation?

Actually, my own interest in the uses of "volatile" is more in the
line of device drivers... Real Soon Now I should get around to trying
to build some of the BSD drivers with gcc, really I will... I was just
wondering about this point.

_kr

gwyn@smoke.BRL.MIL (Doug Gwyn ) (12/09/88)

In article <8356@bloom-beacon.MIT.EDU> raeburn@athena.mit.edu (Ken Raeburn) writes:
>But what about variables (automatic, global, or static) that are used
>in the neighborhood of setjmp/longjmp calls?  If I have a variable
>that is a char or small enum (which my compiler might want to fit into
>a char under normal circumstances), what happens when I declare them
>volatile?  I think that, given sufficiently bizzarre machine
>descriptions, possibly any type (except maybe int?) might require
>multiple accesses (multiple reads, or reads-and-writes) for at least
>some machine types.  The "no-surprise" guideline seems like the only
>way to make sense of this across different machine types.  (My
>impression is that the use of "volatile" for setjmp/longjmp is covered
>under "portability" rather than "machine-specific device drivers", and
>I should be able to do something reasonable here in a portable way.
>Or should I?)

I don't understand what the problem is.

Upon a longjmp()ed return from a matching setjmp(), 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 longjmp() call are
indeterminate.  [From the proposed standard.]

The only mention of "volatile" is that a volatile auto is guaranteed
to have the correct value, even though a non-volatile auto in the same
function may have been cached and therefore have an incorrect stored
value.  (Otherwise, optimizers would have to give special treatment to
use of setjmp/longjmp.)  "volatile" really has nothing to do with
setjmp/longjmp semantics; the explicit note that its use could make a
local auto safe even across longjmp() was just weakening the exception
being made for local autos due to the effects of optimization, since
it was clear that "volatile" semantics permitted this extra guarantee.

>Can the compiler allocate extra storage around a "volatile" variable
>(that is not part of a structure and does not have external linkage)
>and randomly smash it when this variable needs to be accessed?  Or is
>it valid to just have it complain that this particular machine type
>can't do that particular operation?

The compiler can allocate extra storage in many cases, but it's not
necessary here.  Use of "volatile" does not give the compiler license
to fail to translate a conforming program.  Warnings can be issued
any time the implementor wishes.

raeburn@athena.mit.edu (Ken Raeburn) (12/17/88)

>I don't understand what the problem is.

My problem was with the idea being discussed that references to a
volatile variable translate to exactly one memory reference.  On a
machine that may require more than one memory access to retrieve or
store one of the C data types (especially if you deal with entire
structures at a time) this wouldn't work.

Being more awake this time, I would point out that any piece of
machine-independent, portable code could have "volatile" declarations
added without ill effect (except perhaps some loss of optimization);
my references to setjmp/longjmp were just reaching for some case
outside of device drivers and the like where use of "volatile" would
be useful, but that's beside my point.  Since any type could have the
"volatile" qualifier, the one-access stuff is out the window.

I was basically trying to find out something that somebody posted a
short while later, which I hadn't noticed before in the draft
standard: "What constitutes an access to an object that has
volatile-qualified type is implementation-defined."