This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: Volatile MEMs in statement expressions and functions inlinedastrees



[ If you read nothing else, read the part at the end which explains why I
  so dislike the "use" rule that Alexandre has.  You asked why I care
  about "x = y = 42", there it is. ]

On Sun, 16 Dec 2001, Jason Merrill wrote:
>
> > It could easily mean that some expressions have the rvalue in and of
> > themselves, like an assignment.
>
> I don't see how you can conclude that from [conv.lval].  It says,
>
>   The value contained in the object indicated by the lvalue is the rvalue
>   result.

So?

An assignment -sets- that value.

So, by implication an assignment inherently _knows_ what the "value
contained in the object" is at the time of the assignment.

> For non-volatile variables, if we already know that value for some reason,
> we don't need to look inside the object.  But for volatiles, we can't know
> the value, so we need to fetch it again.

That definitely doesn't follow.

The reason, for volatile basically can be boiled down to "randomly
changing", as far as the compiler is concerned, would you not agree?

It does _not_ follow that "we need to fetch it again" at ALL.

Quite the reverse, in my opinion. Fetching it again is meaningless,
because the only thing fetching it again can do is to inject a new and
"random" value into the stream.

That value has nothing to do with the value of the _assignment_.

The only _well_defined_ value we know the object had (even if it was only
for a very fleeting moment) is the value we stored to it, and re-reading
the value only means we get a random value back.

So there is one, and _exactly_ one value we can "know" it has. At the
exact time of assignment, it has the value we wrote.

So we return that, because that value is the only "stable" value we know
had _some_ meaning at the well-defined time of assignment.

> > See my argument?
>
> Yes, but I don't see anything in the text to suggest that assignment is
> special in this way.  I can understand the desire to make it so with some
> sort of lvalue-rvalue duality as Alexandre has been suggesting, and as
> Erwin suggested toward the end of the issue discussion, but I don't see how
> you can read that into the existing text.

Why not? It's certainly one tempting way to read the standard, and I still
don't see anything that forbids my reading. Even the discussion you quoted
really seemed to _prefer_ my reading, would you not say?

It all hinges on the "lvalue to rvalue conversion". Does the conversion
_have_ to imply a dereference? I say it does not, and that the standard
does not require it to.

I also realized (yeah, I'm dense, it was probably obvious to everybody
else) why the standard never says that the conversion is "accessing the
object", and only says that it must NOT access the object when the
expression isn't evaluated.

The reason is that the "conversion" does not actually happen at run-time
AT ALL.

The conversion is a _semantic_ conversion, not a run-time conversion. This
is what the "sizeof()" example _really_ tells you. Never mind the use of
"access" in that part - the fact that "sizeof()" is mentioned at all (even
though sizeof doesn't even need an rvalue) is that there are potential
rvalue conversions _inside_ the sizeof.

Let me give an example:

	int *p;

	a = sizeof(*p+1);

Here "*p" is an lvalue, but needs to be converted to an rvalue not because
of the sizeof, but because of the plus operator (yeah, code like this
actually happens in real life, if only because of macros).

But obviously no actual code is ever even _generated_ for the "*p+1"
expression, much less executed. Yet the _conversion_ was done.

What does that tell you? It _should_ put up a big red blinking light that
says "semantic conversion".

Now, why does "semantic conversion" as opposed to "dereference" matter?

For a run-time "dereference", you can claim that by the time you
dereference the object, only the object exists, and you've "lost" the fact
that the object was created by an assignment.

If you think of the "lvalue->rvalue" conversion as a run-time dereference
thing, you do not think of the conversion as being dependent on anything
but the lvalue. You don't _have_ anything else. You see "oh, it's an
lvalue", and you just dereference it. End of story.

However, a semantic conversion has more information. It can look at the
semantics of the thing it is converting, and from a semantic conversion
standpoint there is no confusion - you can always just have the semantic
conversion that says "the value that we store IS the value of the object
at the time of the assignment".

Basically, it's a higher-level decision.

So the only thing you need to buy into my world-view is to just buy into
the semantic conversion above: "on a semantic level the value we store to
a volatile object _is_ the value it contains immediately after the
assignemnt".

Once you buy into that, you're home free. And I don't think it should be
that hard to buy into that.

Because if you buy into the semantic statement "the value that we store IS
the value of the object", then the semantic lvalue->rvalue conversion
requirement of "the rvalue is the value of the object" clearly says that
the rvalue of an assignment is the value we just stored.

Now, if you don't buy into the "semantic" argument, you won't. I've tried
my best.

[ Ok, on to the important part ]

> And I don't see why 'p = q = 42' is an important enough case to merit
> special handling.  For a plain assignment, which is all anyone should be
> doing with volatiles, this doesn't matter.

Actually, I don't think it's "p = q = 42" per se that is important.

What is important is being able to clearly say that assignments make _one_
and _exactly_ one access to a volatile object. That's a really useful
thing to have.

For example, right now we have the issue that an assignment to a volatile
value will have _different_ semantics depending on non-local effects
around that assignment.

I'll give you an example, maybe you'll see what I mean.

Let's say that I have a debugging macro that basically looks like

	#ifdef DEBUG
	#define ASSERT(cond, string, ...) \
		do { if (unlikely(!(cond))) printf(x  ,##...) } while (0)
	#else
	#define ASSERT(cond, string, , ...) \
		do { cond; } while (0)
	#endif

(The "unlikely()" thing is just a more readable standard kernel macro for
the gcc "__builtin_expect()" thing, you get the idea).

Note how it's actually actively _trying_ to be "polite" by allowing side
effects in the thing we assert, and the idea is that the user can easily
do things like

	ASSERT( ++i >= 0, "What? Negative i = %d\n", i );

and when debugging is disabled, the "++i" still happens, but we'll not
have the assertion code bloating up the image.

Sounds reasonable?

The way _I_ expect things to work, it should not _matter_ whether a value
is used or not. The side effects of that expression take place, and the
value is either discarded or not.

The currently suggested  gcc semantics are bad. If I write

	ASSERT( x = result , "What? X was zero? Punt punt punt!\n");

and "x" is volatile, then the behaviour _changes_ depending on whether the
value of the assignment is actually used or not. So with DEBUG enabled,
gcc would load from the volatile "x" (possibly causing side effects),
while with DEBUG disabled, gcc would only store to it.

See how the current suggested semantics have very non-obvious and
non-consistent behaviour?

In contrast, if an assignment always returns the value that the assignment
stores (careful language here), then ALL ASSIGNMENTS ALWAYS WORK THE SAME.

Then you can GUARANTEE, that a "x = y" will always only access "x" _once_.
Regardless of what the surroundings are. No surprising subtle issues. When
a value is thrown away, it is just thrown away - it doesn't change the
_semantics_ of the program.

That _consistency_ is what I claim is worth it. It's not a special case,
it's a _lack_ of special cases, as far as the user is concerned.

So do I really care about "x = y = 42", when "y" is volatile? Not one
whit. But I _do_ care about the general issue of consistency, and I think
an expression should behave the same way whether the value of the
expression is used or not.

			Linus


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]