Skip to content

[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

Open
wants to merge 43 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 39 commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
abdbf89
Add sycl_external attribute
schittir May 16, 2025
f631d7a
Fix test and remove space
schittir May 16, 2025
128ab1b
Address review comments #1
schittir May 23, 2025
118656c
Fix conditional and failing tests
schittir May 28, 2025
7c592a4
Fix the remaining six failing tests
schittir Jun 4, 2025
90ead01
Fix formatting
schittir Jun 4, 2025
195a3cc
Merge branch 'main' into sycl_external
schittir Jun 5, 2025
a0071d1
Remove sycl_external attribute support to variables.
schittir Jun 9, 2025
d20382c
Rename test file
schittir Jun 9, 2025
65262ba
Add tests for sycl_external attribute
schittir Jun 9, 2025
770c65e
Add code examples to sycl_external documentation
schittir Jun 10, 2025
328d242
Merge branch 'main' into sycl_external
schittir Jun 10, 2025
aab6f7d
Update clang/lib/Sema/SemaDeclAttr.cpp
schittir Jun 10, 2025
385ea37
Address review comments -2
schittir Jun 10, 2025
be80436
Address review comments -3
schittir Jun 17, 2025
060b24f
Rename test file
schittir Jun 17, 2025
625cff2
Address review comments -4
schittir Jun 17, 2025
a9fe3fb
Merge branch 'main' into sycl_external
schittir Jun 17, 2025
4eb05b8
Fix failing tests and address review comments
schittir Jun 18, 2025
ab845a2
Address review comments -3
schittir Jun 24, 2025
3ff689e
Merge branch 'main' into sycl_external
schittir Jun 24, 2025
a177b9b
Merge branch 'main' into sycl_external
schittir Jun 24, 2025
58ffb64
Merge branch 'main' into sycl_external
schittir Jul 1, 2025
7893e90
Merge branch 'main' into sycl_external
schittir Jul 1, 2025
7e76afd
Change the second RUN line to use -sycl-is-host
schittir Jul 3, 2025
e8d26a2
Switch to using sycl_external attr to pass the failing test
schittir Jul 3, 2025
82fa98a
Change diagnostic messages
schittir Jun 25, 2025
e4d15eb
Revert RUN line to -fsycl-is-device
schittir Jul 3, 2025
b38e578
Revert test change
schittir Jul 3, 2025
1d82fc1
Merge branch 'main' into sycl_external
schittir Jul 8, 2025
d751b43
Fix conflict resolution errors.
schittir Jul 8, 2025
2b22ed2
Remove changes introduced from downstream.
schittir Jul 8, 2025
1b3a198
Update diagnostic messages in tests
schittir Jul 8, 2025
568b569
Undo more downstream changes
schittir Jul 8, 2025
0ab9ac5
Ungroup diagnostics and add test cases
schittir Jul 9, 2025
19d1660
Merge branch 'main' into sycl_external
schittir Jul 10, 2025
a70e2df
Fix newly failing tests by adding sycl_external attribute
schittir Jul 10, 2025
45f7b09
Add constexpr and consteval test cases
schittir Jul 10, 2025
4db4101
Use existing diagnostic and address other minor comments
schittir Jul 11, 2025
931fd76
Add additional test cases and address review comments
schittir Jul 14, 2025
e34f2a6
Add test cases
schittir Jul 16, 2025
13a68d5
Add FIXME comments, enable diagnostics for host, remove a needless decl
schittir Jul 18, 2025
2c6cf7f
Merge branch 'main' into sycl_external
schittir Jul 18, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions clang/include/clang/Basic/Attr.td
Original file line number Diff line number Diff line change
Expand Up @@ -1641,6 +1641,13 @@ def DeviceKernel : DeclOrTypeAttr {
}];
}

