Tim Rentsch <
t...@alumni.caltech.edu> wrote:
(snip, I wrote)
>> Before there was "volatile", there was PL/I and the ABNORMAL attribute.
>> Now, PL/I had multitasking pretty much from the beginning, so there
>> was always a way that a variable could change at unexpected times.
>> A compiler wasn't supposed to optimize, for example, A+A as 2*A,
>> as A might change.
> I don't have a PL/I specification readily available, so
> I won't try to compare ABNORMAL to volatile.
OK, I now downloaded:
http://bitsavers.trailing-edge.com/pdf/ibm/360/pli/C28-6571-1_PL_I_Language_Specifications_Jul65.pdf
This is, as I understand it, how PL/I is supposed to work, separate from
any specific implementation. Separate manuals describe it as
implemented.
Among others that I had forgotten, it applies to procedures and to
variable, or at least did in 1965.
"Rules for abnormality in procedures:
1. Abnormality is a property of both external an dinternal
procedures. Blocks invoding procedures that are abnormal must be
within the scope of an ABNORMAL, USES, or SETS declaration for the
invoked entry name. However, the invocation of an abnormal procedure
does not make the envoking procedure itself abnormal. These
attributes enable program optimization to be performed.
2. An external procedure is abnormal if it or any procedure invoked
by it:
a. Access, modify, alocate, or free external data.
b. Modify, allocate, or free thier arguments.
c. Return inconsistent function values for the same argument values.
d. Maintain any kind of history.
e. Perform input/output operations.
f. Return control from the procedure by means of a GOTO statement.
3. An internal procedure is abnormal:
a. Under any condition listed above for external procedures.
b. If it, or any procedure called by it, access, modify, allocate,
or free variables declared in an outer block.
4. Abnormal external procedures invoked as functions much be declared
with at least one of the attributes, ABNORMAL, USES, or SETS. The
scope of this declaration must include the invoking block.
5. ABNORMAL used alone specifies that all possible types of abnormality
should be assumed. It is unnecessary to specify ABNORMAL for the
built-in functions, TIME and DATE.
6. The NORMAL attribute specifies that the entry name is for a
procedure that is not abnormal.
a. Access, modify, alocate, or free external data.
b. Modify, allocate, or free thier arguments.
c. Return inconsistent function values for the same argument values.
d. Maintain any kind of history.
e. Perform input/output operations.
f. Return control from the procedure by means of a GOTO statement.
3. An internal procedure is abnormal:
a. Under any condition listed above for external procedures.
b. If it, or any procedure called by it, access, modify, allocate,
or free variables declared in an outer block.
4. Abnormal external procedures invoked as functions much be declared
with at least one of the attributes, ABNORMAL, USES, or SETS. The
scope of this declaration must include the invoking block.
5. ABNORMAL used alone specifies that all possible types of abnormality
should be assumed. It is unnecessary to specify ABNORMAL for the
built-in functions, TIME and DATE.
6. The NORMAL attribute specifies that the entry name is for a
procedure that is not abnormal."
That part is pretty interesting. I know that many programs did those
things without the ABNORMAL attribute, but then maybe it is the default
for procedures.
Onto variables:
"Rules for abnormal data:
1. The ABNORMAL attribute may be declared for any variable.
2. The ABNORMAL attribute specifies that a variable may be altered or
otherwise accessed at an unpredictable time during execution of a
program. The situation might occur, for example, during the
execution of an ON-unit as described in "The ON Statement," in
Chapter 8.
3. Every time ABNORMAL data is referred to, its associated storage
contains its current value."
Much simpler than for procedures. Anyway:
"Default for abnormality of procedures:
If an external entry name appears only as a function reference, the
entry name is assumed to have the NORMAL attribute; otherwise, the
entry name is assumed to be ABNORMAL. Entry names of all internal
procedures and entry names of external procedures invoked in a CALL
statement are assumed to have the ABNORMAL attribute.
Default for abnormality if data:
Variables are assumed to be NORMAL, except structures containing
ABNORMAL elements; such structures may not be declared to be NORMAL."
>> I don't know C11 well at all, has multitasking, or multithreading,
>> been added now?
> Yes but volatile has been in standard C since before multithreading
> was included, so it isn't really affected by that.
>> Is there a way, within a C program (not counting I/O registers
>> and such) for a variable to change within a statement, other
>> than as side effects of that statement?
> Not in a way that's defined by the Standard, no. (And ignoring
> threading, which doesn't bear on the current discussion.)
But it is convenient that multithreading does allow variables to change
at unexpected (in the statement where they might be used) times.
>> If so, then the compiler has to allow for that.
>> Otherwise, it seems to me, that "volatile" has to be
>> implementation defined.
> It might seem that way but the Standard is very clear that this
> isn't so. The consequences, or even potential consequences, of
> any volatile access are unknown to the implementation, and this
> is explicit in the Standard.
In that case, compilers should just give up.
To make the discussion more interesting, are "volatile" variables
allowed to be partially modified? Consider one that is more than one
byte long, and the bytes are not written with any interlock. An
interrupt could occur while one is only partly updated.
S/370 has CAS, Compare and Swap, for interlocked updating of memory.
Other architectures have similar ways of updating storage. Maybe
compilers should use that?
>> As a door that an implementation can use in implementaion specific
>> ways. If an implementation allows for variables to be I/O
>> registers, then the compiler has to compile as appropriate for
>> that case.
> The only rule is that access to an object through a volatile-qualified
> type must be done "naively", ie, according to straightforward rules
> of expression evaluation and not optimized out. (The Standard has
> a more precise definition, but this is the gist.) Anything beyond
> that must be managed by the programmer, not the implementation.
But if you can't say which ways data might be modified, then it is
pretty hard to expect compilers to account for those ways.
(snip, I wrote)
>> Stretching this farther than I probably should, consider a function
>> in the same file as its call, and that the function doesn't do
>> anything, as the compiler can plainly see. Now, consider that
>> one might use a linkage editor (the OS/360 linker can do this)
>> to later replace that function with a different one.
> A similar idea. I don't think the exact mechanism is important,
> as long as it is outside the domain of what the implementation
> (eg, compiler) knows.
OK, sounds good to me.
(snip)
>> x=0*fclose(out);
> The C Standard apparently is more demanding; optimizations
> are allowed only when they don't change the results of what
> the program naively does (again the Standard defines this
> condition more precisely).
Would be interesting to have "volatile" and "nonvolatile" attribute
for functions.
(snip)
> Did you miss the word "Suppose" in what I wrote before? Magic
> functions are a hypothetical construct, defined as described
> above and putatively added to C, for the purpose of illustration.
Maybe.
>> The "volatile" attribute is, but, as I understand, not well enough
>> to say what it actually does.
> The point of volatile is to impose additional requirements, or
> limitations really, on how a C program may be compiled. Beyond these
> requirements, no semantics are defined (beyond those of the access
> itself) either by the Standard or by the implementation. Indeed, the
> Standard says that volatile objects "may be modified in ways unknown
> to the implementation or have other unknown side effects." What
> happens upon accessing such objects is very explicitly outside the
> domain both of the Standard and of the implementation.
(snip)
>> An implementation may allow variables to be I/O ports, and use
>> the "volatile" keywork, but the standard does not have any such
>> wording.
> The point of declaring something 'volatile' is that how it
> behaves when accessed is outside the domain of what the
> implementation knows, and hence the implementation must
> treat it in a particular way. In a sense, the word 'volatile'
> says to the implementation, "You don't know what's going on
> here, so don't imagine that you do."
But the implementation has to make some assumptions.
As I mentioned above, one might be atomic access to memory,
or that other accesses are atomic.
(snip)
>> Yes. My feeling is that the keyword is there to allow for
>> implementation defined behavior. [snip]
> The Standard contradicts this idea.
How about in earlier times?
(snip)
>> It does seem that the compiler should follow the code as
>> written. If there is one reference to a variable, it should be
>> referenced once, for the implementation dependent definition of
>> reference.
>> volatile int x;
>> y=2*x;
>> z=x+x;
>> In this case, y should always be even, z has the possibility
>> of not being even, and the compiler should allow for that.
> Not even that. Even ignoring the possible undefined behavior
> because of overflow, after the second statement is done
> y could have any value at all, because accessing 'x' in
> 'z = x + x;' might have the side effect of storing into
> y, and the compiler isn't allowed to know that or assume
> that it doesn't happen. Any use of y after the second
> assignment statement must refetch y, for just this reason.
Even if y isn't volatile?
(I specifically only made x volatile.)
-- glen