Defect Report #008
Submission Date: 10 Dec 92
Submittor: WG14
Source: X3J11/90-021 (Otto R. Newman)
Question 1
Could you tell me if it is legitimate for a conforming C compiler
to perform what's commonly referred to as dead-store elimination for the
first assignment in the following code fragment:
auto int flag; /* non-volatile */
...
flag = 1;
flag = f();
If it is valid to do so, then consider
auto int flag; /* non-volatile */
if
(setjmp(buf))
{
if (flag == 1) ...
}
flag
= 1;
flag = f();
where function f invokes longjmp.
Is the result of the relational expression defined? A solution might be
to define flag as volatile, but
flag is not really volat ile, and the programmer may
not wish to degrade all references to flag nor to
locate all such possible flags and lie about their
volati lity. A related issue is that in many existing applications,
users have coded setjmp-like mechanisms based on a
particular operational environment. The functions do not have the name
``setjmp,'' but essentially establish an externally
accessible entry point within the containing function. Sometimes,
pointers are set to reference such functions, even though the standard
precludes this from being done with setjmp itself
since it is allowable that it only be provided as a macro.
There
are a number of additional optimizations which must be inhibited across
the actual invocation of setjmp, or a setjmp-like
function. Always avoiding these optimizations as well as the dead-store
elimination shown in the example may make the program safe for non-local
jumps, but unnecessarily penalizes programs that don't use
setjmp. To circumvent this problem, some implementors
have defined a pragma which is included in setjmp.h to
identify ``setjmp'' as having the property of
establishing an externally accessible entry, i.e., defining an otherwise
non-obvious point of control flow. Other implementations have hard-coded
tests for the name ``setjmp.''
... would you
please respond to the question regarding the legitimacy of the
optimization in the first example?
Response
The
relevant citation is subclause 7.6.2.1: 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-qual ified type and have been changed between the
setjmp invocation and
longjmp call are indeterminate.
In
response to your question about the effect on optimizations of
setjmp: Yes, it is legitimate for a compiler to
perform optimiza tions that eliminate dead stores to local,
non-volatile, automatic variables when setjmp is used.
Subclause 7.6.2.1 makes the values of all such variables indeterminate
after the longjmp is called. This grants a compiler
the liberty to perform dead-store elimination as well as several other
optimizations.
Question 2
What is happening is that, since the standard
has not provided a mechanism to describe a very recognizable and very
important property of a function, such mechanisms are by necessity being
provided in non-standard ways. My understanding is that a pragma should
never be required for a program to execute correctly as defined by the
standard.
The existing situation serves to reduce portability of C
programs. We believe the Committee should address this problem and would
like to offer a suggestion which seems rather attractive.
Currently,
defining an object as volatile indicates to the compi
ler that its contents may be altered in ways not under control of the
implementation. This is meaningless with function declarations since a
function doesn't have alterable contents (i.e., is not an lvalue).
Instead, it may be possible to utilize this otherwise syntactic no-op by
defining a ``volatile function'' to be one whose return may not
necessarily occur sequentially at the point of the invocation, but
possibly at some other point where the state of the calling program is
unknown. In other words, invocation of such a function results in the
state of the program becoming volatile.
Now, I admit that this is
not a perfectly ``clean'' extrapolation of the use of the type qualifier
volatile, but it is rather compelling, having the
following advantages:
- It solves the described problem in a general way that can be used
with functions not necessarily named ``setjmp.''
Implementations defining setjmp as a function in
setjmp.h
would simply declare
int volatile setjmp(jmp_buf
env);
- It utilizes an existing keyword and gives meaning to its use in a
context which would be otherwise meaningless.
- It is consistent with the type specifier syntax to distinguish
between volatile pointers and pointers to volatile objects. For example,
int volatile setjmp();
defines setjmp
to be a volatile function (i.e., a function whose invocation must
inhibit certain optimizations).
int volatile (*maybe_setjmp_ptr)();
defines a
pointer to such a function, while
int (*mustnotbe_setjmp_ptr)();
defines a pointer
to a normal function.
int (* volatile vol_mustnotbe_setjmp_ptr)();
defines a
volatile pointer to a normal function.
int volatile (* volatile vol_maybe_setjmp_ptr)();
defines a volatile pointer to a volatile function, and so on ...
- Type consistency rules are already in place and make sense. For
example,
maybe_setjmp_ptr = mustnotbe_setjmp_ptr;
is okay with no type-checking violation, whereas
mustnotbe_setjmp_ptr = maybe_setjmp_ptr;
is diagnosed. It would require casting such as
mustnotbe_setjmp_ptr = (int (*)())maybe_setjmp_ptr;
- Since no new syntax or keywords are required, the impact of this
change is very small to both the document defining the standard and to
compilers which support it.
If there is enough Committee interest in this sort of solution, I
would be glad to draft a formal proposal.
Response
The Committee reasserts that the current semantics for type
qualifiers as they appear in the standard are as intended.
Previous Defect Report
< - > Next Defect Report