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 47 commits into
base: main
Choose a base branch
from

Conversation

schittir
Copy link
Contributor

@schittir schittir commented May 16, 2025

This patch is part of the upstreaming effort for supporting SYCL language front end.
It makes the following changes:

  1. Adds sycl_external attribute for functions with external linkage.
  2. Adds checks to avoid emitting device code when sycl_external and sycl_kernel_entry_point attributes are not enabled
  3. Fixes test failures caused by the above changes

This patch is missing diagnostics for the following diagnostics listed in the SYCL 2020 specification's section 5.10.1, which will be addressed in a subsequent PR:
Functions that are declared using SYCL_EXTERNAL have the following additional restrictions beyond those imposed on other device functions:

  1. If the SYCL backend does not support the generic address space then the function cannot use raw pointers as parameter or return types. Explicit pointer classes must be used instead;
  2. The function cannot call group::parallel_for_work_item;
  3. The function cannot be called from a parallel_for_work_group scope.

Copy link
Contributor

@tahonermann tahonermann left a comment

Choose a reason for hiding this comment

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

Thanks for working on this @schittir! I completed an initial pass of all of the code, but still need to look more closely at the documentation updates.

@schittir
Copy link
Contributor Author

Thanks for working on this @schittir! I completed an initial pass of all of the code, but still need to look more closely at the documentation updates.

Thank you for the initial pass review, Tom!

Copy link

github-actions bot commented May 26, 2025

✅ With the latest revision this PR passed the C/C++ code formatter.

@schittir schittir changed the title Add sycl_external attribute [SYCL] Add sycl_external attribute Jun 10, 2025
@schittir schittir marked this pull request as ready for review June 10, 2025 16:16
@schittir schittir changed the title [SYCL] Add sycl_external attribute [clang][SYCL Upstreaming] Add sycl_external attribute Jun 10, 2025
@Fznamznon Fznamznon added the SYCL https://registry.khronos.org/SYCL label Jun 10, 2025
@schittir schittir changed the title [clang][SYCL Upstreaming] Add sycl_external attribute [clang][SYCL Upstreaming] Add sycl_external attribute and restrict emitting code Jun 10, 2025
@schittir schittir changed the title [clang][SYCL Upstreaming] Add sycl_external attribute and restrict emitting code [clang][SYCL Upstreaming] Add sycl_external attribute and restrict emitting device code Jun 10, 2025
schittir and others added 3 commits June 10, 2025 14:48
@Fznamznon Fznamznon changed the title [clang][SYCL Upstreaming] Add sycl_external attribute and restrict emitting device code [clang][SYCL] Add sycl_external attribute and restrict emitting device code Jun 11, 2025
Copy link
Contributor

@tahonermann tahonermann left a comment

Choose a reason for hiding this comment

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

Thanks @schittir, here are my initial review comments. I expect to review the newly added tests more closely once these comments are all addressed.

@Fznamznon
Copy link
Contributor

Oops new tests are failing.

Copy link
Contributor

@tahonermann tahonermann left a comment

Choose a reason for hiding this comment

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

This is looking good @schittir! I added some comments for minor issues. I still need to review the tests more closely.

Copy link
Contributor

@tahonermann tahonermann left a comment

Choose a reason for hiding this comment

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

A few more comments. I still haven't finished my review of sycl-external-attr.cpp, but will try to later today or over the weekend.

Copy link
Contributor

@tahonermann tahonermann left a comment

Choose a reason for hiding this comment

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

I finished an initial pass through all of the tests. I'll do another round once you've had a chance to catch up on the comments so far.

Copy link
Contributor

@tahonermann tahonermann left a comment

Choose a reason for hiding this comment

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

This is looking great @schittir! I just realized that, since we made the change to allow the attribute to be present during host compilation that we should no longer restrict diagnostics to device compilation and that tests should exercise host compilation as well.

I suggested a few FIXME comments, but other than that, I think this is good to go!

@schittir
Copy link
Contributor Author

This is looking great @schittir! I just realized that, since we made the change to allow the attribute to be present during host compilation that we should no longer restrict diagnostics to device compilation and that tests should exercise host compilation as well.

