This is the mail archive of the
mailing list for the GCC project.
Re: Volatile MEMs in statement expressions and functions inlined astrees
- From: Linus Torvalds <torvalds at transmeta dot com>
- To: Alexandre Oliva <aoliva at redhat dot com>
- Cc: Richard Henderson <rth at redhat dot com>, <gdr at codesourcery dot com>, <gcc-patches at gcc dot gnu dot org>
- Date: Fri, 14 Dec 2001 09:14:20 -0800 (PST)
- Subject: Re: Volatile MEMs in statement expressions and functions inlined astrees
On 14 Dec 2001, Alexandre Oliva wrote:
> I used to think we shouldn't load back in neither C nor C++. But then
> the following thought occurred to me. Consider:
> (a += b) += c;
Yes. I will consider it, and let me parse it the way I see it.
- "(a += b)" has the rvalue "a+b" (no reloading), and the lvalue "a". In
addition, the "+=" is an implied sequence point, so stores to "a" have
to be stable and cannot be re-ordered with the second part of the
End result for code generation:
t1 = a; // load 'a'
t2 = b; // load 'b'
r1 = t1 + t2; // rvalue of 'a += b'
store r1 into a // stable side effect of 'a += b'
I think everybody can agree with this part.
So now we have the second part: "X += c" where "X" is the expression
above, which has the "lvalue" 'a', and the "rvalue" 'r1'.
And notice how, because it is on the _left_ side of the assignment, we use
the lvalue - the rvalue is not used _at_all_. So parsing continues:
t3 = a; // reload 'a'
t4 = c; // load 'c'
r2 = t3 + t4; // rvalue of '(a += b) += c
store r2 into a // stable side effect of whole expression
See? We end up reloading 'a' - simply because on the left side of the
assignment we have to use the lvalue.
This is why the behaviour for
a += (b += c);
is so different from
(a += b) += c;
In the first example, the assignment expression is on the right, and is
used as a rlvalue: thus no re-loading. In the second example, the
assignment expression is on the left, and as a result we use the lvalue,
and we have to reload.
> If we decide that the rvalue of an assignment needs not be loaded back
> from the resulting lvalue, ever, then we can rewrite this as:
> t1 = a;
> t2 = t1 + b;
> a = t2;
> t3 = t2 + c; // use t2, instead of loading back from a
> a = t3;
NO, you cannot generate it the above way, because the original rvalue (I
call it "r1", you call it "t2") is not used after the first assignment:
the left side of the second assignment uses _only_ the "lvalue", the same
way that the right side of any assignment uses _only_ the "rvalue".
So you cannot re-use t2, and that explains why _your_ parse tree has
ambiguities wrt inlining etc.
But notice how my (very straightforward) approach of keeping the "lvalue"
and "rvalue" always clearly defined and _separate_ does not have any
strange behaviour. Your inlining example (which I deleted) with the
functions getting different values depending on inlining or not, does
_not_ give any different results when you use the clearly separated l/r
> Now I'm totally confused as to what the right behavior in C++ should
> do, and I'm convinced that lvalue/rvalue distinctions can indeed make
> a difference. I'm not sure they *should* make a difference, but I'm
> sure they *can* :-)
I'm saying that the C (and C++) languages _do_ make a very clear
distinction about lvalues vs rvalues, and lvalues are always used on the
left side of an assignment, and rvalues on the right side. And notice how
the above has no ambiguities: we _always_ reload when you use the lvalue,
and we _never_ reload when you use the rvalue.
This has the clear distinction of not _ever_ depending on whether a
function is inlined or optimized or anything else, becomes combinations of
different assignments always have the exact same behaviour as the
assignment on its own.
Consistency - it's a good thing to aim for.
(As to behaviour of "lvalue vs rvalue" _outside_ assignments, I haven't
checked the full gamut of rules, but it's no longer as simple as "r is
right, l is left":
- taking an address of an expression always uses the "lvalue" (because
the "rvalue" doesn't even _have_ an address).
- Passing by reference (and in C++ this happens implicitly for things
like "this" etc when doing method calls, whatever) likewise, and for
the same reasons, always uses the lvalue.
- pretty much all other evaluations (as opposed to the special things
like "sizeof" etc) use the rvalue.
I'm sure it's clearly defined somewhere).