From 176dba1e961600c5d12194affb9fd7fc25167e94 Mon Sep 17 00:00:00 2001 From: Alexey Sachkov Date: Mon, 22 Sep 2025 14:40:36 +0200 Subject: [PATCH 1/2] [SYCL] Fix error with type aliases used as free function kernel args (#20123) This PR fixes type name that is being printed as free function kernel argument type in its forward-declaration in the integration header. Before the change, we used the original argument type name, which could be an alias - this patch makes use of the canonical type's name to make sure that all type aliases are "unwrapped". --- clang/lib/Sema/SemaSYCL.cpp | 8 +-- .../free-function-kernel-type-alias-arg.cpp | 63 +++++++++++++++++++ 2 files changed, 67 insertions(+), 4 deletions(-) create mode 100644 clang/test/CodeGenSYCL/free-function-kernel-type-alias-arg.cpp diff --git a/clang/lib/Sema/SemaSYCL.cpp b/clang/lib/Sema/SemaSYCL.cpp index 18759a0f3ee37..75dce37b81e87 100644 --- a/clang/lib/Sema/SemaSYCL.cpp +++ b/clang/lib/Sema/SemaSYCL.cpp @@ -6793,13 +6793,13 @@ class FreeFunctionPrinter { continue; } - TemplateName TN = TST->getTemplateName(); + TemplateName CTN = CTST->getTemplateName(); + CTN.getAsTemplateDecl()->printQualifiedName(ParmListOstream); + ParmListOstream << "<"; + auto SpecArgs = TST->template_arguments(); auto DeclArgs = CTST->template_arguments(); - TN.getAsTemplateDecl()->printQualifiedName(ParmListOstream); - ParmListOstream << "<"; - for (size_t I = 0, E = std::max(DeclArgs.size(), SpecArgs.size()), SE = SpecArgs.size(); I < E; ++I) { diff --git a/clang/test/CodeGenSYCL/free-function-kernel-type-alias-arg.cpp b/clang/test/CodeGenSYCL/free-function-kernel-type-alias-arg.cpp new file mode 100644 index 0000000000000..d605d8be537bf --- /dev/null +++ b/clang/test/CodeGenSYCL/free-function-kernel-type-alias-arg.cpp @@ -0,0 +1,63 @@ +// RUN: %clang_cc1 -fsycl-is-device -internal-isystem %S/Inputs -triple spir64-unknown-unknown -sycl-std=2020 -fsycl-int-header=%t.h %s +// RUN: FileCheck -input-file=%t.h %s +// +// The purpose of this test is to ensure that forward declarations of free +// function kernels are emitted properly. +// However, this test checks a specific scenario: +// - free function arguments are type aliases (through using or typedef) + +namespace ns { + +using IntUsing = int; +typedef int IntTypedef; + +template +struct Foo {}; + +using FooIntUsing = Foo; +typedef Foo FooIntTypedef; + +template +struct Bar {}; + +template +using BarUsing = Bar; + +class Baz { +public: + using type = BarUsing; +}; + +} // namespace ns + +[[__sycl_detail__::add_ir_attributes_function("sycl-nd-range-kernel", 2)]] +void int_using(ns::IntUsing Arg) {} + +// CHECK: void int_using(int Arg); + +[[__sycl_detail__::add_ir_attributes_function("sycl-nd-range-kernel", 2)]] +void int_typedef(ns::IntTypedef Arg) {} + +// CHECK: void int_typedef(int Arg); + +[[__sycl_detail__::add_ir_attributes_function("sycl-nd-range-kernel", 2)]] +void foo_using(ns::FooIntUsing Arg) {} + +// CHECK: void foo_using(ns::Foo Arg); + +[[__sycl_detail__::add_ir_attributes_function("sycl-nd-range-kernel", 2)]] +void foo_typedef(ns::FooIntTypedef Arg) {} + +// CHECK: void foo_typedef(ns::Foo Arg); + +template +[[__sycl_detail__::add_ir_attributes_function("sycl-nd-range-kernel", 2)]] +void bar_using(ns::BarUsing Arg) {} +template void bar_using(ns::BarUsing); + +// CHECK: template void bar_using(ns::Bar); + +[[__sycl_detail__::add_ir_attributes_function("sycl-nd-range-kernel", 2)]] +void baz_type(ns::Baz::type Arg) {} + +// CHECK: void baz_type(ns::Bar Arg); From 0dcb0d68f02d679c464a7431188d6c3d3b5c1c5f Mon Sep 17 00:00:00 2001 From: Alexey Sachkov Date: Thu, 25 Sep 2025 10:09:29 +0200 Subject: [PATCH 2/2] [SYCL] Allow free function kernel args be templated on integer expressions (#20187) `constexpr` variables are not forward-declarable so if one is used as a template parameter of a free function kernel argument, we cannot reference the variable, but must inline the value into the integration header. --- clang/lib/Sema/SemaSYCL.cpp | 44 +++++-- ...e-function-kernel-expr-as-template-arg.cpp | 110 ++++++++++++++++++ 2 files changed, 145 insertions(+), 9 deletions(-) create mode 100644 clang/test/CodeGenSYCL/free-function-kernel-expr-as-template-arg.cpp diff --git a/clang/lib/Sema/SemaSYCL.cpp b/clang/lib/Sema/SemaSYCL.cpp index 75dce37b81e87..c6bbfaa2f83ea 100644 --- a/clang/lib/Sema/SemaSYCL.cpp +++ b/clang/lib/Sema/SemaSYCL.cpp @@ -6597,10 +6597,12 @@ class FreeFunctionPrinter { raw_ostream &O; PrintingPolicy &Policy; bool NSInserted = false; + ASTContext &Context; public: - FreeFunctionPrinter(raw_ostream &O, PrintingPolicy &PrintPolicy) - : O(O), Policy(PrintPolicy) {} + FreeFunctionPrinter(raw_ostream &O, PrintingPolicy &PrintPolicy, + ASTContext &Context) + : O(O), Policy(PrintPolicy), Context(Context) {} /// Emits the function declaration of template free function. /// \param FTD The function declaration to print. @@ -6797,18 +6799,42 @@ class FreeFunctionPrinter { CTN.getAsTemplateDecl()->printQualifiedName(ParmListOstream); ParmListOstream << "<"; - auto SpecArgs = TST->template_arguments(); - auto DeclArgs = CTST->template_arguments(); + ArrayRef SpecArgs = TST->template_arguments(); + ArrayRef DeclArgs = CTST->template_arguments(); + + auto TemplateArgPrinter = [&](const TemplateArgument &Arg) { + if (Arg.getKind() != TemplateArgument::ArgKind::Expression || + Arg.isInstantiationDependent()) { + Arg.print(Policy, ParmListOstream, /* IncludeType = */ false); + return; + } + + Expr *E = Arg.getAsExpr(); + assert(E && "Failed to get an Expr for an Expression template arg?"); + if (E->getType().getTypePtr()->isScopedEnumeralType()) { + // Scoped enumerations can't be implicitly cast from integers, so + // we don't need to evaluate them. + Arg.print(Policy, ParmListOstream, /* IncludeType = */ false); + return; + } + + Expr::EvalResult Res; + [[maybe_unused]] bool Success = + Arg.getAsExpr()->EvaluateAsConstantExpr(Res, Context); + assert(Success && "invalid non-type template argument?"); + assert(!Res.Val.isAbsent() && "couldn't read the evaulation result?"); + Res.Val.printPretty(ParmListOstream, Policy, Arg.getAsExpr()->getType(), + &Context); + }; for (size_t I = 0, E = std::max(DeclArgs.size(), SpecArgs.size()), SE = SpecArgs.size(); I < E; ++I) { if (I != 0) ParmListOstream << ", "; - if (I < SE) // A specialized argument exists, use it - SpecArgs[I].print(Policy, ParmListOstream, false /* IncludeType */); - else // Print a canonical form of a default argument - DeclArgs[I].print(Policy, ParmListOstream, false /* IncludeType */); + // If we have a specialized argument, use it. Otherwise fallback to a + // default argument. + TemplateArgPrinter(I < SE ? SpecArgs[I] : DeclArgs[I]); } ParmListOstream << ">"; @@ -7207,7 +7233,7 @@ void SYCLIntegrationHeader::emit(raw_ostream &O) { // template arguments that match default template arguments while printing // template-ids, even if the source code doesn't reference them. Policy.EnforceDefaultTemplateArgs = true; - FreeFunctionPrinter FFPrinter(O, Policy); + FreeFunctionPrinter FFPrinter(O, Policy, S.getASTContext()); if (FTD) { FFPrinter.printFreeFunctionDeclaration(FTD); if (const auto kind = K.SyclKernel->getTemplateSpecializationKind(); diff --git a/clang/test/CodeGenSYCL/free-function-kernel-expr-as-template-arg.cpp b/clang/test/CodeGenSYCL/free-function-kernel-expr-as-template-arg.cpp new file mode 100644 index 0000000000000..81120ef702d52 --- /dev/null +++ b/clang/test/CodeGenSYCL/free-function-kernel-expr-as-template-arg.cpp @@ -0,0 +1,110 @@ +// RUN: %clang_cc1 -fsycl-is-device -internal-isystem %S/Inputs -triple spir64-unknown-unknown -sycl-std=2020 -fsycl-int-header=%t.h %s +// RUN: FileCheck -input-file=%t.h %s +// +// The purpose of this test is to ensure that forward declarations of free +// function kernels are emitted properly. +// However, this test checks a specific scenario: +// - free function argument is a template which accepts constant expressions as +// arguments + +constexpr int A = 2; +constexpr int B = 3; + +namespace ns { + +constexpr int C = 4; + +struct Foo { + static constexpr int D = 5; +}; + +enum non_class_enum { + VAL_A, + VAL_B +}; + +enum class class_enum { + VAL_A, + VAL_B +}; + +enum non_class_enum_typed : int { + VAL_C, + VAL_D +}; + +enum class class_enum_typed : int { + VAL_C, + VAL_D +}; + +constexpr int bar(int arg) { + return arg + 42; +} + +} // namespace ns + +template +struct Arg {}; + +template +struct Arg2 {}; + +template +struct Arg3 {}; + +template +struct Arg4 {}; + +template +struct Arg5 {}; + +[[__sycl_detail__::add_ir_attributes_function("sycl-nd-range-kernel", 2)]] +void constant(Arg<1>) {} + +// CHECK: void constant(Arg<1> ); + +[[__sycl_detail__::add_ir_attributes_function("sycl-nd-range-kernel", 2)]] +void constexpr_v(Arg) {} + +// CHECK: void constexpr_v(Arg<2> ); + +[[__sycl_detail__::add_ir_attributes_function("sycl-nd-range-kernel", 2)]] +void constexpr_expr(Arg) {} + +// CHECK: void constexpr_expr(Arg<6> ); + +[[__sycl_detail__::add_ir_attributes_function("sycl-nd-range-kernel", 2)]] +void constexpr_ns(Arg) {} + +// CHECK: void constexpr_ns(Arg<4> ); + +[[__sycl_detail__::add_ir_attributes_function("sycl-nd-range-kernel", 2)]] +void constexpr_ns2(Arg) {} + +// CHECK: void constexpr_ns2(Arg<5> ); + +[[__sycl_detail__::add_ir_attributes_function("sycl-nd-range-kernel", 2)]] +void constexpr_ns2(Arg2) {} + +// CHECK: void constexpr_ns2(Arg2 ); + +[[__sycl_detail__::add_ir_attributes_function("sycl-nd-range-kernel", 2)]] +void constexpr_ns2(Arg3) {} + +// CHECK: void constexpr_ns2(Arg3 ); + +[[__sycl_detail__::add_ir_attributes_function("sycl-nd-range-kernel", 2)]] +void constexpr_ns2(Arg4) {} + +// CHECK: void constexpr_ns2(Arg4 ); + +[[__sycl_detail__::add_ir_attributes_function("sycl-nd-range-kernel", 2)]] +void constexpr_ns2(Arg5) {} + +// CHECK: void constexpr_ns2(Arg5 ); + +[[__sycl_detail__::add_ir_attributes_function("sycl-nd-range-kernel", 2)]] +void constexpr_call(Arg) {} + +// CHECK: void constexpr_call(Arg<45> );