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 38 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
Show all changes
38 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
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
14 changes: 13 additions & 1 deletion clang/include/clang/Basic/Attr.td
Original file line number Diff line number Diff line change
Expand Up @@ -408,7 +408,7 @@ class SubjectList<list<AttrSubject> subjects, SubjectDiag diag = WarnDiag,
string CustomDiag = customDiag;
}

class LangOpt<string name, code customCode = [{}]> {
class LangOpt<string name, code customCode = [{}], bit silentlyIgnore = 0> {
// The language option to test; ignored when custom code is supplied.
string Name = name;

Expand Down Expand Up @@ -1545,6 +1545,18 @@ def SYCLKernel : InheritableAttr {
let Documentation = [SYCLKernelDocs];
}

def GlobalStorageNonLocalVar : SubsetSubject<Var,
[{S->hasGlobalStorage() &&
!S->isLocalVarDeclOrParm()}],
"global variables">;

def SYCLExternal : InheritableAttr {
let Spellings = [Clang<"sycl_external">];
let Subjects = SubjectList<[Function, GlobalStorageNonLocalVar]>;
let LangOpts = [SYCLDevice];
let Documentation = [SYCLExternalDocs];
}

def SYCLKernelEntryPoint : InheritableAttr {
let Spellings = [Clang<"sycl_kernel_entry_point">];
let Args = [
Expand Down
11 changes: 11 additions & 0 deletions clang/include/clang/Basic/AttrDocs.td
Original file line number Diff line number Diff line change
Expand Up @@ -472,6 +472,17 @@ 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 (or the ``SYCL_EXTERNAL`` macro) can only be applied to
functions, and indicates that the function must be treated as a device function and
must be emitted even if it has no direct uses from other device functions.
All ``sycl_external`` function callees implicitly inherit this attribute.
}];
}

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

// SYCL kernel entry point diagnostics
def err_sycl_entry_point_invalid : Error<
"'sycl_kernel_entry_point' attribute cannot be applied to a"
Expand Down
1 change: 1 addition & 0 deletions clang/include/clang/Sema/SemaSYCL.h
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ 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 CheckSYCLEntryPointFunctionDecl(FunctionDecl *FD);
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 @@ -12909,6 +12909,10 @@ bool ASTContext::DeclMustBeEmitted(const Decl *D) {
if (D->hasAttr<WeakRefAttr>())
return false;

if (LangOpts.SYCLIsDevice &&
(!D->hasAttr<SYCLKernelEntryPointAttr>() || !D->hasAttr<SYCLExternalAttr>()))
return false;

// Aliases and used decls are required.
if (D->hasAttr<AliasAttr>() || D->hasAttr<UsedAttr>())
return true;
Expand All @@ -12924,8 +12928,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.
// Functions 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
3 changes: 3 additions & 0 deletions clang/lib/Sema/SemaDeclAttr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7113,6 +7113,9 @@ ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, const ParsedAttr &AL,
case ParsedAttr::AT_SYCLKernel:
S.SYCL().handleKernelAttr(D, AL);
break;
case ParsedAttr::AT_SYCLExternal:
S.SYCL().handleExternalAttr(D, AL);
break;
case ParsedAttr::AT_SYCLKernelEntryPoint:
S.SYCL().handleKernelEntryPointAttr(D, AL);
break;
Expand Down
11 changes: 11 additions & 0 deletions clang/lib/Sema/SemaSYCL.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,17 @@ void SemaSYCL::handleKernelAttr(Decl *D, const ParsedAttr &AL) {
handleSimpleAttribute<SYCLKernelAttr>(*this, D, AL);
}

void SemaSYCL::handleExternalAttr(Decl *D, const ParsedAttr &AL) {
auto *ND = cast<NamedDecl>(D);
if (!ND->isExternallyVisible()) {
Diag(AL.getLoc(), diag::err_sycl_attribute_invalid_linkage)
<< AL << !isa<FunctionDecl>(ND);
return;
}

handleSimpleAttribute<SYCLExternalAttr>(*this, D, AL);
}

void SemaSYCL::handleKernelEntryPointAttr(Decl *D, const ParsedAttr &AL) {
ParsedType PT = AL.getTypeArg();
TypeSourceInfo *TSI = nullptr;
Expand Down
9 changes: 4 additions & 5 deletions clang/test/CodeGenSYCL/kernel-caller-entry-point.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -100,11 +100,10 @@ int main() {

// Verify that SYCL kernel caller functions are emitted for each device target.
//
// FIXME: The following set of matches are used to skip over the declaration of
// main(). main() shouldn't be emitted in device code, but that pruning isn't
// performed yet.
// CHECK-DEVICE: Function Attrs: convergent mustprogress noinline norecurse nounwind optnone
// CHECK-DEVICE-NEXT: define {{[a-z_ ]*}}noundef i32 @main() #0
// main() shouldn't be emitted in device code. It is not annotated with
// sycl_kernel_entry_point or sycl_external attributes.
// Function Attrs: convergent mustprogress noinline norecurse nounwind optnone
// CHECK-NOT: define {{[a-z_ ]*}}noundef i32 @main() #0

// IR for the SYCL kernel caller function generated for
// single_purpose_kernel_task with single_purpose_kernel_name as the SYCL kernel
Expand Down
52 changes: 52 additions & 0 deletions clang/test/SemaSYCL/sycl-external-attribute.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
// RUN: %clang_cc1 -fsycl-is-device -fsyntax-only -verify -DSYCL %s
// RUN: %clang_cc1 -fsycl-is-host -fsyntax-only -verify -DHOST %s
// RUN: %clang_cc1 -verify %s

// Semantic tests for sycl_external attribute

#ifdef SYCL

__attribute__((sycl_external(3))) // expected-error {{'sycl_external' attribute takes no arguments}}
void bar() {}

__attribute__((sycl_external)) // expected-error {{'sycl_external' attribute cannot be applied to a function without external linkage}}
static void func1() {}

namespace {
__attribute__((sycl_external)) // expected-error {{'sycl_external' attribute cannot be applied to a function without external linkage}}
void func2() {}

struct UnnX {};
}

__attribute__((sycl_external)) // expected-error {{'sycl_external' attribute cannot be applied to a function without external linkage}}
void func4(UnnX) {}

class A {
__attribute__((sycl_external))
A() {}

__attribute__((sycl_external)) void func3() {}
};

class B {
public:
__attribute__((sycl_external)) virtual void foo() {}

__attribute__((sycl_external)) virtual void bar() = 0;
};

__attribute__((sycl_external)) int *func0() { return nullptr; }

__attribute__((sycl_external)) void func2(int *) {}

#elif defined(HOST)

// expected-no-diagnostics
__attribute__((sycl_external)) void func3() {}

#else
__attribute__((sycl_external)) // expected-warning {{'sycl_external' attribute ignored}}
void baz() {}

#endif
Loading