Skip to content

Commit 42dfab4

Browse files
committed
[SYCL] Specialization constants support in the Front End.
1. Detect kernel lambda object captures corresponding to specialization constants and (a) don't create kernel arguments for them (b) generate specializations of the SpecConstantInfo structure into the integration header. 2. Recognize the __sycl_fe_getStableUniqueTypeName intrinsic and replace it with a string literal uniquely identifying the type of the typename template parameter to this intrinsic. 3. FE-related changes in the runtime: - new SpecConstantInfo templated struct for type->name translation for specialization constants used by integration header - define the __sycl_fe_getStableUniqueTypeName intrinsic Signed-off-by: Konstantin S Bobrovsky <[email protected]>
1 parent b6d1acb commit 42dfab4

File tree

7 files changed

+208
-9
lines changed

7 files changed

+208
-9
lines changed

clang/include/clang/Sema/Sema.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -339,6 +339,9 @@ class SYCLIntegrationHeader {
339339
/// invocation descriptor has finished.
340340
void endKernel();
341341

342+
/// Registers a specialization constant to emit info for it into the header.
343+
void addSpecConstant(StringRef IDName, QualType IDType);
344+
342345
private:
343346
// Kernel actual parameter descriptor.
344347
struct KernelParamDesc {
@@ -407,6 +410,13 @@ class SYCLIntegrationHeader {
407410
/// SYCLIntegrationHeader::startKernel
408411
SmallVector<KernelDesc, 4> KernelDescs;
409412

413+
using SpecConstID = std::pair<QualType, std::string>;
414+
415+
/// Keeps specialization constants met in the translation unit. Maps spec
416+
/// constant's ID type to generated unique name. Duplicates are removed at
417+
/// integration header emission time.
418+
llvm::SmallVector<SpecConstID, 4> SpecConsts;
419+
410420
/// Used for emitting diagnostics.
411421
DiagnosticsEngine &Diag;
412422

clang/lib/CodeGen/CGExpr.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#include "CGObjCRuntime.h"
1818
#include "CGOpenMPRuntime.h"
1919
#include "CGRecordLayout.h"
20+
#include "CGSYCLRuntime.h"
2021
#include "CodeGenFunction.h"
2122
#include "CodeGenModule.h"
2223
#include "ConstantEmitter.h"

clang/lib/Sema/SemaSYCL.cpp

Lines changed: 86 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,10 @@ class Util {
7878
/// \param Tmpl whether the class is template instantiation or simple record
7979
static bool isSyclType(const QualType &Ty, StringRef Name, bool Tmpl = false);
8080

81+
/// Checks whether given clang type is a full specialization of the SYCL
82+
/// specialization constant class.
83+
static bool isSyclSpecConstantType(const QualType &Ty);
84+
8185
/// Checks whether given clang type is declared in the given hierarchy of
8286
/// declaration contexts.
8387
/// \param Ty the clang type being checked
@@ -773,6 +777,14 @@ static CompoundStmt *CreateOpenCLKernelBody(Sema &S,
773777
getExprForSpecialSYCLObj(FldType, WrapperFld,
774778
WrapperFldCRD, Base,
775779
InitMethodName, BodyStmts);
780+
} else if (Util::isSyclSpecConstantType(FldType)) {
781+
// Specialization constants are "invisible" to the
782+
// kernel argument creation and device-side SYCL object
783+
// materialization infrastructure in this source.
784+
// It is OK not to really materialize them on the kernel
785+
// side, because their only use can be via
786+
// 'spec_const_obj.get()' method, which is translated to
787+
// an intrinsic and 'this' is really never used.
776788
} else {
777789
// Field is a structure or class so change the wrapper
778790
// object and recursively search for accessor field.
@@ -816,6 +828,8 @@ static CompoundStmt *CreateOpenCLKernelBody(Sema &S,
816828
InitExprs.push_back(MemberInit.get());
817829
getExprForSpecialSYCLObj(FieldType, Field, CRD, KernelObjCloneRef,
818830
InitMethodName, BodyStmts);
831+
} else if (Util::isSyclSpecConstantType(FieldType)) {
832+
// Just skip specialization constants - not part of signature.
819833
} else if (CRD || FieldType->isScalarType()) {
820834
// If field has built-in or a structure/class type just initialize
821835
// this field with corresponding kernel argument using copy
@@ -959,11 +973,13 @@ static bool buildArgTys(ASTContext &Context, CXXRecordDecl *KernelObj,
959973
QualType FldType = WrapperFld->getType();
960974
if (FldType->isStructureOrClassType()) {
961975
if (Util::isSyclAccessorType(FldType)) {
962-
// accessor field is found - create descriptor
976+
// Accessor field is found - create descriptor.
963977
createSpecialSYCLObjParamDesc(WrapperFld, FldType);
978+
} else if (Util::isSyclSpecConstantType(FldType)) {
979+
// Don't try recursive search below.
964980
} else {
965-
// field is some class or struct - recursively check for
966-
// accessor fields
981+
// Field is some class or struct - recursively check for
982+
// accessor fields.
967983
createParamDescForWrappedAccessors(WrapperFld, FldType);
968984
}
969985
}
@@ -985,6 +1001,8 @@ static bool buildArgTys(ASTContext &Context, CXXRecordDecl *KernelObj,
9851001
QualType ArgTy = Fld->getType();
9861002
if (Util::isSyclAccessorType(ArgTy) || Util::isSyclSamplerType(ArgTy)) {
9871003
createSpecialSYCLObjParamDesc(Fld, ArgTy);
1004+
} else if (Util::isSyclSpecConstantType(ArgTy)) {
1005+
// Specialization constants are not added as arguments.
9881006
} else if (ArgTy->isStructureOrClassType()) {
9891007
if (Context.getLangOpts().SYCLStdLayoutKernelParams) {
9901008
if (!ArgTy->isStandardLayoutType()) {
@@ -1127,6 +1145,21 @@ static void populateIntHeader(SYCLIntegrationHeader &H, const StringRef Name,
11271145
uint64_t Sz = Ctx.getTypeSizeInChars(Fld->getType()).getQuantity();
11281146
H.addParamDesc(SYCLIntegrationHeader::kind_pointer,
11291147
static_cast<unsigned>(Sz), static_cast<unsigned>(Offset));
1148+
} else if (Util::isSyclSpecConstantType(ArgTy)) {
1149+
// Add specialization constant ID to the header.
1150+
auto *TmplSpec =
1151+
cast<ClassTemplateSpecializationDecl>(ArgTy->getAsCXXRecordDecl());
1152+
const TemplateArgumentList *TemplateArgs =
1153+
&TmplSpec->getTemplateInstantiationArgs();
1154+
// Get specialization constant ID type, which is the second template
1155+
// argument.
1156+
QualType SpecConstIDTy = TypeName::getFullyQualifiedType(
1157+
TemplateArgs->get(1).getAsType(), Ctx, true)
1158+
.getCanonicalType();
1159+
const std::string SpecConstName = PredefinedExpr::ComputeName(
1160+
Ctx, PredefinedExpr::UniqueStableNameExpr, SpecConstIDTy);
1161+
H.addSpecConstant(SpecConstName, SpecConstIDTy);
1162+
// Spec constant lambda capture does not become a kernel argument.
11301163
} else if (ArgTy->isStructureOrClassType() || ArgTy->isScalarType()) {
11311164
// the parameter is an object of standard layout type or scalar;
11321165
// the check for standard layout is done elsewhere
@@ -1658,6 +1691,13 @@ void SYCLIntegrationHeader::emitForwardClassDecls(
16581691
}
16591692
}
16601693

1694+
static std::string getCPPTypeString(QualType Ty) {
1695+
LangOptions LO;
1696+
PrintingPolicy P(LO);
1697+
P.SuppressTypedefs = true;
1698+
return eraseAnonNamespace(Ty.getAsString(P));
1699+
}
1700+
16611701
void SYCLIntegrationHeader::emit(raw_ostream &O) {
16621702
O << "// This is auto-generated SYCL integration header.\n";
16631703
O << "\n";
@@ -1666,6 +1706,33 @@ void SYCLIntegrationHeader::emit(raw_ostream &O) {
16661706
O << "#include <CL/sycl/detail/kernel_desc.hpp>\n";
16671707

16681708
O << "\n";
1709+
1710+
if (SpecConsts.size() > 0) {
1711+
// Remove duplicates.
1712+
std::sort(SpecConsts.begin(), SpecConsts.end(),
1713+
[](const SpecConstID &SC1, const SpecConstID &SC2) {
1714+
// Sort by string IDs for stable spec consts order in the
1715+
// header.
1716+
return SC1.second.compare(SC2.second) < 0;
1717+
});
1718+
SpecConstID *End =
1719+
std::unique(SpecConsts.begin(), SpecConsts.end(),
1720+
[](const SpecConstID &SC1, const SpecConstID &SC2) {
1721+
// Here can do faster comparison of types.
1722+
return SC1.first == SC2.first;
1723+
});
1724+
O << "// Specialization constants IDs:\n";
1725+
for (const auto &P : llvm::make_range(SpecConsts.begin(), End)) {
1726+
std::string CPPName = getCPPTypeString(P.first);
1727+
O << "template <> struct sycl::detail::SpecConstantInfo<" << CPPName
1728+
<< "> {\n";
1729+
O << " static constexpr const char* getName() {\n";
1730+
O << " return \"" << P.second << "\";\n";
1731+
O << " }\n";
1732+
O << "};\n";
1733+
}
1734+
}
1735+
16691736
if (!UnnamedLambdaSupport) {
16701737
O << "// Forward declarations of templated kernel function types:\n";
16711738

@@ -1747,11 +1814,8 @@ void SYCLIntegrationHeader::emit(raw_ostream &O) {
17471814
O << "', '" << c;
17481815
O << "'> {\n";
17491816
} else {
1750-
LangOptions LO;
1751-
PrintingPolicy P(LO);
1752-
P.SuppressTypedefs = true;
1753-
O << "template <> struct KernelInfo<"
1754-
<< eraseAnonNamespace(K.NameType.getAsString(P)) << "> {\n";
1817+
O << "template <> struct KernelInfo<" << getCPPTypeString(K.NameType)
1818+
<< "> {\n";
17551819
}
17561820
O << " DLL_LOCAL\n";
17571821
O << " static constexpr const char* getName() { return \"" << K.Name
@@ -1815,6 +1879,10 @@ void SYCLIntegrationHeader::endKernel() {
18151879
// nop for now
18161880
}
18171881

1882+
void SYCLIntegrationHeader::addSpecConstant(StringRef IDName, QualType IDType) {
1883+
SpecConsts.emplace_back(std::make_pair(IDType, IDName.str()));
1884+
}
1885+
18181886
SYCLIntegrationHeader::SYCLIntegrationHeader(DiagnosticsEngine &_Diag,
18191887
bool _UnnamedLambdaSupport)
18201888
: Diag(_Diag), UnnamedLambdaSupport(_UnnamedLambdaSupport) {}
@@ -1835,6 +1903,16 @@ bool Util::isSyclStreamType(const QualType &Ty) {
18351903
return isSyclType(Ty, "stream");
18361904
}
18371905

1906+
bool Util::isSyclSpecConstantType(const QualType &Ty) {
1907+
const StringRef &Name = "spec_constant";
1908+
std::array<DeclContextDesc, 4> Scopes = {
1909+
Util::DeclContextDesc{clang::Decl::Kind::Namespace, "cl"},
1910+
Util::DeclContextDesc{clang::Decl::Kind::Namespace, "sycl"},
1911+
Util::DeclContextDesc{clang::Decl::Kind::Namespace, "experimental"},
1912+
Util::DeclContextDesc{Decl::Kind::ClassTemplateSpecialization, Name}};
1913+
return matchQualifiedTypeName(Ty, Scopes);
1914+
}
1915+
18381916
bool Util::isSyclType(const QualType &Ty, StringRef Name, bool Tmpl) {
18391917
Decl::Kind ClassDeclKind =
18401918
Tmpl ? Decl::Kind::ClassTemplateSpecialization : Decl::Kind::CXXRecord;

clang/test/CodeGenSYCL/Inputs/sycl.hpp

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,23 @@ template <typename Type>
241241
struct get_kernel_name_t<auto_name, Type> {
242242
using name = Type;
243243
};
244+
245+
namespace experimental {
246+
template <typename T, typename ID = T>
247+
class spec_constant {
248+
public:
249+
spec_constant() {}
250+
spec_constant(T Cst) {}
251+
252+
T get() const { // explicit access.
253+
return T(); // Dummy implementaion.
254+
}
255+
operator T() const { // implicit conversion.
256+
return get();
257+
}
258+
};
259+
} // namespace experimental
260+
244261
#define ATTR_SYCL_KERNEL __attribute__((sycl_kernel))
245262
template <typename KernelName = auto_name, typename KernelType>
246263
ATTR_SYCL_KERNEL void kernel_single_task(KernelType kernelFunc) {
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
// RUN: %clang -I %S/Inputs -fsycl-device-only -Xclang -fsycl-int-header=%t.h %s -c -o kernel.spv
2+
// RUN: FileCheck -input-file=%t.h %s
3+
4+
#include "sycl.hpp"
5+
6+
// This test verifies proper emission of specialization constants into the
7+
// integration header.
8+
9+
class SpecializedKernel;
10+
class MyBoolConst;
11+
class MyInt8Const;
12+
class MyUInt8Const;
13+
class MyInt16Const;
14+
class MyUInt16Const;
15+
class MyInt32Const;
16+
class MyUInt32Const;
17+
18+
class MyFloatConst;
19+
class MyDoubleConst;
20+
21+
int main() {
22+
// Create specialization constants.
23+
cl::sycl::experimental::spec_constant<bool, MyBoolConst> i1(false);
24+
cl::sycl::experimental::spec_constant<char, MyInt8Const> i8(0);
25+
cl::sycl::experimental::spec_constant<unsigned char, MyUInt8Const> ui8(0);
26+
cl::sycl::experimental::spec_constant<short, MyInt16Const> i16(0);
27+
cl::sycl::experimental::spec_constant<unsigned short, MyUInt16Const> ui16(0);
28+
cl::sycl::experimental::spec_constant<int, MyInt32Const> i32(0);
29+
// Constant used twice, but there must be single entry in the int header,
30+
// otherwise compilation error would be issued.
31+
cl::sycl::experimental::spec_constant<int, MyInt32Const> i32_1(0);
32+
cl::sycl::experimental::spec_constant<unsigned int, MyUInt32Const> ui32(0);
33+
cl::sycl::experimental::spec_constant<float, MyFloatConst> f32(0);
34+
cl::sycl::experimental::spec_constant<double, MyDoubleConst> f64(0);
35+
36+
double val;
37+
double *ptr = &val; // to avoid "unused" warnings
38+
39+
cl::sycl::kernel_single_task<SpecializedKernel>([=]() {
40+
*ptr = i1.get() +
41+
// CHECK-DAG: template <> struct sycl::detail::SpecConstantInfo<class MyBoolConst> {
42+
// CHECK-DAG-NEXT: static constexpr const char* getName() {
43+
// CHECK-DAG-NEXT: return "_ZTS11MyBoolConst";
44+
// CHECK-DAG-NEXT: }
45+
// CHECK-DAG-NEXT: };
46+
i8.get() +
47+
// CHECK-DAG: return "_ZTS11MyInt8Const";
48+
ui8.get() +
49+
// CHECK-DAG: return "_ZTS12MyUInt8Const";
50+
i16.get() +
51+
// CHECK-DAG: return "_ZTS12MyInt16Const";
52+
ui16.get() +
53+
// CHECK-DAG: return "_ZTS13MyUInt16Const";
54+
i32.get() +
55+
i32_1.get() +
56+
// CHECK-DAG: return "_ZTS12MyInt32Const";
57+
ui32.get() +
58+
// CHECK-DAG: return "_ZTS13MyUInt32Const";
59+
f32.get() +
60+
// CHECK-DAG: return "_ZTS12MyFloatConst";
61+
f64.get();
62+
// CHECK-DAG: return "_ZTS13MyDoubleConst";
63+
});
64+
}

sycl/include/CL/sycl/detail/kernel_desc.hpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
// See https://llvm.org/LICENSE.txt for license information.
55
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
66
//
7-
//===----------------------------------------------------------------------===////
7+
//===----------------------------------------------------------------------===//
88

99
#pragma once
1010

@@ -41,6 +41,11 @@ struct kernel_param_desc_t {
4141
int offset;
4242
};
4343

44+
// Translates specialization constant type to its name.
45+
template <class Name> struct SpecConstantInfo {
46+
static constexpr const char *getName() { return ""; }
47+
};
48+
4449
#ifndef __SYCL_UNNAMED_LAMBDA__
4550
template <class KernelNameType> struct KernelInfo {
4651
static constexpr unsigned getNumParams() { return 0; }
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
//==------ sycl_fe_intrins.hpp --- SYCL Device Compiler's FE intrinsics ----==//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
// C++ intrinsics recognized by the SYCL device compiler frontend
9+
//===----------------------------------------------------------------------===//
10+
11+
#pragma once
12+
13+
#ifdef __SYCL_DEVICE_ONLY__
14+
15+
// Returns a unique string identifying the template parameter type. Stable
16+
// across device compiler invocations.
17+
template <typename T> const char *__sycl_fe_getStableUniqueTypeName();
18+
19+
// Get the value of the specialization constant with given name.
20+
// Post-link tool traces the ID to a string literal it points to and assigns
21+
// integer ID.
22+
template <typename T> T __sycl_getSpecConstantValue(const char *ID);
23+
24+
#endif

0 commit comments

Comments
 (0)