def SYCLExternal : InheritableAttr {
let Spellings = [Clang<"sycl_external">];
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
let Spellings = [Clang<"sycl_external">];
let Spellings = [CXX11<"sycl_external">];

Alternatively:

Suggested change
let Spellings = [Clang<"sycl_external">];
let Spellings = [CXX11<"sycl_external">, C23<"sycl_external">];

Copy link
Contributor

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 for sycl_kernel_entry_point, what would be the reason for doing differently here? The attribute is an implementation detail used to provide the SYCL_EXTERNAL functionality, so shouldn't be directly written by SYCL programmers. Are you suggesting excluding the GNU 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 the clang namespace should be retained.

Suggested change
let Spellings = [Clang<"sycl_external">];
let Spellings = [CXX11<"clang", "sycl_external">];

Copy link
Collaborator

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 for sycl_kernel_entry_point, what would be the reason for doing differently here? The attribute is an implementation detail used to provide the SYCL_EXTERNAL functionality, so shouldn't be directly written by SYCL programmers. Are you suggesting excluding the GNU 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 the clang namespace should be retained.

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.

Copy link
Contributor

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).

Copy link
Collaborator

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).

Agreed. SLIGHT preference for separate PR (as I feel like we're almost done with this one, so for the sake of expedience).

let Subjects = SubjectList<[Function], ErrorDiag>;
let LangOpts = [SYCLHost, SYCLDevice];
let Documentation = [SYCLExternalDocs];
}

def SYCLKernelEntryPoint : InheritableAttr {
let Spellings = [Clang<"sycl_kernel_entry_point">];
let Args = [
Expand Down
42 changes: 42 additions & 0 deletions clang/include/clang/Basic/AttrDocs.td
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
following requirements.
following requirements:


* Has external linkage.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* Has external linkage.
* Has external linkage

Copy link
Collaborator

Choose a reason for hiding this comment

The 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).
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
defaulted function that is defined as deleted).
defaulted function that is defined as deleted)


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.

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.

}];
}

def SYCLKernelEntryPointDocs : Documentation {
let Category = DocCatFunction;
let Content = [{
Expand Down
9 changes: 9 additions & 0 deletions clang/include/clang/Basic/DiagnosticSemaKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -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">;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd suggest spelling these differently. Either:

'sycl_external' attribute
OR:
[[sycl_external]].

Probably the latter if spellings list is changed above.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Searching for "can only" in DiagnosticSemaKinds.td doesn't suggest a consistent formulation. There are quite a few examples that match the currently proposed spelling.

If "attribute" is added after sycl_external, then I would suggest adding "the" before it; "the 'sycl_external' attribute ...".

We probably should use 'clang::sycl_external' for consistency with diagnostics that use substitution to provide the attribute name; see the "... attribute ignored" diagnostics for example.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Searching for "can only" in DiagnosticSemaKinds.td doesn't suggest a consistent formulation. There are quite a few examples that match the currently proposed spelling.

We are consistently inconsistent :)

If "attribute" is added after sycl_external, then I would suggest adding "the" before it; "the 'sycl_external' attribute ...".

Agreed.

We probably should use 'clang::sycl_external' for consistency with diagnostics that use substitution to provide the attribute name; see the "... attribute ignored" diagnostics for example.

Even better, I'd probably still wrap it in [[ and ]] to make it clear what we're talking about.

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">;

// SYCL kernel entry point diagnostics
def err_sycl_entry_point_invalid : Error<
"'sycl_kernel_entry_point' attribute cannot be applied to a"
Expand Down
2 changes: 2 additions & 0 deletions clang/include/clang/Sema/SemaSYCL.h
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,10 @@ class SemaSYCL : public SemaBase {
ParsedType ParsedTy);

void handleKernelAttr(Decl *D, const ParsedAttr &AL);
void handleExternalAttr(Decl *D, const ParsedAttr &AL);
void handleKernelEntryPointAttr(Decl *D, const ParsedAttr &AL);

void CheckSYCLExternalFunctionDecl(FunctionDecl *FD);
void CheckSYCLEntryPointFunctionDecl(FunctionDecl *FD);
StmtResult BuildSYCLKernelCallStmt(FunctionDecl *FD, CompoundStmt *Body);
};
Expand Down
11 changes: 9 additions & 2 deletions clang/lib/AST/ASTContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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>())
Copy link
Collaborator

Choose a reason for hiding this comment

The 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?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is intentional and consistent with what DPC++ does for its sycl_device attribute; see https://github.com/intel/llvm/blob/a70552b59fd4f91b2d80d755aedcd9e93ae61ee1/clang/lib/AST/ASTContext.cpp#L13068-L13070. For example, global variables shouldn't be emitted in device code.

