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:
  1. 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);
  2. It utilizes an existing keyword and gives meaning to its use in a context which would be otherwise meaningless.
  3. 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 ...
  4. 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;
  5. 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