[cfe-dev] Can indirect class parameters be noalias?
Hal Finkel via cfe-dev
cfe-dev at lists.llvm.org
Fri Jul 31 11:27:05 PDT 2020
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> 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), 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
>> 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
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/cfe-dev/attachments/20200731/5c189726/attachment-0001.html>
More information about the cfe-dev
mailing list