It might be worth adding a comment that explains this though.

Copy link
Collaborator

Choose a reason for hiding this comment

The 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?):

// Some comment about how other attributes/etc are all irrelevant for the purposes of SYCL.
if (LangOpts.SYCLIsDevice) 
      return FD->hasAttr<SYCLKernelEntryPointAttr>() || FD->hasAttr<SYCLExternalAttr>();

Then around 12990:

// Some comment about how global variables should never be emitted
if(LangOpts.SYCLIsDevice) return false;

That way this requires MUCH less tea-leaf reading of the rest of the function to figure out when/why we're excluding these.

Copy link
Contributor

Choose a reason for hiding this comment

The 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.

Copy link
Collaborator

Choose a reason for hiding this comment

The 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.

Copy link
Contributor

Choose a reason for hiding this comment

The 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 AliasAttr or UsedAttr being emitted during device compilation. We could special case that check of course, but I don't think adding additional checks or special cases makes the code easier to reason about.

However, I think I have convinced myself that the existing check for SYCLKernelEntryPointAttr and the new check for SYCLExternalAttr should be done before the "Forward declarations aren't required" check since extern inline functions shouldn't be emitted for device compilation unless used.

Copy link
Collaborator

@erichkeane erichkeane Jul 23, 2025

Choose a reason for hiding this comment

The 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 !SYCLIsDevice + comment on that one for exactly that reason (and so the next thing added there ahs to think about it!).

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

An additional thought after clicking 'start-review': Perhaps something like:

// Comment explaining that on a sycl device, the ONLY thing we emit is functions (not variables), and ONLY those that have one of these two attributes.
if (LangOpts.SYCLIsDevice)
return isa<FunctionDecl>(D) && (D->hasAttr<...>() || D->hasAttr<....>());

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.

Copy link
Contributor

Choose a reason for hiding this comment

The 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;
Expand All @@ -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;

// Constructors and destructors are required.
if (FD->hasAttr<ConstructorAttr>() || FD->hasAttr<DestructorAttr>())
Expand Down
26 changes: 26 additions & 0 deletions clang/lib/Sema/SemaDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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>();
}
}

// (C++98 8.3.5p3):
// All declarations for a function shall agree exactly in both the
// return type and the parameter-type-list.
Expand Down Expand Up @@ -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);
Copy link
Collaborator

Choose a reason for hiding this comment

The 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?

Copy link
Contributor

Choose a reason for hiding this comment

The 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.

Copy link
Collaborator

Choose a reason for hiding this comment

The 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?

Copy link
Contributor

Choose a reason for hiding this comment

The 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 VisibilityAttr. Checking here ensures that all attributes have been processed and therefore avoids visitation ordering issues.

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.

Copy link
Collaborator

Choose a reason for hiding this comment

The 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 VisibilityAttr. Checking here ensures that all attributes have been processed and therefore avoids visitation ordering issues.

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.

Ah, I see, the visibility attribute makes sense here, thank you for looking into that.

I don't think there is anything to do to handle instantiation.

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?

Copy link
Contributor

Choose a reason for hiding this comment

The 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 clang/test/SemaSYCL/sycl-external-attr.cpp.

Though, hmm, I think we're missing a test for implicit instantiation; I don't think we should diagnose cases like this:

namespace { struct S9 {}; }
struct T9 {
  using type = S9;
};
template<typename>
[[clang::sycl_external]] void func9() {}
template<typename T>
[[clang::sycl_external]] void test_func9() {
  func9<typename T::type>();
}
template void test_func9<T9>(); // Ok; don't diagnose implicit instantiation of func9<S9>().


// Semantic checking for this function declaration (in isolation).

