-
Notifications
You must be signed in to change notification settings - Fork 15.4k
[clang] Add value_type attr, use it to add noalias when pass-by-value. #95004
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -9242,3 +9242,15 @@ Declares that a function potentially allocates heap memory, and prevents any pot | |||||
| of ``nonallocating`` by the compiler. | ||||||
| }]; | ||||||
| } | ||||||
|
|
||||||
| def ValueTypeDocs : Documentation { | ||||||
| let Category = DocCatDecl; | ||||||
| let Content = [{ | ||||||
| The ``value_type`` attribute can be used to mark user-defined types as 'value | ||||||
| types'. When objects of value types are passed value to functions, the objects | ||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
| are always considered to be formally copied into a new object. This means the | ||||||
| argument itself must be the only value referencing the passed object. This | ||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I am having a hard time figuring out why pass-by-value doesn't already imply this? Pass-by-value means it was copied into the new object, right? So I guess I have a hard time figuring out what we really mean here.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. NRVO copy-elision can enable a function which returns by value to stash the address of the returned value. Or, C++ constructors can do the same. Then, copy-elision on the call can result in the callee receiving a object whose address is escaped, despite it receiving it "passed by value". E.g. in the following example, But, I don't think this new attribute is viable; the intent expressed by the PR message is to use it in libc++ to mark stdlib types. But, this cannot be a type property, because any code could do the above with any type.
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ok, I see what you mean now. Yeah, that doesn't look like something that could be a property of the type so much as the uses itself. So I again find myself wondering what property of the type we can annotate that makes it usable in the opt requested.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If the argument satisfies the formal conditions for being passed in registers, the implementation is allowed to introduce a copy despite NRVO, which might be enough to make any escaped/self pointers formally invalid even if we don't actually copy. But yeah, NRVO might mean we're out of luck for an arbitrary type to which that doesn't apply. We could probably talk to the C++ committee about this if we can show that we could do useful optimizations except for some contrived and dangerous code pattern like escaping the address of a copyable class value prior to returning it via NRVO.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @jyknight thanks for sharing the example! I made a few changes to make it runable: https://clang.godbolt.org/z/WrE8Ga6d9 What's interesting is that we get different results with Clang with and without optimizations (without optimizations, there's a copy due to On AArch64, the argument isn't marked as So overall it seems like there already are a number of inconsistencies when trying to capture the address of a pass-by-value argument across multiple dimensions; IIUC the program should return the same results for both GCC, Clang, with opts, w/o opts and different architectures. The attribute itself is intended to forbid such uses of the type, but I agree it is not clear if it would be possible to use other for types in libc++.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ugh, right, stack arguments. Anyway, that code specifically has... at least unspecified behavior. I don't know whether that's a strong enough argument to actually treat the existence of pointers to the parameter as UB, though. And it's specific to formally trivial types, which presumably doesn't really help us with our optimization efforts because we want to optimize |
||||||
| allows the optimizer to assume that no other pointers may alias the object when | ||||||
| it is passed indirectly to a function.. | ||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
| }]; | ||||||
| } | ||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,40 @@ | ||
| // NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 5 | ||
| // RUN: %clang_cc1 -triple arm64-apple-ios11 -std=c++11 -fcxx-exceptions -fexceptions -emit-llvm -o - %s | FileCheck %s | ||
|
|
||
| struct Small { | ||
| int *p; | ||
| Small(); | ||
| ~Small(); | ||
| Small(const Small &) noexcept; | ||
| Small &operator=(const Small &); | ||
| }; | ||
|
|
||
|
|
||
| struct __attribute__((value_type)) SmallVT { | ||
| int *p; | ||
| SmallVT(); | ||
| ~SmallVT(); | ||
| SmallVT(const SmallVT &) noexcept; | ||
| SmallVT &operator=(const SmallVT &); | ||
| }; | ||
|
|
||
| struct Large { | ||
| int *p; | ||
| int a[128]; | ||
| Large(); | ||
| ~Large(); | ||
| Large(const Large &) noexcept; | ||
| Large &operator=(const Large &); | ||
| }; | ||
|
|
||
| struct __attribute__((value_type)) LargeVT { | ||
| int *p; | ||
| int a[128]; | ||
| LargeVT(); | ||
| ~LargeVT(); | ||
| LargeVT(const LargeVT &) noexcept; | ||
| LargeVT &operator=(const LargeVT &); | ||
| }; | ||
|
|
||
| void foo(Small a, SmallVT b, Large c, LargeVT d) {} | ||
| // CHECK: define void @_Z3foo5Small7SmallVT5Large7LargeVT(ptr noundef %a, ptr noalias noundef %b, ptr noundef %c, ptr noalias noundef %d) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why are we adding docs, but setting the documentation link here to something else?