-
Notifications
You must be signed in to change notification settings - Fork 14.5k
[clang][SYCL] Add sycl_external attribute and restrict emitting device code #140282
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 39 commits
abdbf89
f631d7a
128ab1b
118656c
7c592a4
90ead01
195a3cc
a0071d1
d20382c
65262ba
770c65e
328d242
aab6f7d
385ea37
be80436
060b24f
625cff2
a9fe3fb
4eb05b8
ab845a2
3ff689e
a177b9b
58ffb64
7893e90
7e76afd
e8d26a2
82fa98a
e4d15eb
b38e578
1d82fc1
d751b43
2b22ed2
1b3a198
568b569
0ab9ac5
19d1660
a70e2df
45f7b09
4db4101
931fd76
e34f2a6
13a68d5
2c6cf7f
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 | ||||
---|---|---|---|---|---|---|
|
@@ -476,6 +476,48 @@ The SYCL kernel in the previous code sample meets these expectations. | |||||
}]; | ||||||
} | ||||||
|
||||||
def SYCLExternalDocs : Documentation { | ||||||
let Category = DocCatFunction; | ||||||
let Heading = "sycl_external"; | ||||||
let Content = [{ | ||||||
The ``sycl_external`` attribute indicates that a function defined in another | ||||||
translation unit may be called by a device function defined in the current | ||||||
translation unit or, if defined in the current translation unit, the function | ||||||
may be called by device functions defined in other translation units. | ||||||
The attribute is intended for use in the implementation of the ``SYCL_EXTERNAL`` | ||||||
macro as specified in section 5.10.1, "SYCL functions and member functions | ||||||
linkage", of the SYCL 2020 specification. | ||||||
|
||||||
The attribute only appertains to functions and only those that meet the | ||||||
following requirements. | ||||||
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
|
||||||
|
||||||
* Has external linkage. | ||||||
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
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. These are sentences so don't need periods. |
||||||
* Is not explicitly defined as deleted (the function may be an explicitly | ||||||
defaulted function that is defined as deleted). | ||||||
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
|
||||||
|
||||||
The attribute shall be present on the first declaration of a function and | ||||||
may optionally be present on subsequent declarations. | ||||||
|
||||||
When compiling for a SYCL device target that does not support the generic | ||||||
address space, the function shall not specify a raw pointer or reference type | ||||||
as the return type or as a parameter type. | ||||||
See section 5.9, "Address-space deduction", of the SYCL 2020 specification. | ||||||
schittir marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
|
||||||
The following examples demonstrate the use of this attribute: | ||||||
|
||||||
.. code-block:: c++ | ||||||
|
||||||
[[clang::sycl_external]] void Foo(); // Ok. | ||||||
|
||||||
[[clang::sycl_external]] void Bar() { /* ... */ } // Ok. | ||||||
|
||||||
[[clang::sycl_external]] extern void Baz(); // Ok. | ||||||
|
||||||
[[clang::sycl_external]] static void Quux() { /* ... */ } // error: Quux() has internal linkage. | ||||||
erichkeane marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
|
||||||
}]; | ||||||
} | ||||||
|
||||||
def SYCLKernelEntryPointDocs : Documentation { | ||||||
let Category = DocCatFunction; | ||||||
let Content = [{ | ||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -12879,6 +12879,15 @@ def err_sycl_special_type_num_init_method : Error< | |
"types with 'sycl_special_class' attribute must have one and only one '__init' " | ||
"method defined">; | ||
|
||
// SYCL external attribute diagnostics | ||
def err_sycl_attribute_invalid_linkage : Error< | ||
"'sycl_external' can only be applied to functions with external linkage">; | ||
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'd suggest spelling these differently. Either:
Probably the latter if spellings list is changed above. 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. Searching for "can only" in If "attribute" is added after We probably should use 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.
We are consistently inconsistent :)
Agreed.
Even better, I'd probably still wrap it in |
||
def err_sycl_attribute_avoid_main : Error< | ||
"'sycl_external' cannot be applied to the 'main' function">; | ||
def err_sycl_attribute_avoid_deleted_function | ||
: Error<"'sycl_external' cannot be applied to an explicitly deleted " | ||
"function">; | ||
schittir marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
// SYCL kernel entry point diagnostics | ||
def err_sycl_entry_point_invalid : Error< | ||
"'sycl_kernel_entry_point' attribute cannot be applied to a" | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -12956,6 +12956,10 @@ bool ASTContext::DeclMustBeEmitted(const Decl *D) { | |
if (D->hasAttr<WeakRefAttr>()) | ||
return false; | ||
|
||
if (LangOpts.SYCLIsDevice && !D->hasAttr<SYCLKernelEntryPointAttr>() && | ||
!D->hasAttr<SYCLExternalAttr>()) | ||
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. This doesn't seem right to me, as it changes/causes us to miss some of the below, right? 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. That is intentional and consistent with what DPC++ does for its It might be worth adding a comment that explains this though. 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. Hmmm... this skips quite a bit which causes obvious discomfort. There's quite a bit inside of the function itself that get skipped too, which is a little concerning, particularly with us all-over-the-place. IMO, I think a better formulation for the purposes of readability of this would to better integrate these in this function. ~12956 (though perhaps higher than 12950?):
Then around 12990:
That way this requires MUCH less tea-leaf reading of the rest of the function to figure out when/why we're excluding these. 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 mentioned global variables are just one example; we don't want to emit anything that follows the current point of check unless it is explicitly used. I understand the concern, but I think the code is right as is. I suggest we stay with the currently proposed change for now and revisit how to handle this better if a need for more fine-grained selections emerges. Note that DPC++ hasn't demonstrated such a need so far. I do think adding a comment would be helpful though. 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 understood what you meant, and strongly disagree. This is highly unreadable and requires grok'ing the entire function to understand the purpose here, and is more likely to result in future patches breaking this. A more 'surgical' approach here would be 'more correct'. What I proposed above is, as far as I can tell, EXACTLY this functionally, but less likely to be broken in the future. 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. Your suggested change would not be correct because it would result in declarations with However, I think I have convinced myself that the existing check for 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. Ah yes, part of why I find this solution problematic to read! It ends up being much less obvious WHAT is missing. I might suggest a 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. An additional thought after clicking 'start-review': Perhaps something like:
I'm realizing as I look through this more, part of my problem is the vascus relationship between this section, the fact that these two attributes are only legal on a function decl, and the part on line 12948. So there are three sizable parts of logic scattered around the program that makes this REALLY awful to figure out. 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, that seems quite reasonable, I'm happy with that suggestion. And yes, this sequence of if statements that are maybe-order-dependent-or-maybe-not makes for a pretty fragile house of cards. I'm sure there is a better way (maybe multiple better ways) to handle all of this, but it isn't obvious to me what better would look like. Either way, something for some future PR. |
||
return false; | ||
|
||
// Aliases and used decls are required. | ||
if (D->hasAttr<AliasAttr>() || D->hasAttr<UsedAttr>()) | ||
return true; | ||
|
@@ -12971,8 +12975,11 @@ bool ASTContext::DeclMustBeEmitted(const Decl *D) { | |
if (LangOpts.SYCLIsDevice && FD->hasAttr<SYCLKernelEntryPointAttr>()) | ||
return true; | ||
|
||
// FIXME: Functions declared with SYCL_EXTERNAL are required during | ||
// device compilation. | ||
// Function definitions with the sycl_external attribute are required | ||
// during device compilation regardless of whether they are reachable from | ||
// a SYCL kernel. | ||
if (LangOpts.SYCLIsDevice && FD->hasAttr<SYCLExternalAttr>()) | ||
return true; | ||
bader marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
// Constructors and destructors are required. | ||
if (FD->hasAttr<ConstructorAttr>() || FD->hasAttr<DestructorAttr>()) | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4084,6 +4084,21 @@ bool Sema::MergeFunctionDecl(FunctionDecl *New, NamedDecl *&OldD, Scope *S, | |
diag::note_carries_dependency_missing_first_decl) << 0/*Function*/; | ||
} | ||
|
||
// SYCL 2020 section 5.10.1, "SYCL functions and member functions linkage": | ||
// When a function is declared with sycl_external, that attribute must be | ||
// used on the first declaration of that function in the translation unit. | ||
// Redeclarations of the function in the same translation unit may | ||
// optionally use sycl_external, but this is not required. | ||
if (LangOpts.SYCLIsDevice) { | ||
const SYCLExternalAttr *SEA = New->getAttr<SYCLExternalAttr>(); | ||
if (SEA && !Old->hasAttr<SYCLExternalAttr>()) { | ||
Diag(SEA->getLocation(), diag::err_attribute_missing_on_first_decl) | ||
<< SEA; | ||
Diag(Old->getLocation(), diag::note_previous_declaration); | ||
New->dropAttr<SYCLExternalAttr>(); | ||
} | ||
schittir marked this conversation as resolved.
Show resolved
Hide resolved
schittir marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
|
||
// (C++98 8.3.5p3): | ||
// All declarations for a function shall agree exactly in both the | ||
// return type and the parameter-type-list. | ||
|
@@ -12251,6 +12266,9 @@ bool Sema::CheckFunctionDeclaration(Scope *S, FunctionDecl *NewFD, | |
if (NewFD->hasAttr<SYCLKernelEntryPointAttr>()) | ||
SYCL().CheckSYCLEntryPointFunctionDecl(NewFD); | ||
|
||
if (NewFD->hasAttr<SYCLExternalAttr>()) | ||
SYCL().CheckSYCLExternalFunctionDecl(NewFD); | ||
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. Hmm... this seems like it should be handled when doing the attribute 'visiting', why is it here instead of there? 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. The checks include checking for linkage which I don't think is necessarily computed at the time the attribute is visited. 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. Can you confirm that linkage isn't computed at that point? I would expect us to (since the entire declaration is read before we handle attributes) have it there, so it is a little surprising. Also, I didn't see instantiation of this attribute, do we prevent it on function templates? 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. Linkage, or rather, external visibility, which is what we actually check, can depend on other attributes like The attribute is allowed on function templates and is automatically inherited by (implicit and explicit) instantiations (and explicit specializations which is incorrect according to the C++ standard). I don't think there is anything to do to handle instantiation. We do have a testing gap to address yet though. We have good tests for diagnostics, but are missing a test to validate which symbols are actually emitted. We'll ensure that test exercises function templates. 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.
Ah, I see, the visibility attribute makes sense here, thank you for looking into that.
We've had to do some work in the past for attribute instantiation, though simple ones might be automatic. Can you make sure that specializations/partial specializations are properly tested? And diagnose if linkage isn't right? 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. We'll add tests to make sure the attribute has the proper affect with regard to actually emitting symbols. Tests for diagnostics are already in place in Though, hmm, I think we're missing a test for implicit instantiation; I don't think we should diagnose cases like this:
|
||
|
||
// Semantic checking for this function declaration (in isolation). | ||
|
||
if (getLangOpts().CPlusPlus) { | ||
|
@@ -12439,6 +12457,14 @@ void Sema::CheckMain(FunctionDecl *FD, const DeclSpec &DS) { | |
return; | ||
} | ||
|
||
if (getLangOpts().SYCLIsDevice) { | ||
if (FD->hasAttr<SYCLExternalAttr>()) { | ||
Diag(FD->getLocation(), diag::err_sycl_attribute_avoid_main); | ||
FD->setInvalidDecl(); | ||
return; | ||
} | ||
} | ||
|
||
// Functions named main in hlsl are default entries, but don't have specific | ||
// signatures they are required to conform to. | ||
if (getLangOpts().HLSL) | ||
|
Original file line number | Diff line number | Diff line change | ||||||
---|---|---|---|---|---|---|---|---|
|
@@ -249,6 +249,19 @@ static bool CheckSYCLKernelName(Sema &S, SourceLocation Loc, | |||||||
|
||||||||
return false; | ||||||||
} | ||||||||
void SemaSYCL::CheckSYCLExternalFunctionDecl(FunctionDecl *FD) { | ||||||||
schittir marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||
for (auto *SEAttr : FD->specific_attrs<SYCLExternalAttr>()) { | ||||||||
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 just happened to notice this while scrolling for Alexey's comments. It isn't necessary to use a
Suggested change
|
||||||||
if (!FD->isExternallyVisible()) { | ||||||||
Diag(SEAttr->getLocation(), diag::err_sycl_attribute_invalid_linkage); | ||||||||
return; | ||||||||
} | ||||||||
if (FD->isDeletedAsWritten()) { | ||||||||
Diag(SEAttr->getLocation(), | ||||||||
diag::err_sycl_attribute_avoid_deleted_function); | ||||||||
return; | ||||||||
} | ||||||||
} | ||||||||
} | ||||||||
|
||||||||
void SemaSYCL::CheckSYCLEntryPointFunctionDecl(FunctionDecl *FD) { | ||||||||
// Ensure that all attributes present on the declaration are consistent | ||||||||
|
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.
Alternatively:
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.
We use the
Clang
spelling forsycl_kernel_entry_point
, what would be the reason for doing differently here? The attribute is an implementation detail used to provide theSYCL_EXTERNAL
functionality, so shouldn't be directly written by SYCL programmers. Are you suggesting excluding theGNU
spelling?SYCL is only relevant for C++, so the
C23
spelling wouldn't be desired.If we switch to (only) the
CXX11
spelling, I think theclang
namespace should be retained.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.
Ah! Yes, I was suggesting to dump the GNU spelling. Keeping the clang namespace is also the right thing to do, so your suggestion is the right one.
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.
Ok, I'm fine with dropping the GNU spelling. But in that case, we should also change the
sycl_kernel_entry_point
attribute to match (either in this PR or in a separate one).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.
Agreed. SLIGHT preference for separate PR (as I feel like we're almost done with this one, so for the sake of expedience).