https://github.com/schittir updated https://github.com/llvm/llvm-project/pull/216393
>From 0feaeafbb0cdc1e36195c1dd40804969dada3d83 Mon Sep 17 00:00:00 2001 From: Sindhu Chittireddy <[email protected]> Date: Fri, 14 Aug 2026 11:10:01 -0700 Subject: [PATCH 1/5] [clang][SYCL] Diagnose variadic sycl_external functions at their declaration SYCL device code does not support variadic functions. --- clang/include/clang/Basic/DiagnosticSemaKinds.td | 2 ++ clang/lib/Sema/SemaSYCL.cpp | 5 +++++ .../spirv-host-adaptation-valist.cpp | 9 +++++---- clang/test/SemaSYCL/sycl-cconv.cpp | 2 +- clang/test/SemaSYCL/sycl-external-attr.cpp | 16 ++++++++++++++++ 5 files changed, 29 insertions(+), 5 deletions(-) diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index b314c17ad27bdc..3816eafaf7e3c1 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -13545,6 +13545,8 @@ def err_sycl_external_invalid_main : Error< "%0 cannot be applied to the 'main' function">; def err_sycl_external_invalid_deleted_function : Error< "%0 cannot be applied to an explicitly deleted function">; +def err_sycl_external_invalid_variadic_function : Error< + "%0 cannot be applied to a variadic function">; def warn_sycl_external_missing_on_first_decl : Warning< "%0 attribute does not appear on the first declaration">, InGroup<NonPortableSYCL>; diff --git a/clang/lib/Sema/SemaSYCL.cpp b/clang/lib/Sema/SemaSYCL.cpp index b942f19761f401..0f2f5753d41365 100644 --- a/clang/lib/Sema/SemaSYCL.cpp +++ b/clang/lib/Sema/SemaSYCL.cpp @@ -293,6 +293,11 @@ void SemaSYCL::CheckSYCLExternalFunctionDecl(FunctionDecl *FD) { diag::err_sycl_external_invalid_deleted_function) << SEAttr; } + if (FD->isVariadic()) { + Diag(SEAttr->getLocation(), + diag::err_sycl_external_invalid_variadic_function) + << SEAttr; + } } void SemaSYCL::CheckSYCLEntryPointFunctionDecl(FunctionDecl *FD) { diff --git a/clang/test/CodeGenSPIRV/spirv-host-adaptation-valist.cpp b/clang/test/CodeGenSPIRV/spirv-host-adaptation-valist.cpp index d926e30376b188..799eaf257af713 100644 --- a/clang/test/CodeGenSPIRV/spirv-host-adaptation-valist.cpp +++ b/clang/test/CodeGenSPIRV/spirv-host-adaptation-valist.cpp @@ -5,9 +5,10 @@ // RUN: %clang_cc1 -triple spirv64-unknown-unknown -aux-triple x86_64-pc-windows-msvc \ // RUN: -fsycl-is-device -emit-llvm -o - %s | FileCheck --check-prefix=WINDOWS %s -[[clang::sycl_external]] int f(int n, ...) { +/// No variadic functions in SYCL device code, so 'ap' comes from the caller. +[[clang::sycl_external]] int f(__builtin_va_list *ap) { __builtin_va_list ap1, ap2; - __builtin_va_start(ap1, n); + __builtin_va_copy(ap1, *ap); int v = __builtin_va_arg(ap1, int); __builtin_va_copy(ap2, ap1); __builtin_va_end(ap1); @@ -15,10 +16,10 @@ return v; } -// LINUX: define {{.*}} i32 @_Z1fiz(i32 noundef %n, ...) {{.*}} { +// LINUX: define {{.*}} i32 @_Z1fPA1_13__va_list_tag( // LINUX: %ap1 = alloca [1 x %struct.__va_list_tag], align 8 // LINUX: %ap2 = alloca [1 x %struct.__va_list_tag], align 8 -// WINDOWS: define {{.*}} i32 @_Z1fiz(i32 noundef %n, ...) {{.*}} { +// WINDOWS: define {{.*}} i32 @_Z1fPPc( // WINDOWS: %ap1 = alloca ptr addrspace(4), align 8 // WINDOWS: %ap2 = alloca ptr addrspace(4), align 8 diff --git a/clang/test/SemaSYCL/sycl-cconv.cpp b/clang/test/SemaSYCL/sycl-cconv.cpp index 664a4dbf37c49d..1d3ba97b624bf5 100644 --- a/clang/test/SemaSYCL/sycl-cconv.cpp +++ b/clang/test/SemaSYCL/sycl-cconv.cpp @@ -8,7 +8,7 @@ // no-aux-error@+1 {{variadic function cannot use spir_function calling convention}} __inline __cdecl int printf(char const* const _Format, ...) { return 0; } -// FIXME: that should be diagnosed. +// expected-error@+1 {{'clang::sycl_external' cannot be applied to a variadic function}} [[clang::sycl_external]] int foo(int, ...) { return 0; } // no-aux-warning@+1 {{'__cdecl' calling convention is not supported for this target}} diff --git a/clang/test/SemaSYCL/sycl-external-attr.cpp b/clang/test/SemaSYCL/sycl-external-attr.cpp index ebda94e7d50307..22f4276d5a5474 100644 --- a/clang/test/SemaSYCL/sycl-external-attr.cpp +++ b/clang/test/SemaSYCL/sycl-external-attr.cpp @@ -113,6 +113,22 @@ class D { // expected-error@+1{{'clang::sycl_external' cannot be applied to an explicitly deleted function}} [[clang::sycl_external]] void del() = delete; +// SYCL device code does not support variadic functions. +// expected-error@+1{{'clang::sycl_external' cannot be applied to a variadic function}} +[[clang::sycl_external]] void var(int, ...) {} + +// expected-error@+1{{'clang::sycl_external' cannot be applied to a variadic function}} +[[clang::sycl_external]] void vardecl(int, ...); + +// expected-error@+2{{'clang::sycl_external' cannot be applied to a variadic function}} +class E { + [[clang::sycl_external]] void mvar(int, ...) {} +}; + +template<typename... Ts> +[[clang::sycl_external]] void pack(Ts...) {} +template void pack(int); + struct NonCopyable { ~NonCopyable() = delete; [[clang::sycl_external]] NonCopyable(const NonCopyable&) = default; >From fbed1e75c687dd652c64b95cc81b9a353ae9078f Mon Sep 17 00:00:00 2001 From: Sindhu Chittireddy <[email protected]> Date: Thu, 20 Aug 2026 20:43:20 -0700 Subject: [PATCH 2/5] Replace the error with a warning --- clang/include/clang/Basic/AttrDocs.td | 6 ++++++ .../include/clang/Basic/DiagnosticSemaKinds.td | 6 ++++-- clang/include/clang/Sema/SemaSYCL.h | 5 +++++ clang/lib/Sema/SemaDeclAttr.cpp | 2 +- clang/lib/Sema/SemaSYCL.cpp | 17 ++++++++++++----- clang/test/SemaSYCL/sycl-cconv.cpp | 2 +- clang/test/SemaSYCL/sycl-external-attr.cpp | 15 ++++++++++++--- clang/test/SemaSYCL/variadic-func-call.cpp | 4 ++++ 8 files changed, 45 insertions(+), 12 deletions(-) diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td index 847e44688ed950..c10ea02b71d330 100644 --- a/clang/include/clang/Basic/AttrDocs.td +++ b/clang/include/clang/Basic/AttrDocs.td @@ -645,6 +645,10 @@ following requirements: The attribute shall be present on the first declaration of a function and may optionally be present on subsequent declarations. +The attribute is ignored, with a warning, when applied to a variadic function. +A variadic function cannot be called from device code, so there is no reason to +emit device code for one. + 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. @@ -661,6 +665,8 @@ The following examples demonstrate the use of this attribute: [[clang::sycl_external]] static void Quux() { /* ... */ } // error: Quux() has internal linkage. + [[clang::sycl_external]] void Corge(int, ...); // warning: attribute ignored; Corge() is variadic. + }]; } diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 3816eafaf7e3c1..38ca60c1d94a12 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -13545,8 +13545,10 @@ def err_sycl_external_invalid_main : Error< "%0 cannot be applied to the 'main' function">; def err_sycl_external_invalid_deleted_function : Error< "%0 cannot be applied to an explicitly deleted function">; -def err_sycl_external_invalid_variadic_function : Error< - "%0 cannot be applied to a variadic function">; +def warn_sycl_external_ignored_variadic_function : Warning< + "%0 attribute ignored; a variadic function cannot be called from device " + "code">, + InGroup<IgnoredAttributes>; def warn_sycl_external_missing_on_first_decl : Warning< "%0 attribute does not appear on the first declaration">, InGroup<NonPortableSYCL>; diff --git a/clang/include/clang/Sema/SemaSYCL.h b/clang/include/clang/Sema/SemaSYCL.h index 4980aa44c30129..7d3b5653964f05 100644 --- a/clang/include/clang/Sema/SemaSYCL.h +++ b/clang/include/clang/Sema/SemaSYCL.h @@ -62,6 +62,11 @@ class SemaSYCL : public SemaBase { ParsedType ParsedTy); void handleKernelAttr(Decl *D, const ParsedAttr &AL); + + /// Adds a SYCLExternalAttr to 'D', or warns that the attribute is ignored if + /// 'D' is a variadic function. + void handleExternalAttr(Decl *D, const ParsedAttr &AL); + void handleKernelEntryPointAttr(Decl *D, const ParsedAttr &AL); /// Issues a deferred diagnostic if use of the declaration designated diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp index 0645f994924334..06461220658d3d 100644 --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -7846,7 +7846,7 @@ ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, const ParsedAttr &AL, S.SYCL().handleKernelAttr(D, AL); break; case ParsedAttr::AT_SYCLExternal: - handleSimpleAttribute<SYCLExternalAttr>(S, D, AL); + S.SYCL().handleExternalAttr(D, AL); break; case ParsedAttr::AT_SYCLKernelEntryPoint: S.SYCL().handleKernelEntryPointAttr(D, AL); diff --git a/clang/lib/Sema/SemaSYCL.cpp b/clang/lib/Sema/SemaSYCL.cpp index 0f2f5753d41365..a96cb060cee4ca 100644 --- a/clang/lib/Sema/SemaSYCL.cpp +++ b/clang/lib/Sema/SemaSYCL.cpp @@ -213,6 +213,18 @@ void SemaSYCL::handleKernelAttr(Decl *D, const ParsedAttr &AL) { handleSimpleAttribute<SYCLKernelAttr>(*this, D, AL); } +void SemaSYCL::handleExternalAttr(Decl *D, const ParsedAttr &AL) { + // Device code can neither call a variadic function nor take its address, so + // there is no reason to emit device code for one. Since SYCL 2020 prohibits + // only the call, ignore the attribute rather than reject the declaration. + if (cast<FunctionDecl>(D)->isVariadic()) { + Diag(AL.getLoc(), diag::warn_sycl_external_ignored_variadic_function) << AL; + return; + } + + handleSimpleAttribute<SYCLExternalAttr>(*this, D, AL); +} + void SemaSYCL::handleKernelEntryPointAttr(Decl *D, const ParsedAttr &AL) { ParsedType PT = AL.getTypeArg(); TypeSourceInfo *TSI = nullptr; @@ -293,11 +305,6 @@ void SemaSYCL::CheckSYCLExternalFunctionDecl(FunctionDecl *FD) { diag::err_sycl_external_invalid_deleted_function) << SEAttr; } - if (FD->isVariadic()) { - Diag(SEAttr->getLocation(), - diag::err_sycl_external_invalid_variadic_function) - << SEAttr; - } } void SemaSYCL::CheckSYCLEntryPointFunctionDecl(FunctionDecl *FD) { diff --git a/clang/test/SemaSYCL/sycl-cconv.cpp b/clang/test/SemaSYCL/sycl-cconv.cpp index 1d3ba97b624bf5..dee3cee8b95c19 100644 --- a/clang/test/SemaSYCL/sycl-cconv.cpp +++ b/clang/test/SemaSYCL/sycl-cconv.cpp @@ -8,7 +8,7 @@ // no-aux-error@+1 {{variadic function cannot use spir_function calling convention}} __inline __cdecl int printf(char const* const _Format, ...) { return 0; } -// expected-error@+1 {{'clang::sycl_external' cannot be applied to a variadic function}} +// expected-warning@+1 {{'clang::sycl_external' attribute ignored; a variadic function cannot be called from device code}} [[clang::sycl_external]] int foo(int, ...) { return 0; } // no-aux-warning@+1 {{'__cdecl' calling convention is not supported for this target}} diff --git a/clang/test/SemaSYCL/sycl-external-attr.cpp b/clang/test/SemaSYCL/sycl-external-attr.cpp index 22f4276d5a5474..a02ae48c6fd9a8 100644 --- a/clang/test/SemaSYCL/sycl-external-attr.cpp +++ b/clang/test/SemaSYCL/sycl-external-attr.cpp @@ -114,13 +114,18 @@ class D { [[clang::sycl_external]] void del() = delete; // SYCL device code does not support variadic functions. -// expected-error@+1{{'clang::sycl_external' cannot be applied to a variadic function}} +// expected-warning@+1{{'clang::sycl_external' attribute ignored; a variadic function cannot be called from device code}} [[clang::sycl_external]] void var(int, ...) {} -// expected-error@+1{{'clang::sycl_external' cannot be applied to a variadic function}} +// expected-warning@+1{{'clang::sycl_external' attribute ignored; a variadic function cannot be called from device code}} [[clang::sycl_external]] void vardecl(int, ...); -// expected-error@+2{{'clang::sycl_external' cannot be applied to a variadic function}} +// expected-warning@+1{{'clang::sycl_external' attribute ignored; a variadic function cannot be called from device code}} +[[clang::sycl_external]] void varredecl(int, ...); +// expected-warning@+1{{'clang::sycl_external' attribute ignored; a variadic function cannot be called from device code}} +[[clang::sycl_external]] void varredecl(int, ...) {} + +// expected-warning@+2{{'clang::sycl_external' attribute ignored; a variadic function cannot be called from device code}} class E { [[clang::sycl_external]] void mvar(int, ...) {} }; @@ -129,6 +134,10 @@ template<typename... Ts> [[clang::sycl_external]] void pack(Ts...) {} template void pack(int); +// expected-warning@+2{{'clang::sycl_external' attribute ignored; a variadic function cannot be called from device code}} +template<typename T> +[[clang::sycl_external]] void tvar(T, ...) {} + struct NonCopyable { ~NonCopyable() = delete; [[clang::sycl_external]] NonCopyable(const NonCopyable&) = default; diff --git a/clang/test/SemaSYCL/variadic-func-call.cpp b/clang/test/SemaSYCL/variadic-func-call.cpp index a9ce19dad226d3..a8e5143a7880d8 100644 --- a/clang/test/SemaSYCL/variadic-func-call.cpp +++ b/clang/test/SemaSYCL/variadic-func-call.cpp @@ -15,6 +15,9 @@ namespace NS { void variadic(int, ...) {} } +// expected-warning@+1{{'clang::sycl_external' attribute ignored; a variadic function cannot be called from device code}} +[[clang::sycl_external]] void external_variadic(int, ...) {} + struct S { S(int, ...) {} void operator()(int, ...) {} @@ -33,6 +36,7 @@ int main() { variadic(5); //expected-error{{SYCL device code does not support variadic functions}} variadic(5, 2); //expected-error{{SYCL device code does not support variadic functions}} NS::variadic(5, 3); //expected-error{{SYCL device code does not support variadic functions}} + external_variadic(5, 4); //expected-error{{SYCL device code does not support variadic functions}} S s(5, 4); //expected-error{{SYCL device code does not support variadic functions}} S s2(5); //expected-error{{SYCL device code does not support variadic functions}} s(5, 5); //expected-error{{SYCL device code does not support variadic functions}} >From 519113cc345eed13dd22b0f2ca67bbdbbb6f17b5 Mon Sep 17 00:00:00 2001 From: Sindhu Chittireddy <[email protected]> Date: Fri, 21 Aug 2026 13:25:14 -0700 Subject: [PATCH 3/5] Keep the warning but stick to the original (non-handler) design. --- clang/include/clang/Sema/SemaSYCL.h | 5 ----- clang/lib/Sema/SemaDecl.cpp | 4 +++- clang/lib/Sema/SemaDeclAttr.cpp | 2 +- clang/lib/Sema/SemaSYCL.cpp | 22 +++++++++---------- .../spirv-host-adaptation-valist.cpp | 14 +++++++----- .../sycl-external-attr-variadic.cpp | 21 ++++++++++++++++++ 6 files changed, 44 insertions(+), 24 deletions(-) create mode 100644 clang/test/CodeGenSYCL/sycl-external-attr-variadic.cpp diff --git a/clang/include/clang/Sema/SemaSYCL.h b/clang/include/clang/Sema/SemaSYCL.h index 7d3b5653964f05..4980aa44c30129 100644 --- a/clang/include/clang/Sema/SemaSYCL.h +++ b/clang/include/clang/Sema/SemaSYCL.h @@ -62,11 +62,6 @@ class SemaSYCL : public SemaBase { ParsedType ParsedTy); void handleKernelAttr(Decl *D, const ParsedAttr &AL); - - /// Adds a SYCLExternalAttr to 'D', or warns that the attribute is ignored if - /// 'D' is a variadic function. - void handleExternalAttr(Decl *D, const ParsedAttr &AL); - void handleKernelEntryPointAttr(Decl *D, const ParsedAttr &AL); /// Issues a deferred diagnostic if use of the declaration designated diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index 032737c7a191de..17fecd28058869 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -4243,8 +4243,10 @@ bool Sema::MergeFunctionDecl(FunctionDecl *New, NamedDecl *&OldD, Scope *S, // 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. + // The attribute is ignored and dropped for a variadic function, so 'Old' + // won't have it; 'New' still does since the drop happens after this check. const SYCLExternalAttr *SEA = New->getAttr<SYCLExternalAttr>(); - if (SEA && !Old->hasAttr<SYCLExternalAttr>()) { + if (SEA && !New->isVariadic() && !Old->hasAttr<SYCLExternalAttr>()) { Diag(SEA->getLocation(), diag::warn_sycl_external_missing_on_first_decl) << SEA; Diag(Old->getLocation(), diag::note_previous_declaration); diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp index 06461220658d3d..0645f994924334 100644 --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -7846,7 +7846,7 @@ ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, const ParsedAttr &AL, S.SYCL().handleKernelAttr(D, AL); break; case ParsedAttr::AT_SYCLExternal: - S.SYCL().handleExternalAttr(D, AL); + handleSimpleAttribute<SYCLExternalAttr>(S, D, AL); break; case ParsedAttr::AT_SYCLKernelEntryPoint: S.SYCL().handleKernelEntryPointAttr(D, AL); diff --git a/clang/lib/Sema/SemaSYCL.cpp b/clang/lib/Sema/SemaSYCL.cpp index a96cb060cee4ca..29453e57e8f0fc 100644 --- a/clang/lib/Sema/SemaSYCL.cpp +++ b/clang/lib/Sema/SemaSYCL.cpp @@ -213,18 +213,6 @@ void SemaSYCL::handleKernelAttr(Decl *D, const ParsedAttr &AL) { handleSimpleAttribute<SYCLKernelAttr>(*this, D, AL); } -void SemaSYCL::handleExternalAttr(Decl *D, const ParsedAttr &AL) { - // Device code can neither call a variadic function nor take its address, so - // there is no reason to emit device code for one. Since SYCL 2020 prohibits - // only the call, ignore the attribute rather than reject the declaration. - if (cast<FunctionDecl>(D)->isVariadic()) { - Diag(AL.getLoc(), diag::warn_sycl_external_ignored_variadic_function) << AL; - return; - } - - handleSimpleAttribute<SYCLExternalAttr>(*this, D, AL); -} - void SemaSYCL::handleKernelEntryPointAttr(Decl *D, const ParsedAttr &AL) { ParsedType PT = AL.getTypeArg(); TypeSourceInfo *TSI = nullptr; @@ -293,6 +281,16 @@ static bool CheckSYCLKernelName(Sema &S, SourceLocation Loc, void SemaSYCL::CheckSYCLExternalFunctionDecl(FunctionDecl *FD) { const auto *SEAttr = FD->getAttr<SYCLExternalAttr>(); assert(SEAttr && "Missing sycl_external attribute"); + // SYCL 2020 section 5.4 prohibits calling a variadic function from device + // code, so the attribute cannot be honored; declaring one is not prohibited, + // so ignore the attribute rather than reject the declaration. + if (FD->isVariadic()) { + Diag(SEAttr->getLocation(), + diag::warn_sycl_external_ignored_variadic_function) + << SEAttr; + FD->dropAttr<SYCLExternalAttr>(); + return; + } if (!FD->isInvalidDecl() && !FD->isTemplated()) { if (!FD->isExternallyVisible()) if (!FD->isFunctionTemplateSpecialization() || diff --git a/clang/test/CodeGenSPIRV/spirv-host-adaptation-valist.cpp b/clang/test/CodeGenSPIRV/spirv-host-adaptation-valist.cpp index 799eaf257af713..359da08ba86d5e 100644 --- a/clang/test/CodeGenSPIRV/spirv-host-adaptation-valist.cpp +++ b/clang/test/CodeGenSPIRV/spirv-host-adaptation-valist.cpp @@ -5,10 +5,11 @@ // RUN: %clang_cc1 -triple spirv64-unknown-unknown -aux-triple x86_64-pc-windows-msvc \ // RUN: -fsycl-is-device -emit-llvm -o - %s | FileCheck --check-prefix=WINDOWS %s -/// No variadic functions in SYCL device code, so 'ap' comes from the caller. -[[clang::sycl_external]] int f(__builtin_va_list *ap) { +/// The sycl_external attribute is ignored for a variadic function, so 'f' is +/// emitted because 'g' references it. +int f(int n, ...) { __builtin_va_list ap1, ap2; - __builtin_va_copy(ap1, *ap); + __builtin_va_start(ap1, n); int v = __builtin_va_arg(ap1, int); __builtin_va_copy(ap2, ap1); __builtin_va_end(ap1); @@ -16,10 +17,13 @@ return v; } -// LINUX: define {{.*}} i32 @_Z1fPA1_13__va_list_tag( +using FP = int (*)(int, ...); +[[clang::sycl_external]] FP g() { return f; } + +// LINUX: define {{.*}} i32 @_Z1fiz(i32 noundef %n, ...) {{.*}} { // LINUX: %ap1 = alloca [1 x %struct.__va_list_tag], align 8 // LINUX: %ap2 = alloca [1 x %struct.__va_list_tag], align 8 -// WINDOWS: define {{.*}} i32 @_Z1fPPc( +// WINDOWS: define {{.*}} i32 @_Z1fiz(i32 noundef %n, ...) {{.*}} { // WINDOWS: %ap1 = alloca ptr addrspace(4), align 8 // WINDOWS: %ap2 = alloca ptr addrspace(4), align 8 diff --git a/clang/test/CodeGenSYCL/sycl-external-attr-variadic.cpp b/clang/test/CodeGenSYCL/sycl-external-attr-variadic.cpp new file mode 100644 index 00000000000000..f2c8ecdbc679b1 --- /dev/null +++ b/clang/test/CodeGenSYCL/sycl-external-attr-variadic.cpp @@ -0,0 +1,21 @@ +// RUN: %clang_cc1 -fsycl-is-device -triple spir64-unknown-unknown \ +// RUN: -disable-llvm-passes -emit-llvm -verify %s -o - | FileCheck %s + +// This test checks that the sycl_external attribute is ignored for variadic +// functions; no device code is emitted for such functions. + +// expected-warning@+1{{'clang::sycl_external' attribute ignored; a variadic function cannot be called from device code}} +[[clang::sycl_external]] int variadic(int n, ...) { return n; } +// CHECK-NOT: @_Z8variadiciz + +int reachedFromVariadicOnly() { return 1; } +// CHECK-NOT: @_Z23reachedFromVariadicOnlyv + +// expected-warning@+1{{'clang::sycl_external' attribute ignored; a variadic function cannot be called from device code}} +[[clang::sycl_external]] int variadicCaller(int n, ...) { + return reachedFromVariadicOnly(); +} +// CHECK-NOT: @_Z14variadicCalleriz + +// CHECK: define dso_local spir_func noundef i32 @_Z11nonVariadici +[[clang::sycl_external]] int nonVariadic(int n) { return n; } >From ee824cf6155fbc4b025dee1eedca1718745a1026 Mon Sep 17 00:00:00 2001 From: Sindhu Chittireddy <[email protected]> Date: Thu, 3 Sep 2026 09:46:26 -0700 Subject: [PATCH 4/5] [clang][SYCL] Address review feedback --- clang/include/clang/Sema/SemaSYCL.h | 1 + clang/lib/Sema/SemaDecl.cpp | 4 +--- clang/lib/Sema/SemaDeclAttr.cpp | 2 +- clang/lib/Sema/SemaSYCL.cpp | 18 ++++++++------- .../sycl-external-attr-variadic.cpp | 22 ++++++++++--------- .../sycl-external-attr-variadic-undefined.cpp | 15 +++++++++++++ clang/test/SemaSYCL/sycl-external-attr.cpp | 5 +++++ 7 files changed, 45 insertions(+), 22 deletions(-) create mode 100644 clang/test/SemaSYCL/sycl-external-attr-variadic-undefined.cpp diff --git a/clang/include/clang/Sema/SemaSYCL.h b/clang/include/clang/Sema/SemaSYCL.h index 4980aa44c30129..f4e4a5ef3e4adf 100644 --- a/clang/include/clang/Sema/SemaSYCL.h +++ b/clang/include/clang/Sema/SemaSYCL.h @@ -61,6 +61,7 @@ class SemaSYCL : public SemaBase { SourceLocation RParen, ParsedType ParsedTy); + void handleSYCLExternalAttr(Decl *D, const ParsedAttr &AL); void handleKernelAttr(Decl *D, const ParsedAttr &AL); void handleKernelEntryPointAttr(Decl *D, const ParsedAttr &AL); diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index 17fecd28058869..032737c7a191de 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -4243,10 +4243,8 @@ bool Sema::MergeFunctionDecl(FunctionDecl *New, NamedDecl *&OldD, Scope *S, // 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. - // The attribute is ignored and dropped for a variadic function, so 'Old' - // won't have it; 'New' still does since the drop happens after this check. const SYCLExternalAttr *SEA = New->getAttr<SYCLExternalAttr>(); - if (SEA && !New->isVariadic() && !Old->hasAttr<SYCLExternalAttr>()) { + if (SEA && !Old->hasAttr<SYCLExternalAttr>()) { Diag(SEA->getLocation(), diag::warn_sycl_external_missing_on_first_decl) << SEA; Diag(Old->getLocation(), diag::note_previous_declaration); diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp index 0645f994924334..d2a830f35255f0 100644 --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -7846,7 +7846,7 @@ ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, const ParsedAttr &AL, S.SYCL().handleKernelAttr(D, AL); break; case ParsedAttr::AT_SYCLExternal: - handleSimpleAttribute<SYCLExternalAttr>(S, D, AL); + S.SYCL().handleSYCLExternalAttr(D, AL); break; case ParsedAttr::AT_SYCLKernelEntryPoint: S.SYCL().handleKernelEntryPointAttr(D, AL); diff --git a/clang/lib/Sema/SemaSYCL.cpp b/clang/lib/Sema/SemaSYCL.cpp index 29453e57e8f0fc..be487d6a169e4f 100644 --- a/clang/lib/Sema/SemaSYCL.cpp +++ b/clang/lib/Sema/SemaSYCL.cpp @@ -278,19 +278,21 @@ static bool CheckSYCLKernelName(Sema &S, SourceLocation Loc, return false; } -void SemaSYCL::CheckSYCLExternalFunctionDecl(FunctionDecl *FD) { - const auto *SEAttr = FD->getAttr<SYCLExternalAttr>(); - assert(SEAttr && "Missing sycl_external attribute"); +void SemaSYCL::handleSYCLExternalAttr(Decl *D, const ParsedAttr &AL) { // SYCL 2020 section 5.4 prohibits calling a variadic function from device // code, so the attribute cannot be honored; declaring one is not prohibited, // so ignore the attribute rather than reject the declaration. - if (FD->isVariadic()) { - Diag(SEAttr->getLocation(), - diag::warn_sycl_external_ignored_variadic_function) - << SEAttr; - FD->dropAttr<SYCLExternalAttr>(); + if (cast<FunctionDecl>(D)->isVariadic()) { + Diag(AL.getLoc(), diag::warn_sycl_external_ignored_variadic_function) << AL; return; } + + handleSimpleAttribute<SYCLExternalAttr>(*this, D, AL); +} + +void SemaSYCL::CheckSYCLExternalFunctionDecl(FunctionDecl *FD) { + const auto *SEAttr = FD->getAttr<SYCLExternalAttr>(); + assert(SEAttr && "Missing sycl_external attribute"); if (!FD->isInvalidDecl() && !FD->isTemplated()) { if (!FD->isExternallyVisible()) if (!FD->isFunctionTemplateSpecialization() || diff --git a/clang/test/CodeGenSYCL/sycl-external-attr-variadic.cpp b/clang/test/CodeGenSYCL/sycl-external-attr-variadic.cpp index f2c8ecdbc679b1..e45af97492447e 100644 --- a/clang/test/CodeGenSYCL/sycl-external-attr-variadic.cpp +++ b/clang/test/CodeGenSYCL/sycl-external-attr-variadic.cpp @@ -1,21 +1,23 @@ // RUN: %clang_cc1 -fsycl-is-device -triple spir64-unknown-unknown \ -// RUN: -disable-llvm-passes -emit-llvm -verify %s -o - | FileCheck %s +// RUN: -disable-llvm-passes -emit-llvm -verify %s -o - | FileCheck %s \ +// RUN: --implicit-check-not=variadic_fn_test1 \ +// RUN: --implicit-check-not=reached_from_variadic_fn_test2 \ +// RUN: --implicit-check-not=variadic_caller_fn_test3 // This test checks that the sycl_external attribute is ignored for variadic // functions; no device code is emitted for such functions. // expected-warning@+1{{'clang::sycl_external' attribute ignored; a variadic function cannot be called from device code}} -[[clang::sycl_external]] int variadic(int n, ...) { return n; } -// CHECK-NOT: @_Z8variadiciz +[[clang::sycl_external]] int variadic_fn_test1(int n, ...) { return n; } -int reachedFromVariadicOnly() { return 1; } -// CHECK-NOT: @_Z23reachedFromVariadicOnlyv +int reached_from_variadic_fn_test2() { return 1; } // expected-warning@+1{{'clang::sycl_external' attribute ignored; a variadic function cannot be called from device code}} -[[clang::sycl_external]] int variadicCaller(int n, ...) { - return reachedFromVariadicOnly(); +[[clang::sycl_external]] int variadic_caller_fn_test3(int n, ...) { + return reached_from_variadic_fn_test2(); } -// CHECK-NOT: @_Z14variadicCalleriz -// CHECK: define dso_local spir_func noundef i32 @_Z11nonVariadici -[[clang::sycl_external]] int nonVariadic(int n) { return n; } +// Check that non-variadic functions are emitted to ensure the lack of emission +// for the others is due to their being variadic functions. +[[clang::sycl_external]] int non_variadic_fn_test4(int n) { return n; } +// CHECK: define dso_local spir_func noundef i32 @{{.*}}non_variadic_fn_test4 diff --git a/clang/test/SemaSYCL/sycl-external-attr-variadic-undefined.cpp b/clang/test/SemaSYCL/sycl-external-attr-variadic-undefined.cpp new file mode 100644 index 00000000000000..8039017ae2b7ec --- /dev/null +++ b/clang/test/SemaSYCL/sycl-external-attr-variadic-undefined.cpp @@ -0,0 +1,15 @@ +// RUN: %clang_cc1 -fsycl-is-device -triple spir64-unknown-unknown -fsyntax-only \ +// RUN: -verify %s + +// This test checks the diagnostics issued for an ODR-use of a variadic function +// declared, but not defined, with the ignored sycl_external attribute. + +// expected-warning@+1{{'clang::sycl_external' attribute ignored; a variadic function cannot be called from device code}} +[[clang::sycl_external]] void undefined_variadic_fn_test1(int, ...); + +[[clang::sycl_external]] void use_undefined_variadic_fn_test2() { + // FIXME: Clang does not diagnose an ODR-use of a function that is neither + // defined in this translation unit nor declared sycl_external. + // expected error once it does. + (void)&undefined_variadic_fn_test1; +} diff --git a/clang/test/SemaSYCL/sycl-external-attr.cpp b/clang/test/SemaSYCL/sycl-external-attr.cpp index a02ae48c6fd9a8..922ca9c328846e 100644 --- a/clang/test/SemaSYCL/sycl-external-attr.cpp +++ b/clang/test/SemaSYCL/sycl-external-attr.cpp @@ -137,6 +137,11 @@ template void pack(int); // expected-warning@+2{{'clang::sycl_external' attribute ignored; a variadic function cannot be called from device code}} template<typename T> [[clang::sycl_external]] void tvar(T, ...) {} +template void tvar(int, ...); +using TVarFP = void (*)(char, ...); +[[clang::sycl_external]] TVarFP tvar_implicit_instantiation() { + return &tvar<char>; +} struct NonCopyable { ~NonCopyable() = delete; >From 312ec03f3d620d5a87c9aea66004863fb52e3435 Mon Sep 17 00:00:00 2001 From: Sindhu Chittireddy <[email protected]> Date: Fri, 23 Oct 2026 09:30:38 -0700 Subject: [PATCH 5/5] Address review comments Clarify the comment in spirv-host-adaptation-valist.cpp, reword the sycl_external documentation for variadic functions, and move the variadic example into the code block. --- clang/include/clang/Basic/AttrDocs.td | 10 +++++----- .../test/CodeGenSPIRV/spirv-host-adaptation-valist.cpp | 5 +++-- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td index ad99e2f85a4047..4491a50daef18d 100644 --- a/clang/include/clang/Basic/AttrDocs.td +++ b/clang/include/clang/Basic/AttrDocs.td @@ -624,9 +624,9 @@ following requirements: The attribute shall be present on the first declaration of a function and may optionally be present on subsequent declarations. -The attribute is ignored, with a warning, when applied to a variadic function. -A variadic function cannot be called from device code, so there is no reason to -emit device code for one. +SYCL prohibits calls to a variadic function in device functions. Since there is +little reason to emit a function that cannot be called, the attribute is ignored, +with a warning, when applied to a variadic function. 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 @@ -642,9 +642,9 @@ The following examples demonstrate the use of this attribute: [[clang::sycl_external]] extern void Baz(); // Ok. [[clang::sycl_external]] static void Quux() { /* ... */ } // error: Quux() has internal linkage. -``` - [[clang::sycl_external]] void Corge(int, ...); // warning: attribute ignored; Corge() is variadic. +[[clang::sycl_external]] void Corge(int, ...); // warning: attribute ignored; Corge() is variadic. +``` }]; } diff --git a/clang/test/CodeGenSPIRV/spirv-host-adaptation-valist.cpp b/clang/test/CodeGenSPIRV/spirv-host-adaptation-valist.cpp index 359da08ba86d5e..814671329423e3 100644 --- a/clang/test/CodeGenSPIRV/spirv-host-adaptation-valist.cpp +++ b/clang/test/CodeGenSPIRV/spirv-host-adaptation-valist.cpp @@ -5,8 +5,9 @@ // RUN: %clang_cc1 -triple spirv64-unknown-unknown -aux-triple x86_64-pc-windows-msvc \ // RUN: -fsycl-is-device -emit-llvm -o - %s | FileCheck --check-prefix=WINDOWS %s -/// The sycl_external attribute is ignored for a variadic function, so 'f' is -/// emitted because 'g' references it. +// SYCL 2020 section 5.4, "Language restrictions for device functions", prohibits +// calls to variadic functions in device code, but doesn't prohibit other ODR-uses. +// Function f() is emitted during device compilation due to the ODR-use in g(). int f(int n, ...) { __builtin_va_list ap1, ap2; __builtin_va_start(ap1, n); _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
