Thomas Koenig <
tkoenig@netcologne.de> writes:
Tim Rentsch <tr.17687@z991.linuxsc.com> schrieb:
>
Thomas Koenig <tkoenig@netcologne.de> writes:
>
After thinking about this for a time, what you want looks a lot
like volaitle.
>
That's a good insight. Certainly there are aspects of what I
have proposed that are similar to how volatile works.
>
The way I understand you is the following: You want the
compiler to be forbidden to remove codepaths on the assumption
that undefined behavior cannot happen, and you want a
"best effort" in that case, which includes throwing an error
or just ignoring everything and proceeding.
The key point is not about removing (or forcing) code paths, but
about what inferences may be drawn. Consider this example:
int a = .. something ..;
if( a > a+1 ){ .. stuff not involving a .. }
if( a != INT_MAX ){ ... }
Relying on the premise that "undefined behavior doesn't happen",
a compiler might discard the dependent block of the first if().
But the compiler might also always execute the dependent block
of the second if(), because if a == INT_MAX then the first test
would have been undefined behavior, which violates our premise.
It is just as wrong to skip the test in the second if() as it
is to remove the controlled block in the first if().
Consider a related example:
int a = .. something ..;
if( a < a+1 ){ .. stuff not involving a .. }
if( a != INT_MAX ){ .. other stuff not involving a .. }
Again operating under the premise that the program has no
undefined behavior, both controlled blocks can be executed
unconditionally, because the assumption of there being no
undefined behavior leads to a bad inference for the second
if() test. Notice by the way that the same bad inference
can be drawn if the order of the if() statements is reversed,
because of the rule that undefined behavior "can travel
backwards in time".
The observable behavior includes (n2596)
>
"Volatile accesses to objects are evaluated strictly according to
the rules of the abstract machine."
>
So, assuming that variables are objects (if there's a definition
of an object in n2596, I missed it) the compiler cannot remove
accessing a in
>
volatile int a;
>
if (a > a + 1)
>
so it cannot remove any code path leading to the if statement, which
is what you want. An interesting point is what "volatile access"
actually means, especially for automatic variables; it seems that
all compilers treat this as a memory access (which makes limited
sense in my opinion - is there an explanation for this?)
The original motivation for volatile is to ensure an actual memory
access occurs, in cases where what is happening is outside what
the C implementation know about. Examples are reading or writing
by another process (perhaps not written in C) or a memory-mapped
I/O port. It may be unlikely that a function-local variable would
fall into such a category, but volatile is there in case someone
thinks it does.
Is there any requirement that you can think of that would not
be fullfilled with "volatile int a"?
>
Is there anything with "volatile int a" that you do not want?
>
Something being volatile has consequences only in reference to
objects, and only when a memory access (either read or write) is
requested. There are no such things as volatile values. What
we're looking for here is constraints on operations, not on
memory accesses. In a sense one might say what we want is
"volatile operators": similar in concept to how volatile works,
but in a different area of language semantics.
>
Hmm.. OK. The nice thing about SSA is that it transforms
complicated expressions like "a + b + c" into
>
tmp1 = a + b
tmp2 = tmp1 + c
>
so it would be possible to write a pass which would declare those
variables as volatile that you want (not needed for unsigned, for
example).
>
Alternatively, you could write a pass which translates
>
int a, b;
>
tmp1 = a + b;
>
into
>
tmp1 = (int) ((unsigned) a + (unsigned) b)
>
or just use -frwapv in the first place.
>
So, SSA offers you the possibility of working on operators, like
you want to.
We're talking about different things. What you are talking about is
(perhaps only partially) an implementation strategy. What I am
talking about is how to define the abstract semantics. Exactly what
the rules are has to come first; after the rules are known then we
can think about how they might be implemented.
In terms of defining the abstract semantics, volatile doesn't do the
job. There are several reasons for this, but the most important is
that undefined behavior takes precedence over volatile. If we have
a program
volatile int *p;
...
*p = 0;
... much further down ...
if( 1/0 ) ...
the assignment to *p doesn't have to have happened, regardless of
the volatile status of *p. There needs to be a meaning defined
for some more constrained form of undefined behavior, which I have
called "limited undefined behavior" in other postings, and a change
to the semantics of some constructs from "undefined behavior" to
"limited undefined behavior" (or some other suitable term), to get
the results desired.
I hope you can see what I'm trying to get at here. I admit that my
descriptions are more abstruse than I would like. It's not an easy
area to talk about.