I suggested a few FIXME comments, but other than that, I think this is good to go!

Thank you for the review, Tom!

@schittir schittir requested a review from tahonermann July 18, 2025 15:49
Copy link
Contributor

@tahonermann tahonermann left a comment

Choose a reason for hiding this comment

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

I love it @schittir, this looks great!

@erichkeane, @bader, please review and lend your approval. Note that some of the changes to the tests are to synchronize them with continued evolution that happened in DPC++ but was not previously upstreamed.

Copy link
Contributor

@bader bader left a comment

Choose a reason for hiding this comment

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

LGTM in general. I have a few minor comments related to the code style and one related to CodeGen testing.

Copy link
Collaborator

@erichkeane erichkeane left a comment

Choose a reason for hiding this comment

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

Ah shoot, apparently I forgot to click 'submit' when I reviewed this last week. Sorry about that. Here are my pending comments.

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

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Separate PR it is!
I'll follow this spelling: let Spellings = [CXX11<"clang", "sycl_external">];

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This change fails two tests; will fix.
Clang :: CodeGenSPIRV/Builtins/generic_cast_to_ptr_explicit.c
Clang :: CodeGenSPIRV/Builtins/ids_and_ranges.c

// Redeclarations of the function in the same translation unit may
// optionally use SYCL_EXTERNAL, but this is not required.
const SYCLExternalAttr *SEA = New->getAttr<SYCLExternalAttr>();
if (SEA && !Old->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.

To be consistent with the rest of our attributes, I'd probably suggest we support adding it up until definition. We can then do a conformance warning instead of error, but still let it work.

Copy link
Contributor

Choose a reason for hiding this comment

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

That might be reasonable. I think we might still have to make it an error if there was an ODR-use before the definition though. I'd have to think about that more.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@tahonermann - do you have any additional thoughts here?
Thank you!

@@ -12251,6 +12264,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>().

Copy link
Contributor Author

Choose a reason for hiding this comment

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

namespace { struct S9 {}; }
struct T9 {
  using type = S9;
};
template<typename>
[[clang::sycl_external]] void func9() {} // error here : {{'sycl_external' can only be applied to functions with external linkage}}
template<typename T>
[[clang::sycl_external]] void test_func9() {
  func9<typename T::type>(); // note here: {{in instantiation of function template specialization 'func9<(anonymous namespace)::S9>' requested here}}
}
template void test_func9<T9>(); // note here: {{in instantiation of function template specialization 'test_func9<T9>' requested here}}

This case is being diagnosed at present. Will fix it.

Copy link
Contributor

Choose a reason for hiding this comment

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

To test for implicit instantiation, call FunctionDecl::getTemplateSpecializationInfo(). If it returns non-null, call getSpecializationKind() via the returned pointer. If that returns TSK_ImplicitInstantiation, then skip the diagnostic.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The suggested change to avoid diagnosing the abovementioned case affects cases even with explicit template specializations, such as the two preceding it in the test file. Using the canonical decls doesn't solve the problem. Further investigation is needed. I will leave the FIXME as it is for this case for follow up in a subsequent PR.

Copy link
Contributor

@tahonermann tahonermann left a comment

Choose a reason for hiding this comment

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

Thanks for the updates, @schittir. A few minor requests.

Comment on lines +34 to +35
[[clang::sycl_external]] virtual void BFunc1WithAttr() { int i = 1; }
// CHECK: define linkonce_odr spir_func void @_ZN1B14BFunc1WithAttrEv
Copy link
Contributor

Choose a reason for hiding this comment

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

This might not be the result we want. Virtual function definitions have particular rules for when they are emitted and those rules are different for the Itanium and MSVC ABIs. In this case, the virtual function is defined as an inline function and emitting it is ok, but I think is also unnecessary. I think we should do the following:

  1. Skip requiring a symbol for an unused inline function declared with sycl_external.
  2. Add a test for a virtual function that is defined outside the class (a symbol should be emitted).
  3. Add a test for an unused virtual function that is defined as inline outside the class (a symbol should not be emitted).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
SYCL https://registry.khronos.org/SYCL
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants