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

A question on volatile accesses

1,334 views
Skip to first unread message

Ron Guilmette

unread,
Nov 2, 1990, 10:47:00 PM11/2/90
to
Given the following code:

volatile int *ip;

void foobar ()
{
register int i;

do
i = *++ip;
while (i);
}

I'd like to know if the standard allows the incrementation of `ip'
to occur *after* the volatile access.

In other words, could the program above legally be treated as:

volatile int *ip;

void foobar ()
{
register int i;

do {
i = *ip;
++ip;
} while (i);
}

This seems entirely counter-intutive to me, and yet one supposedly ANSI
C compiler provides such a treatment.

I guess the question comes down to this: When you dereference a
pre-incremented pointer, can you always safely assume that the
pre-increment has already occured by the time the indirection
through the pointer occurs?

Note that the variable `ip' is not itself volatile.

Chris Torek

unread,
Nov 3, 1990, 2:29:00 PM11/3/90
to
In article <23...@lupine.NCD.COM> r...@lupine.ncd.com (Ron Guilmette) writes:
>Given ... volatile int *ip; ... i = *++ip;

>I'd like to know if the standard allows the incrementation of `ip'
>to occur *after* the volatile access.

Yes.

>In other words, could the program above legally be treated as:

>... i = *ip; ++ip; ...

No. The equivalent expansion is, instead,

i = ip[1]; ++ip;
/* or: tmp = ip + 1; i = *tmp; ip = tmp; */

Even if `ip' itself were volatile (`volatile int *volatile ip;') the
same sequence could be used, since `volatile' really does not say much
anyway. The main idea behind `volatile' is to make sure that loads
and stores are not deferred beyond sequence points. Without volatile,
the sequence

i = *ip; ip++; i *= *ip;

could be computed as

x = ip[1]; y = ip[0]; i = x * y; ip += 2;

whereas with it, the x and y above must be loaded in the other order.
I am not at all certain whether ip must be altered between those
operations (if ip itself were volatile, this would be true).
--
In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 405 2750)
Domain: ch...@cs.umd.edu Path: uunet!mimsy!chris

Stephen Clamage

unread,
Nov 3, 1990, 12:16:32 PM11/3/90
to
r...@lupine.ncd.com (Ron Guilmette) writes:

| volatile int *ip;
| void foobar ()
| {
| register int i;
| do
| i = *++ip;
| while (i);
| }
|I'd like to know if the standard allows the incrementation of `ip'
|to occur *after* the volatile access.
|In other words, could the program above legally be treated as:

| do {
| i = *ip;
| ++ip;
| } while (i);
|This seems entirely counter-intutive to me, and yet one supposedly ANSI
|C compiler provides such a treatment.

You have changed the semantics here. Forgetting volatile for the moment,
i = *++ip;
is in some sense equivalent to
++ip;
i = *ip;
or to


i = ip[1];
++ip;

by the definition of pre-increment.

|I guess the question comes down to this: When you dereference a
|pre-incremented pointer, can you always safely assume that the
|pre-increment has already occured by the time the indirection
|through the pointer occurs?

There is no sequence point within the expression
i = *++ip;
The side effect of incrementing ip must occur before executing the
statement following the ";", and the expression must be evaluated
AS IF it occurred before dereferencing ip.

So yes, the expression must behave as though the increment occurred
before the dereference, but no, the increment need not actually
occur then. Since ip is not itself volatile, as you pointed out,
this distinction should not make any difference.
--

Steve Clamage, TauMetric Corp, st...@taumet.com

Henry Spencer

unread,
Nov 3, 1990, 6:18:56 PM11/3/90
to
In article <23...@lupine.NCD.COM> r...@lupine.ncd.com (Ron Guilmette) writes:
> volatile int *ip;
> i = *++ip;

>
>I'd like to know if the standard allows the incrementation of `ip'
>to occur *after* the volatile access.

It depends on what you are asking. If I am not mistaken, it is proper for
the change to `ip' -- the variable -- to occur only after the access.
However, the access itself *must* use the incremented value, even if that
value has not yet been written back into the variable; the definition of
the prefix `++' operator demands this.

>In other words, could the program above legally be treated as:

> i = *ip;
> ++ip;

No, although it could be treated as:

i = *(ip + 1);
++ip;

