[cfe-dev] Can indirect class parameters be noalias?
John McCall via cfe-dev
cfe-dev at lists.llvm.org
Mon Aug 3 18:45:13 PDT 2020
On 31 Jul 2020, at 19:50, Hal Finkel wrote:
> On 7/31/20 5:59 PM, James Y Knight wrote:
>> This discussion reminds me of an example I ran into a couple weeks
>> ago, where the execution of the program is dependent precisely upon
>> whether the ABI calls for the object to be passed indirectly, or in a
>> register
>>
>> In the case where NVRO is triggered, the class member foo_ is
>> fully-constructed on the first line of CreateFoo (despite appearing
>> as if that's only constructing a local variable). In the case where
>> the struct is small enough to fit in a register, NVRO does not apply,
>> and in that case, foo_ isn't constructed until after CreateFoo
>> returns.
>>
>> Therefore, I believe it's implementation-defined whether the
>> following program has undefined behavior.
>>
>> https://godbolt.org/z/YT9zsz <https://godbolt.org/z/YT9zsz>
>>
>> #include <assert.h>
>>
>> struct Foo {
>> int x;
>> * // assert fails if you comment out these unused fields!
>> * int dummy[4];
>> };
>>
>> struct Bar {
>> Bar() : foo_(CreateFoo()) {}
>>
>> Foo CreateFoo() {
>> Foo f;
>> f.x = 55;
>> assert(foo_.x == 55);
>> return f;
>> }
>> Foo foo_;
>> };
>>
>> int main() {
>> Bar b;
>> }
>
>
> Looks that way to me too. The example in 11.10.5p2 sort of makes this
> point as well (by pointing out that you can directly initialize a
> global this way).
It does seem hard to argue that this is invalid under the specification.
To me it seems like it clearly *ought* to be invalid, though. Note
that Clang has always emitted return address arguments as `noalias`, so
this has immediate significance.
If I were writing the specification, I would rewrite the restriction in
`[class.cdtor]p2` to say that pointers derived by naming a
returned/constructed object do not formally point to the object until
the function actually returns, even if the copy is elided. That would
make James’s example undefined behavior.
John.
>
> -Hal
>
>
>>
>> On Fri, Jul 31, 2020 at 2:27 PM Hal Finkel via cfe-dev
>> <cfe-dev at lists.llvm.org <mailto:cfe-dev at lists.llvm.org>> wrote:
>>
>>
>> On 7/31/20 1:24 PM, Hal Finkel wrote:
>>> On 7/31/20 12:43 PM, John McCall wrote:
>>>>
>>>> n 31 Jul 2020, at 7:35, Hal Finkel wrote:
>>>>
>>>> On 7/29/20 9:00 PM, John McCall via cfe-dev wrote:
>>>>
>>>> On 29 Jul 2020, at 17:42, Richard Smith wrote:
>>>>
>>>> On Wed, 29 Jul 2020 at 12:52, John McCall
>>>> <rjmccall at apple.com> <mailto:rjmccall at apple.com> wrote:
>>>>
>>>> ...
>>>>
>>>> I think concretely, the escape hatch doesn't stop
>>>> things
>>>> from
>>>> going wrong,
>>>> because -- as you note -- even though we *could* have
>>>> made a copy,
>>>> it's
>>>> observable whether or not we *did* make a copy. For
>>>> example:
>>>>
>>>> I would say that it’s observable whether the
>>>> parameter
>>>> variable has
>>>> the same address as the argument. That doesn’t /have/
>>>> to
>>>> be the same
>>>> question as whether a copy was performed: we could
>>>> consider there to be
>>>> a formal copy (or series of copies) that ultimately
>>>> creates /an/ object
>>>> at the same address, but it’s not the /same/ object
>>>> and
>>>> so pointers
>>>> to the old object no longer validly pointer to it. But
>>>> I
>>>> guess that
>>>> would probably violate the lifetime rules, because it
>>>> would make accesses
>>>> through old pointers UB when in fact they should at
>>>> worst access a valid
>>>> object that’s just unrelated to the parameter object.
>>>>
>>>> I think that it would be great to be able to do this, but
>>>> unfortunately, I think that the point that you raise here
>>>> is
>>>> a key issue. Whether or not the copy is performed is
>>>> visible
>>>> in the model, and so we can't simply act as though there
>>>> was
>>>> a copy when optimizing. Someone could easily have code that
>>>> looks like:
>>>>
>>>> Foo DefaultX;
>>>>
>>>> ...
>>>>
>>>> void something(Foo &A, Foo &B) {
>>>>
>>>> if (&A == &B) { ... }
>>>>
>>>> }
>>>>
>>>> void bar(Foo X) { something(X, DefaultX); }
>>>>
>>>> This example isn’t really on point; a call like
>>>> |bar(DefaultX)|
>>>> obviously cannot just pass the address of |DefaultX| as a
>>>> by-value argument without first proving a lot of stuff about
>>>> how
>>>> |foo| uses both its parameter and |DefaultX|. I think |noalias|
>>>> is actually a subset of what would have to be proven there.
>>>>
>>>
>>> Yes, I apologize. You're right: my pseudo-code missed the point.
>>> So the record is clear, let me rephrase:
>>>
>>> Foo *DefaultX = nullptr;
>>> ...
>>> Foo::Foo() { if (!DefaultX) DefaultX = this; }
>>> ...
>>> void bar(Foo X) { something(X, *DefaultX); }
>>> ...
>>> bar(Foo{});
>>>
>>> I think that's closer to what we're talking about.
>>>
>>>
>>>> In general, the standard is clear that you cannot rely on
>>>> escaping a pointer to/into a trivially-copyable pr-value
>>>> argument prior to the call and then rely on that pointer
>>>> pointing into the corresponding parameter object.
>>>> Implementations are /allowed/ to introduce copies. But it does
>>>> seem like the current wording would allow you to rely on that
>>>> pointer pointing into /some/ valid object, at least until the
>>>> end of the caller’s full-expression. That means that, if we
>>>> don’t guarantee to do an actual copy of the argument, we
>>>> cannot
>>>> make it UB to access the parameter variable through pointers to
>>>> the argument temporary, which is what marking the parameter as
>>>> |noalias| would do.
>>>>
>>>> So I guess the remaining questions are:
>>>>
>>>> * Is this something we can reasonably change in the standard?
>>>>
>>>
>>> This is the part that I'm unclear about. What change would we
>>> make?
>>>
>>>
>>
>> Also, maybe some extended use of the no_unique_address attribute
>> would help?
>>
>> -Hal
>>
>>
>>>
>>>> * Are we comfortable setting |noalias| in C if the only place
>>>> that would break is with a C++ caller?
>>>>
>>>
>>> Out of curiosity, if you take C in combination with our
>>> statement-expression extension implementation
>>> (https://gcc.gnu.org/onlinedocs/gcc/Statement-Exprs.html
>>> <https://gcc.gnu.org/onlinedocs/gcc/Statement-Exprs.html>), and
>>> notwithstanding the statement in the GCC manual about returns by
>>> value (i.e., the part just before where it says, "Therefore the
>>> this pointer observed by Foo is not the address of a."), is
>>> there
>>> any relationship to this topic?
>>>
>>> Thanks again,
>>>
>>> Hal
>>>
>>>
>>>> John.
>>>>
>>>> As Richard's example shows, the code doesn't need to
>>>> explicitly compare the addresses to detect the copy either.
>>>> Any code that reads/writes to the objects can do it. A
>>>> perhaps-more-realistic example might be:
>>>>
>>>> int Cnt = A.RefCnt; ++A.RefCnt; ++B.RefCnt; if (Cnt + 1
>>>> !=
>>>> A.RefCnt) { /* same object case */ }
>>>>
>>>> The best suggestion that I have so far is that we could add
>>>> an attribute like 'can_copy' indicating that the optimizer
>>>> can make a formal copy of the argument in the callee and
>>>> use
>>>> that instead of the original pointer if that seems useful.
>>>> I
>>>> can certainly imagine a transformation such as LICM making
>>>> use of such a thing (although the cost modeling would
>>>> probably need to be fairly conservative).
>>>>
>>>> -Hal
>>>>
>>>> ...
>>>>
>>>> John.
>>>>
>>>>
>>>> _______________________________________________
>>>> cfe-dev mailing list
>>>> cfe-dev at lists.llvm.org <mailto:cfe-dev at lists.llvm.org>
>>>> https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-dev
>>>> <https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-dev>
>>>>
>>>> --
>>>> Hal Finkel
>>>> Lead, Compiler Technology and Programming Languages
>>>> Leadership Computing Facility
>>>> Argonne National Laboratory
>>>>
>>> --
>>> Hal Finkel
>>> Lead, Compiler Technology and Programming Languages
>>> Leadership Computing Facility
>>> Argonne National Laboratory
>>
>> --
>> Hal Finkel
>> Lead, Compiler Technology and Programming Languages
>> Leadership Computing Facility
>> Argonne National Laboratory
>>
>> _______________________________________________
>> cfe-dev mailing list
>> cfe-dev at lists.llvm.org <mailto:cfe-dev at lists.llvm.org>
>> https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-dev
>> <https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-dev>
>>
> --
> Hal Finkel
> Lead, Compiler Technology and Programming Languages
> Leadership Computing Facility
> Argonne National Laboratory
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/cfe-dev/attachments/20200803/4c3c2df9/attachment.html>
More information about the cfe-dev
mailing list