if (getLangOpts().CPlusPlus) {
Expand Down Expand Up @@ -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)
Expand Down
3 changes: 3 additions & 0 deletions clang/lib/Sema/SemaDeclAttr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7255,6 +7255,9 @@ ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, const ParsedAttr &AL,
case ParsedAttr::AT_EnumExtensibility:
handleEnumExtensibilityAttr(S, D, AL);
break;
case ParsedAttr::AT_SYCLExternal:
handleSimpleAttribute<SYCLExternalAttr>(S, D, AL);
break;
case ParsedAttr::AT_SYCLKernelEntryPoint:
S.SYCL().handleKernelEntryPointAttr(D, AL);
break;
Expand Down
13 changes: 13 additions & 0 deletions clang/lib/Sema/SemaSYCL.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,19 @@ static bool CheckSYCLKernelName(Sema &S, SourceLocation Loc,

return false;
}
void SemaSYCL::CheckSYCLExternalFunctionDecl(FunctionDecl *FD) {
for (auto *SEAttr : FD->specific_attrs<SYCLExternalAttr>()) {
Copy link
Contributor

Choose a reason for hiding this comment

The 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 for loop here since there is no meaningful distinction between one sycl_external attribute and another; unlike sycl_kernel_entry_point, there is no attribute argument that can cause them to be different. We should be able to just get the attribute.

Suggested change
for (auto *SEAttr : FD->specific_attrs<SYCLExternalAttr>()) {
const auto *SEAttr = FD->getAttr<SYCLExternalAttr>();
assert(SEPAttr && "Missing sycl_external attribute");

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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
// CHECK-NEXT: [[SPV_CAST:%.*]] = tail call noundef ptr @llvm.spv.generic.cast.to.ptr.explicit.p0(ptr addrspace(4) %p)
// CHECK-NEXT: ret ptr [[SPV_CAST]]
//
__attribute__((opencl_private)) int* test_cast_to_private(int* p) {
[[clang::sycl_external]] __attribute__((opencl_private)) int* test_cast_to_private(int* p) {
return __builtin_spirv_generic_cast_to_ptr_explicit(p, 7);
}

Expand All @@ -18,7 +18,7 @@ __attribute__((opencl_private)) int* test_cast_to_private(int* p) {
// CHECK-NEXT: [[SPV_CAST:%.*]] = tail call noundef ptr addrspace(1) @llvm.spv.generic.cast.to.ptr.explicit.p1(ptr addrspace(4) %p)
// CHECK-NEXT: ret ptr addrspace(1) [[SPV_CAST]]
//
__attribute__((opencl_global)) int* test_cast_to_global(int* p) {
[[clang::sycl_external]] __attribute__((opencl_global)) int* test_cast_to_global(int* p) {
return __builtin_spirv_generic_cast_to_ptr_explicit(p, 5);
}

Expand All @@ -28,6 +28,6 @@ __attribute__((opencl_global)) int* test_cast_to_global(int* p) {
// CHECK-NEXT: [[SPV_CAST:%.*]] = tail call noundef ptr addrspace(3) @llvm.spv.generic.cast.to.ptr.explicit.p3(ptr addrspace(4) %p)
// CHECK-NEXT: ret ptr addrspace(3) [[SPV_CAST]]
//
__attribute__((opencl_local)) int* test_cast_to_local(int* p) {
[[clang::sycl_external]] __attribute__((opencl_local)) int* test_cast_to_local(int* p) {
return __builtin_spirv_generic_cast_to_ptr_explicit(p, 4);
}
24 changes: 12 additions & 12 deletions clang/test/CodeGenSPIRV/Builtins/ids_and_ranges.c
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
// CHECK64-NEXT: tail call i64 @llvm.spv.num.workgroups.i64(i32 0)
// CHECK32-NEXT: tail call i32 @llvm.spv.num.workgroups.i32(i32 0)
//
unsigned int test_num_workgroups() {
[[clang::sycl_external]] unsigned int test_num_workgroups() {
return __builtin_spirv_num_workgroups(0);
}

Expand All @@ -16,7 +16,7 @@ unsigned int test_num_workgroups() {
// CHECK64-NEXT: tail call i64 @llvm.spv.workgroup.size.i64(i32 0)
// CHECK32-NEXT: tail call i32 @llvm.spv.workgroup.size.i32(i32 0)
//
unsigned int test_workgroup_size() {
[[clang::sycl_external]] unsigned int test_workgroup_size() {
return __builtin_spirv_workgroup_size(0);
}

Expand All @@ -25,7 +25,7 @@ unsigned int test_workgroup_size() {
// CHECK64-NEXT: tail call i64 @llvm.spv.group.id.i64(i32 0)
// CHECK32-NEXT: tail call i32 @llvm.spv.group.id.i32(i32 0)
//
unsigned int test_workgroup_id() {
[[clang::sycl_external]] unsigned int test_workgroup_id() {
return __builtin_spirv_workgroup_id(0);
}

Expand All @@ -34,7 +34,7 @@ unsigned int test_workgroup_id() {
// CHECK64-NEXT: tail call i64 @llvm.spv.thread.id.in.group.i64(i32 0)
// CHECK32-NEXT: tail call i32 @llvm.spv.thread.id.in.group.i32(i32 0)
//
unsigned int test_local_invocation_id() {
[[clang::sycl_external]] unsigned int test_local_invocation_id() {
return __builtin_spirv_local_invocation_id(0);
}

Expand All @@ -43,7 +43,7 @@ unsigned int test_local_invocation_id() {
// CHECK64-NEXT: tail call i64 @llvm.spv.thread.id.i64(i32 0)
// CHECK32-NEXT: tail call i32 @llvm.spv.thread.id.i32(i32 0)
//
unsigned int test_global_invocation_id() {
[[clang::sycl_external]] unsigned int test_global_invocation_id() {
return __builtin_spirv_global_invocation_id(0);
}

Expand All @@ -52,7 +52,7 @@ unsigned int test_global_invocation_id() {
// CHECK64-NEXT: tail call i64 @llvm.spv.global.size.i64(i32 0)
// CHECK32-NEXT: tail call i32 @llvm.spv.global.size.i32(i32 0)
//
unsigned int test_global_size() {
[[clang::sycl_external]] unsigned int test_global_size() {
return __builtin_spirv_global_size(0);
}

Expand All @@ -61,46 +61,46 @@ unsigned int test_global_size() {
// CHECK64-NEXT: tail call i64 @llvm.spv.global.offset.i64(i32 0)
// CHECK32-NEXT: tail call i32 @llvm.spv.global.offset.i32(i32 0)
//
unsigned int test_global_offset() {
[[clang::sycl_external]] unsigned int test_global_offset() {
return __builtin_spirv_global_offset(0);
}

// CHECK: @test_subgroup_size(
// CHECK-NEXT: [[ENTRY:.*:]]
// CHECK-NEXT: tail call i32 @llvm.spv.subgroup.size()
//
unsigned int test_subgroup_size() {
[[clang::sycl_external]] unsigned int test_subgroup_size() {
return __builtin_spirv_subgroup_size();
}

// CHECK: @test_subgroup_max_size(
// CHECK-NEXT: [[ENTRY:.*:]]
// CHECK-NEXT: tail call i32 @llvm.spv.subgroup.max.size()
//
unsigned int test_subgroup_max_size() {
[[clang::sycl_external]] unsigned int test_subgroup_max_size() {
return __builtin_spirv_subgroup_max_size();
}

// CHECK: @test_num_subgroups(
// CHECK-NEXT: [[ENTRY:.*:]]
// CHECK-NEXT: tail call i32 @llvm.spv.num.subgroups()
//
unsigned int test_num_subgroups() {
[[clang::sycl_external]] unsigned int test_num_subgroups() {
return __builtin_spirv_num_subgroups();
}

// CHECK: @test_subgroup_id(
// CHECK-NEXT: [[ENTRY:.*:]]
// CHECK-NEXT: tail call i32 @llvm.spv.subgroup.id()
//
unsigned int test_subgroup_id() {
[[clang::sycl_external]] unsigned int test_subgroup_id() {
return __builtin_spirv_subgroup_id();
}

// CHECK: @test_subgroup_local_invocation_id(
// CHECK-NEXT: [[ENTRY:.*:]]
// CHECK-NEXT: tail call i32 @llvm.spv.subgroup.local.invocation.id()
//
unsigned int test_subgroup_local_invocation_id() {
[[clang::sycl_external]] unsigned int test_subgroup_local_invocation_id() {
return __builtin_spirv_subgroup_local_invocation_id();
}
Loading
Loading