The volatility of the thing pointed at, by the way, has absolutely no
bearing on the issue (except insofar as a kludged-in implementation of
`volatile' may have introduced bugs).

>This seems entirely counter-intutive to me, and yet one supposedly ANSI
>C compiler provides such a treatment.

Your use of the word "supposedly" is appropriate. The way you asked this
implies that this doesn't happen if the thing pointed at is not `volatile'.
My diagnosis would be as mentioned above: somebody kludged `volatile' into
a compiler that originally didn't support it, and broke the prefix `++'
operator in the process.
--
"I don't *want* to be normal!" | Henry Spencer at U of Toronto Zoology
"Not to worry." | he...@zoo.toronto.edu utzoo!henry

Doug Gwyn

unread,
Nov 4, 1990, 3:27:16 AM11/4/90
to
In article <24...@lupine.NCD.COM> r...@NCD.COM (Ron Guilmette) writes:
>So is this a violation of the ANSI standard or what?

It's simply a violation of C. You don't need to look in the standard
to know that *++ip means "increment the pointer then fetch what is now
pointed at". The value of "++ip" is the only issue here and that has
been well defined ever since Dennis invented it.

Ron Guilmette

unread,
Nov 3, 1990, 4:29:59 PM11/3/90
to
Chris Torek's response to my question was (at first) incredibly confusing
to me. I see now that he (perhaps) missed the real thrust of my question,
and this was entirely my fault because I stated the question badly.

Let me restate it one more time, and I'll try to make it clear what I'm
asking about. Consider the following code:

int array[2] = { 1, 2 };

int example ()
{
volatile int *ip = &array[0];
int i;

i = *++ip;

return i;
}

My question is this. Does the ANSI standard permit the function shown
above to delay the pre-increment until *after* the pointer indirection
occurs (thus causing the function to return `1') or does the standard
*require* that the pre-increment occur *before* the pointer indirection
(thus in turn requiring the function to always return `2')?

My (naive?) assumption is that the standard requires that the pre-increment
occur *before* the resulting value is used as a basis for indirection,
but I need to know where the standard states this requirement.

The reason I need to know is as follows.

I am working with a (supposedly) ANSI C compiler which generates code for
a target instruction set which includes delayed branch instructions.
When given code like:

int array[2] = { 1, 2 };
int j = 0;

int example ()
{
volatile int *ip = &array[0];
int i;

do {
i = *++ip;
while (j);

return i;
}

The compiler generates code in which the pre-increment operation gets
stuffed into the delay slot of the (conditional) branch instruction
which is the translation of the `while' clause of the loop.

Thus, the pre-increment occurs *after* the indirection.

Thus, the function returns `1' and not `2'.

So is this a violation of the ANSI standard or what?

--

// Ron Guilmette - C++ Entomologist
// Internet: r...@ncd.com uucp: ...uunet!lupine!rfg
// Motto: If it sticks, force it. If it breaks, it needed replacing anyway.

Doug Gwyn

unread,
Nov 4, 1990, 12:07:01 AM11/4/90
to
In article <23...@lupine.NCD.COM> r...@lupine.ncd.com (Ron Guilmette) writes:
> volatile int *ip;
> i = *++ip;
>In other words, could the program above legally be treated as:
> volatile int *ip;
> i = *ip;
> ++ip;

"volatile" has nothing to do with this.

The above rewrite would be incorrect in any case. Better would be
volatile int *ip;
i = ip[1];
++ip;

The only places in the code where side effects are required to be
brought up to date (synchronized) are at sequence points, such as
at the end of the assignment statement in the original code.

Ron Guilmette

unread,
Nov 4, 1990, 3:21:35 AM11/4/90
to
In article <1990Nov3.2...@zoo.toronto.edu> he...@zoo.toronto.edu (Henry Spencer) writes:
>
>>This seems entirely counter-intutive to me, and yet one supposedly ANSI
>>C compiler provides such a treatment.
>
>Your use of the word "supposedly" is appropriate. The way you asked this
>implies that this doesn't happen if the thing pointed at is not `volatile'.
>My diagnosis would be as mentioned above: somebody kludged `volatile' into
>a compiler that originally didn't support it, and broke the prefix `++'
>operator in the process.

OK. Let me apologize to everyone for having wasted net bandwidth on
this question.

Here is what was *actually* happening.

char *extern_charptr_func ();
int extern_int_func ();

void example ()
{
int i = extern_int_func ();
volatile char *cp = extern_charptr_func ();

do {
i--;
(++cp)[0];
} while (i);
}

Now in this particular compiler (which happens to be GCC, as I believe
Henry guessed) I asked the author (Richard Stallman) some time back to
add a nice little feature for me, which he was nice enough to do.

The feature that I asked for was to have the values of volatile
expressions evaluated, even if the result was not used, so that:

volatile char *cp;

...
*cp;
...

would in fact cause a (byte) fetch from the address pointed to by `cp'.

Now apparently, the ANSI standard doesn't explicitly require such treatment,
but it certainly doesn't rule it out either. I found this feature quite
useful in certain circumstances (e.g. driving I/O chips) in order to be
able to code (entirely in C) a simple load operation (on say a device
register which recognizes the load itself as a signal to do something).

Unfortunately, due to a minor oversight while implementing this enviable
feature, Richard caused such expressions to have code for their side
effects generated *twice*.

Thus, for my complete example above, the pointer `cp' was being incremented
*twice* (once before the indirection and once after) for each trip thru
the loop. The latter increment was (later) getting stuffed into a
delay slot.

Serves me right for asking for a bizzare feature.

Anyway, I just tracked down the bug and wrote a three line fix. Now,
only one increment is generated (prior to the indirection) and correct
ordering of operations is maintained nicely. (Try that on your binary-
only compilers!)

So anyway, NEVERMIND!

Chris Torek

unread,
Nov 4, 1990, 5:03:20 PM11/4/90
to
In article <27...@mimsy.umd.edu> I wrote:
> i = *ip; ip++; i *= *ip;
>could be computed as
> x = ip[1]; y = ip[0]; i = x * y; ip += 2;

Oops, that should be `ip += 1' (or ip++ or ++ip).

In article <24...@lupine.NCD.COM> r...@NCD.COM (Ron Guilmette) writes

further:


>Thus, the pre-increment occurs *after* the indirection.
>Thus, the function returns `1' and not `2'.
>So is this a violation of the ANSI standard or what?

Yes, it is incorrect (and the presence or absence of `volatile' is
irrelevant to this answer). `i = *++ip' must indirect through the
value that results from the increment, even though the increment itself
need not occur before the indirection. (Consider a machine with
several functional units, which might handle `i = *++ip' as:

tell unit 0: `read memory at 4(r9)'
tell unit 1: `compute r9 + 4'
wait for unit 0 to release r9 (indicating it has saved the old value)
tell unit 1: `store result in r9'
tell unit 0: `store result in r10'

in which the increment and the fetch happen `almost simultaneously'.

Doug Gwyn

unread,
Nov 5, 1990, 8:41:23 AM11/5/90
to
In article <24...@lupine.NCD.COM> r...@lupine.ncd.com (Ron Guilmette) writes:
> volatile char *cp;
> *cp;

>would in fact cause a (byte) fetch from the address pointed to by `cp'.
>Now apparently, the ANSI standard doesn't explicitly require such treatment,

Actually, it does require an access (preferably but not necessarily a byte-
wide one). See 3.5.3 Constraints. The implementation must define what
constitutes an access; it is intended that the access be constrained to the
program-specified width whenever possible.

>I found this feature quite useful in certain circumstances (e.g. driving
>I/O chips) in order to be able to code (entirely in C) a simple load
>operation (on say a device register which recognizes the load itself as
>a signal to do something).

Indeed, that was one of the main motivations for the introduction of the
"volatile" type qualifier. To take a specific example, UNIX device drivers
are almost always coded entirely in C, and on the PDP-11 and similar memory-
mapped I/O architectures, some device registers perform different actions
upon a "read-byte", "read-word", "write-byte", "write-word", "read-modify-
write", or other variations of the memory-bus access cycles involved.
Trying to get the right type of machine code generated while coding the
driver in C was quite tricky, and many hard-to-track-down bugs resulted.
With compilers other than Ritchie's, enabling optimization often would
change this behavior, too. At least one version of the UNIX Portable C
Compiler (PCC) had a special hack to recognize constructs like
((struct xxx *)0177450)->zzz
as being potential references to I/O space (device registers) and would
avoid excessive optimization involving such expressions (where the constant
lay within the Unibus I/O address range). X3J11 decided that this problem
had to be faced squarely, and introduced "volatile" to obviate the need for
such hacks. However, although it was proposed that conforming
implementations be required to implement the minimum possible access
"width" for volatile-qualified data, and that is the intent of requiring an
implementation definition for it, it was not practical to insist on it in
every implementation; thus, some latitude was allowed implementors in that
regard.

0 new messages