volatile and "needed side effects"

D

dingoatemydonut

The C99 standard states:
"In the abstract machine, all expressions are evaluated as specified
by the semantics. An actual implementation need not evaluate part
of an expression if it can deduce that its value is not used and
that no needed side effects are produced (including any caused by
calling a function or accessing a volatile object)."


Does that mean that in the following code, *p does not have to
be evaluated since its side effects are not truly needed?:

extern volatile int *p;

int main(void)
{
*p;
return 0;
}
 
E

Eric Sosman

The C99 standard states:
"In the abstract machine, all expressions are evaluated as specified
by the semantics. An actual implementation need not evaluate part
of an expression if it can deduce that its value is not used and
that no needed side effects are produced (including any caused by
calling a function or accessing a volatile object)."


Does that mean that in the following code, *p does not have to
be evaluated since its side effects are not truly needed?:

extern volatile int *p;

int main(void)
{
*p;
return 0;
}

Just the opposite: Since `p' points to an `int' that
is volatile, and since accessing a volatile object is a
side-effect, `*p' must be evaluated -- or at any rate,
the access it implies must be performed. This code might,
for example, be part of a program named IgnoreStatus, whose
entire purpose is to un-wedge a device that's refusing to
do anything until it's been tickled by somebody reading
its memory-mapped status register.
 
K

Keith Thompson

Eric Sosman said:
Just the opposite: Since `p' points to an `int' that
is volatile, and since accessing a volatile object is a
side-effect, `*p' must be evaluated -- or at any rate,
the access it implies must be performed. This code might,
for example, be part of a program named IgnoreStatus, whose
entire purpose is to un-wedge a device that's refusing to
do anything until it's been tickled by somebody reading
its memory-mapped status register.

Right -- unless the compiler knows somehow that accessing *p doesn't
actually have a "needed side effect". But since the user went out of
his way to declare it volatile, it's unlikely that a compiler would go
to the effort to prove this, especially since it might be wrong.
 
E

Eric Sosman

Keith Thompson wrote On 10/10/05 16:31,:
Right -- unless the compiler knows somehow that accessing *p doesn't
actually have a "needed side effect". But since the user went out of
his way to declare it volatile, it's unlikely that a compiler would go
to the effort to prove this, especially since it might be wrong.

Right (back at'cha). It is, however, rather difficult to
reason about volatile in a portable way. The Standard requires
that all the accesses the abstract machine would make must
actually be made, but since the implementation gets to define
what "access" means ...
 
K

Keyser Soze

Eric Sosman said:
Keith Thompson wrote On 10/10/05 16:31,:

Right (back at'cha). It is, however, rather difficult to
reason about volatile in a portable way. The Standard requires
that all the accesses the abstract machine would make must
actually be made, but since the implementation gets to define
what "access" means ...

We do a lot of embedded C code so we test compiliers on how they handle
volatile data types. Some of the test code we use is so abreviated as to
have no real functionality other than to test the code generator.

One compiler we tested claims to conform with ISO C but has a somewhat
nonconformist way of working with volatile data types.

Specifically:

int volatile a;

void test1(void)
{
a = a + 0;
}

void test2(void)
{
a += 0;
}

The code produced by function test1 was correct.

The variable 'a' was read then written.

The code produced by function test2 was incorrect.

The variable 'a' was read but not written.

When we suggested that the code for test2 did not conform we were told the
expression has only one sequence point and that the generated code was
correct.

We did not use that compiler.

Not because there was not a way to work around this particular issue but
because there was now way to know how many more of these kinds of things may
be lurking within their code generator.
 
M

Mark F. Haigh

Keyser Soze wrote:
We do a lot of embedded C code so we test compiliers on how they handle
volatile data types. Some of the test code we use is so abreviated as to
have no real functionality other than to test the code generator.

One compiler we tested claims to conform with ISO C but has a somewhat
nonconformist way of working with volatile data types.

Specifically:

int volatile a;

void test1(void)
{
a = a + 0;
}

void test2(void)
{
a += 0;
}

The code produced by function test1 was correct.

The variable 'a' was read then written.

The code produced by function test2 was incorrect.

The variable 'a' was read but not written.

When we suggested that the code for test2 did not conform we were told the
expression has only one sequence point and that the generated code was
correct.

Using compound assignment operators when dealing with
volatile-qualified objects that are "sensitive" to the difference
(usually hardware registers) is a classic no-no. Here's my best
explanation:

C99: 5.1.2.3 Program execution

[#6] The least requirements on a conforming implementation are:

-- At sequence points, volatile objects are stable in the
sense that previous accesses are complete and
subsequent accesses have not yet occurred.


GCC 4.02 Manual:
4 C Implementation-defined behavior
4.10 Qualifiers - When is a Volatile Object Accessed?

[...]
The minimum either standard specifies is that at a
sequence point all previous accesses to volatile objects
have stabilized and no subsequent accesses have occurred.
Thus an implementation is free to reorder and combine
volatile accesses which occur between sequence points,
but cannot do so for accesses across a sequence point.


6.7.3 Type qualifiers

[#6] 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 [...]


6.5.16.2 Compound assignment

[#3] A compound assignment of the form E1 op= E2 differs
from the simple assignment expression E1 = E1 op (E2) only
in that the lvalue E1 is evaluated only once.


"E1" is evaluated "only once", and implementations are (at least
according to the GCC group's interpretation) "free to reorder and
combine volatile accesses which occur between sequence points". I
believe the compiler's behavior in this case is conforming.

At least that's my interpretation. Dissent or confirmation is welcome.
Personally speaking, I have never used compound assignment to
volatile-qualified types in any device drivers I've ever written.

<snip>

Mark F. Haigh
(e-mail address removed)
 
K

Keyser Soze

Mark F. Haigh said:
Keyser Soze wrote:
We do a lot of embedded C code so we test compiliers on how they handle
volatile data types. Some of the test code we use is so abreviated as to
have no real functionality other than to test the code generator.

One compiler we tested claims to conform with ISO C but has a somewhat
nonconformist way of working with volatile data types.

Specifically:

int volatile a;

void test1(void)
{
a = a + 0;
}

void test2(void)
{
a += 0;
}

The code produced by function test1 was correct.

The variable 'a' was read then written.

The code produced by function test2 was incorrect.

The variable 'a' was read but not written.

When we suggested that the code for test2 did not conform we were told
the
expression has only one sequence point and that the generated code was
correct.

Using compound assignment operators when dealing with
volatile-qualified objects that are "sensitive" to the difference
(usually hardware registers) is a classic no-no. Here's my best
explanation:

C99: 5.1.2.3 Program execution

[#6] The least requirements on a conforming implementation are:

-- At sequence points, volatile objects are stable in the
sense that previous accesses are complete and
subsequent accesses have not yet occurred.


GCC 4.02 Manual:
4 C Implementation-defined behavior
4.10 Qualifiers - When is a Volatile Object Accessed?

[...]
The minimum either standard specifies is that at a
sequence point all previous accesses to volatile objects
have stabilized and no subsequent accesses have occurred.
Thus an implementation is free to reorder and combine
volatile accesses which occur between sequence points,
but cannot do so for accesses across a sequence point.


6.7.3 Type qualifiers

[#6] 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 [...]


6.5.16.2 Compound assignment

[#3] A compound assignment of the form E1 op= E2 differs
from the simple assignment expression E1 = E1 op (E2) only
in that the lvalue E1 is evaluated only once.


"E1" is evaluated "only once", and implementations are (at least
according to the GCC group's interpretation) "free to reorder and
combine volatile accesses which occur between sequence points". I
believe the compiler's behavior in this case is conforming.

At least that's my interpretation. Dissent or confirmation is welcome.
Personally speaking, I have never used compound assignment to
volatile-qualified types in any device drivers I've ever written.

The standard and implementation guides are a little vague and hand wavy on
just exactly what is meant by "evaluated only once" when processing a
compound assignment involving a volatile data type.

--------------------

ansi_iso_iec_9899, Section 6.7.3, footnote:

114) A volatile declaration may be used to describe an object corresponding
to a memory-mapped input/output port or 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.

--------------------

The syntax of the statement would imply that one read and one write would
always be performed on the volatile lvalue of a compound assignment.

This would seem to require that compound assignments to volatile lvaues have
two sequence points. One at the "read" evaluation of E1 and one at the
"write" operation of E2.

The bottom line here seems to be if you need to use volatile data types to
access hardware you had better take a good look at an example of the
generated code for side affects.
 
M

Mark F. Haigh

Keyser said:
The standard and implementation guides are a little vague and hand wavy on
just exactly what is meant by "evaluated only once" when processing a
compound assignment involving a volatile data type.

--------------------

ansi_iso_iec_9899, Section 6.7.3, footnote:

114) A volatile declaration may be used to describe an object corresponding
to a memory-mapped input/output port or 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.

--------------------

The syntax of the statement would imply that one read and one write would
always be performed on the volatile lvalue of a compound assignment.

This would seem to require that compound assignments to volatile lvaues have
two sequence points. One at the "read" evaluation of E1 and one at the
"write" operation of E2.

Footnotes are not normative: See the C99 Forward:

[#6] [...] In accordance with the
ISO/IEC Directives, Part 3, this foreword, the introduction,
notes, footnotes, and examples are for information only.
The bottom line here seems to be if you need to use volatile data types to
access hardware you had better take a good look at an example of the
generated code for side affects.

Agreed, but using compound assignments to write hardware registers is
never a good idea.


Mark F. Haigh
(e-mail address removed)
 
E

Eric Sosman

Mark F. Haigh wrote On 10/11/05 16:01,:
Footnotes are not normative: See the C99 Forward:

[#6] [...] In accordance with the
ISO/IEC Directives, Part 3, this foreword, the introduction,
notes, footnotes, and examples are for information only.

But the Foreword (not "Forward") is also non-normative!
Why should we believe anything it says? In particular, why
should we believe what it says about footnotes?

(And the reference to ISO/IED directives is within the
non-normative Foreword, so it can't be taken seriously ;-)
 
M

Mark F. Haigh

Eric said:
Mark F. Haigh wrote On 10/11/05 16:01,:
Footnotes are not normative: See the C99 Forward:

[#6] [...] In accordance with the
ISO/IEC Directives, Part 3, this foreword, the introduction,
notes, footnotes, and examples are for information only.

But the Foreword (not "Forward") is also non-normative!
Why should we believe anything it says? In particular, why
should we believe what it says about footnotes?

(And the reference to ISO/IED directives is within the
non-normative Foreword, so it can't be taken seriously ;-)

Nice! How about:

The ISO/IEC Directives, Part 3, section 6.5, classify footnotes to text
as "other informative elements", and as such are not considered to be
normative.


Mark F. Haigh
(e-mail address removed)
 
B

Ben Pfaff

Eric Sosman said:
Mark F. Haigh wrote On 10/11/05 16:01,:
Footnotes are not normative: See the C99 Forward:

[#6] [...] In accordance with the
ISO/IEC Directives, Part 3, this foreword, the introduction,
notes, footnotes, and examples are for information only.

But the Foreword (not "Forward") is also non-normative!
Why should we believe anything it says? In particular, why
should we believe what it says about footnotes?

How do you know that the Foreword is non-normative? You seem to
believe that it is so only on the basis of what the Foreword
says, but that's not a valid source because it is non-normative.
 
E

Eric Sosman

Ben said:
Mark F. Haigh wrote On 10/11/05 16:01,:
Footnotes are not normative: See the C99 Forward:

[#6] [...] In accordance with the
ISO/IEC Directives, Part 3, this foreword, the introduction,
notes, footnotes, and examples are for information only.

But the Foreword (not "Forward") is also non-normative!
Why should we believe anything it says? In particular, why
should we believe what it says about footnotes?


How do you know that the Foreword is non-normative? You seem to
believe that it is so only on the basis of what the Foreword
says, but that's not a valid source because it is non-normative.

Assume the Foreword is normative. Then what the Foreward
says is true, and since the Foreword says the Forward is non-
normative we have a contradiction. Therefore, the Foreword
cannot be normative and must be normative. QED, and you may
stay after class to clean the erasers.

(The Foreword's statement that it itself is non-normative
is not a perfect replica of Eubulides' Paradox; unlike E.P. it
is decidable.)
 
T

Tim Rentsch

Keith Thompson said:
Eric Sosman said:
Just the opposite: Since `p' points to an `int' that
is volatile, and since accessing a volatile object is a
side-effect, `*p' must be evaluated -- or at any rate,
the access it implies must be performed. This code might,
for example, be part of a program named IgnoreStatus, whose
entire purpose is to un-wedge a device that's refusing to
do anything until it's been tickled by somebody reading
its memory-mapped status register.

Right -- unless the compiler knows somehow that accessing *p doesn't
actually have a "needed side effect". [snip]

The meaning of volatile implies that no compiler can know. The
expression '*p' must be evaluated by a conforming implementation.
Section 6.7.3 p6.
 
T

Tim Rentsch

Mark F. Haigh said:
Keyser Soze wrote:
We do a lot of embedded C code so we test compiliers on how they handle
volatile data types. Some of the test code we use is so abreviated as to
have no real functionality other than to test the code generator.

One compiler we tested claims to conform with ISO C but has a somewhat
nonconformist way of working with volatile data types.

Specifically:

int volatile a;

void test1(void)
{
a = a + 0;
}

void test2(void)
{
a += 0;
}

The code produced by function test1 was correct.

The variable 'a' was read then written.

The code produced by function test2 was incorrect.

The variable 'a' was read but not written.

When we suggested that the code for test2 did not conform we were told the
expression has only one sequence point and that the generated code was
correct.

Using compound assignment operators when dealing with
volatile-qualified objects that are "sensitive" to the difference
(usually hardware registers) is a classic no-no. Here's my best
explanation:

C99: 5.1.2.3 Program execution

[#6] The least requirements on a conforming implementation are:

-- At sequence points, volatile objects are stable in the
sense that previous accesses are complete and
subsequent accesses have not yet occurred.


GCC 4.02 Manual:
4 C Implementation-defined behavior
4.10 Qualifiers - When is a Volatile Object Accessed?

[...]
The minimum either standard specifies is that at a
sequence point all previous accesses to volatile objects
have stabilized and no subsequent accesses have occurred.
Thus an implementation is free to reorder and combine
volatile accesses which occur between sequence points,
but cannot do so for accesses across a sequence point.


6.7.3 Type qualifiers

[#6] 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 [...]


6.5.16.2 Compound assignment

[#3] A compound assignment of the form E1 op= E2 differs
from the simple assignment expression E1 = E1 op (E2) only
in that the lvalue E1 is evaluated only once.


"E1" is evaluated "only once", and implementations are (at least
according to the GCC group's interpretation) "free to reorder and
combine volatile accesses which occur between sequence points". I
believe the compiler's behavior in this case is conforming.

At least that's my interpretation. Dissent or confirmation is welcome.

I would read it differently. In particular, when the cited sentence
in 6.5.16.2 p3 says "the lvalue E1 is evaluated only once", that seems
better understood as saying

If E1 is of type T1, then the compound assignment E1 op= E2
means the same as if pE1 = &(E1), *pE1 = *pE1 op (E2)
where pE1 is of type T1*, and not counting that the comma
operator implies an ordering between evaluating &(E1) and
evaluating (E2) that isn't there for (E1) op (E2).

Alternatively,

If E1 is of type T1, then the compound assignment E1 op= E2
means the same as (tE1 = *(pE1 = &(E1)) op (E2), *pE1 = tE1)
where pE1 is of type T1* and tE1 is of type T1 with any type
qualifiers removed.

These interpretations make more sense to me. If they're right, that
means gcc's interpretation is non-conforming. I would guess that
6.5.16.2 p3 is worded as it is because alternative formulations
(such as given above) come out sounding so clumsy.
 
T

Tim Rentsch

Ben Pfaff said:
Eric Sosman said:
Mark F. Haigh wrote On 10/11/05 16:01,:
Footnotes are not normative: See the C99 Forward:

[#6] [...] In accordance with the
ISO/IEC Directives, Part 3, this foreword, the introduction,
notes, footnotes, and examples are for information only.

But the Foreword (not "Forward") is also non-normative!
Why should we believe anything it says? In particular, why
should we believe what it says about footnotes?

How do you know that the Foreword is non-normative? You seem to
believe that it is so only on the basis of what the Foreword
says, but that's not a valid source because it is non-normative.

It might be illuminating to look at the ISO/IEC Directives, Part 3.
Elements can be non-normative but still a valid source of information,
because 'normative' doesn't mean what one might think it means.
 
C

Chris Torek

Using compound assignment operators when dealing with
volatile-qualified objects that are "sensitive" to the difference
(usually hardware registers) is a classic no-no. ...

On the other hand, when using hardware that supports read-modify-write
transactions (as on the Unibus for instance), I would *want*:

volatile unsigned short *p;
...
*p = (*p) & ~3;
...
*p &= ~3;

to generate the VAX instructions:

bicw3 (r7),$3,(r7)

and:

bicw2 $3,(r7)

respectively -- they do different things in the hardware.

The C standard cannot require this, and on a load/store architecture
like the SPARC or MIPS, it would be unreasonable even to ask.
There is no such thing as an R/M/W bus cycle on these processors.

One may not always get what one wants; there are always alternative
solutions (such as assembly language functions, callable from C,
or gcc's __asm__ construct in some cases) for situations in which
neither the Standard nor the compiler itself is particularly helpful.
 
T

Tim Rentsch

Chris Torek said:
On the other hand, when using hardware that supports read-modify-write
transactions (as on the Unibus for instance), I would *want*:

volatile unsigned short *p;
...
*p = (*p) & ~3;
...
*p &= ~3;

to generate the VAX instructions:

bicw3 (r7),$3,(r7)

and:

bicw2 $3,(r7)

respectively -- they do different things in the hardware.

Having the generated code do different things for the two cases seems
inconsistent with the semantics stated in 6.5.16.2 p3. Even though
wanting the two cases to have different results is understandable, the
Semantics of compound assignment apparently doesn't allow it. As long
as either form may generate either instruction sequence that's ok;
it's distinguishing the two cases that causes a problem.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,954
Messages
2,570,116
Members
46,704
Latest member
BernadineF

Latest Threads

Top