This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
Re: Volatile MEMs in statement expressions and functions inlined as trees
- From: Linus Torvalds <torvalds at transmeta dot com>
- To: Alexandre Oliva <aoliva at redhat dot com>
- Cc: <gdr at codesourcery dot com>, <gcc-patches at gcc dot gnu dot org>
- Date: Thu, 13 Dec 2001 09:35:08 -0800 (PST)
- Subject: Re: Volatile MEMs in statement expressions and functions inlined as trees
On 13 Dec 2001, Alexandre Oliva wrote:
> On Dec 13, 2001, Linus Torvalds <torvalds@transmeta.com> wrote:
>
> > So clearly "p = 0" is _not_ equivalent to "(p = 0, &p)", and claiming it
> > is equivalent is just stupid and misguided.
>
> If you read &p as `p as a reference', not as `pointer to p', it does
> make sense to me.
Well, but if it was a reference, then the thing really is, as claimed,
*(p = 0, &p)
which obviously really _is_ a lvalue. But Jason does not claim that, as
that would also totally destroy his argument, and make it obvious that an
assignment always has to also read the target (which is logically
consistent, but is also obviously wrong).
The fact is, a reference is a reference, and a lvalue is an lvalue, and
the two have nothing to do with each other. The only thing special about
a lvalue is that you can use assign to it, and _if_ you assign to it, you
don't dereference it.
But Jason is, I think, really trying to argue for the opposite, namely
that if you don't use the value of an lvalue, it doesn't get dereferenced.
However, I do not think he has any real logic to back up that claim. For
an expression like
extern volatile in p;
p;
you would expect to see the assembly code to contain a dereference of "p",
would you not? And that expected behaviour is indeed what gcc will do.
Because "p" is volatile, you're not supposed to optimize away the
dereference just because the value doesn't happen to get used. Are we in
agreement on that?
Now, change the expression to "p = 0", and ask yourself: what is it we
expect that statement to do? I would claim that 100% of C programmers
expect it to generate one store to "p", and no loads. Right?
What changed? Right - "p" is an lvalue, and assigning _to_ "p" changes the
semantics of the expression. We do not dereference "p" for reading, we
dereference "p" for writing. THAT is the fundamental property of an
lvalue: you can, with an assignment operator, change the "mode" of
dereference on the _left_ side of the operator (while the right side stays
the same - it still just returns a value, whether it is an lvalue or a
rvalue).
Now, the one thing that "p" and "p = 0" have in common is that they both
return a value. And they return that value _whether_it_is_used_or_not_.
The C standard does not distinguish between values that are used, and
values that get thrown away. The expression is the same.
And clearly, "p = 0" is not supposed to read "p" after the assignment,
whether "p" is volatile or not.
Take those two things together: "p = 0" _does_ return a value and "p = 0"
is _not_ supposed to read off "p". Now start using the value:
q = (p = 0);
and realize that nothing has changed. "p = 0" still returns a value. And
we're _still_ not supposed to read off "p". The fact that we use the value
so returned does not change anything.
And the fact that in C++ you can write
(p = 0) = 1;
does not change anything. Yes, "p = 0" is an lvalue, but as is clearly
seen by _other_ lvalues (like just "p"), the fact that is an lvalue does
ABSOLUTELY NOT mean that its behaviour would somehow change due to having
its value used or not.
The behaviour of an lvalue only changes when it is on the left side of
an expression, ie the target (not value) of an assignment.
QED
Linus