Skip to content

Commit

Permalink
[SYCL] Add support for kernel name types templated using enums. (inte…
Browse files Browse the repository at this point in the history
…l#1675)

Modify printing policy and add test for enums in anonymous namespace
Added diagnostic to handle unscoped enums with no fixed underlying type.

Also fixed handling of template argument type - enum type. The prior
patch handled only enum values, not the type itself.

Signed-off-by: Elizabeth Andrews <elizabeth.andrews@intel.com>
  • Loading branch information
elizabethandrews authored May 18, 2020
1 parent 3192ee7 commit e7020a1
Show file tree
Hide file tree
Showing 9 changed files with 394 additions and 26 deletions.
7 changes: 4 additions & 3 deletions clang/include/clang/AST/Decl.h
Original file line number Diff line number Diff line change
Expand Up @@ -283,15 +283,16 @@ class NamedDecl : public Decl {
/// Creating this name is expensive, so it should be called only when
/// performance doesn't matter.
void printQualifiedName(raw_ostream &OS) const;
void printQualifiedName(raw_ostream &OS, const PrintingPolicy &Policy) const;
void printQualifiedName(raw_ostream &OS, const PrintingPolicy &Policy,
bool WithGlobalNsPrefix = false) const;

/// Print only the nested name specifier part of a fully-qualified name,
/// including the '::' at the end. E.g.
/// when `printQualifiedName(D)` prints "A::B::i",
/// this function prints "A::B::".
void printNestedNameSpecifier(raw_ostream &OS) const;
void printNestedNameSpecifier(raw_ostream &OS,
const PrintingPolicy &Policy) const;
void printNestedNameSpecifier(raw_ostream &OS, const PrintingPolicy &Policy,
bool WithGlobalNsPrefix = false) const;

// FIXME: Remove string version.
std::string getQualifiedNameAsString() const;
Expand Down
4 changes: 3 additions & 1 deletion clang/include/clang/Basic/DiagnosticSemaKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -10789,7 +10789,9 @@ def err_builtin_matrix_disabled: Error<
// SYCL-specific diagnostics
def err_sycl_kernel_incorrectly_named : Error<
"kernel %select{name is missing"
"|needs to have a globally-visible name}0">;
"|needs to have a globally-visible name"
"|name is invalid. Unscoped enum requires fixed underlying type"
"}0">;
def err_sycl_restrict : Error<
"SYCL kernel cannot "
"%select{use a non-const global variable"
Expand Down
15 changes: 9 additions & 6 deletions clang/lib/AST/Decl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1538,14 +1538,14 @@ void NamedDecl::printQualifiedName(raw_ostream &OS) const {
printQualifiedName(OS, getASTContext().getPrintingPolicy());
}

void NamedDecl::printQualifiedName(raw_ostream &OS,
const PrintingPolicy &P) const {
void NamedDecl::printQualifiedName(raw_ostream &OS, const PrintingPolicy &P,
bool WithGlobalNsPrefix) const {
if (getDeclContext()->isFunctionOrMethod()) {
// We do not print '(anonymous)' for function parameters without name.
printName(OS);
return;
}
printNestedNameSpecifier(OS, P);
printNestedNameSpecifier(OS, P, WithGlobalNsPrefix);
if (getDeclName())
OS << *this;
else {
Expand All @@ -1566,7 +1566,8 @@ void NamedDecl::printNestedNameSpecifier(raw_ostream &OS) const {
}

void NamedDecl::printNestedNameSpecifier(raw_ostream &OS,
const PrintingPolicy &P) const {
const PrintingPolicy &P,
bool WithGlobalNsPrefix) const {
const DeclContext *Ctx = getDeclContext();

// For ObjC methods and properties, look through categories and use the
Expand All @@ -1593,6 +1594,9 @@ void NamedDecl::printNestedNameSpecifier(raw_ostream &OS,
Ctx = Ctx->getParent();
}

if (WithGlobalNsPrefix)
OS << "::";

for (const DeclContext *DC : llvm::reverse(Contexts)) {
if (const auto *Spec = dyn_cast<ClassTemplateSpecializationDecl>(DC)) {
OS << Spec->getName();
Expand All @@ -1605,8 +1609,7 @@ void NamedDecl::printNestedNameSpecifier(raw_ostream &OS,
if (ND->isAnonymousNamespace()) {
OS << (P.MSVCFormatting ? "`anonymous namespace\'"
: "(anonymous namespace)");
}
else
} else
OS << *ND;
} else if (const auto *RD = dyn_cast<RecordDecl>(DC)) {
if (!RD->getIdentifier())
Expand Down
136 changes: 132 additions & 4 deletions clang/lib/Sema/SemaSYCL.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1638,6 +1638,18 @@ static std::string eraseAnonNamespace(std::string S) {
return S;
}

static bool checkEnumTemplateParameter(const EnumDecl *ED,
DiagnosticsEngine &Diag,
SourceLocation KernelLocation) {
if (!ED->isScoped() && !ED->isFixed()) {
Diag.Report(KernelLocation, diag::err_sycl_kernel_incorrectly_named) << 2;
Diag.Report(ED->getSourceRange().getBegin(), diag::note_entity_declared_at)
<< ED;
return true;
}
return false;
}

// Emits a forward declaration
void SYCLIntegrationHeader::emitFwdDecl(raw_ostream &O, const Decl *D,
SourceLocation KernelLocation) {
Expand Down Expand Up @@ -1691,10 +1703,22 @@ void SYCLIntegrationHeader::emitFwdDecl(raw_ostream &O, const Decl *D,
PrintingPolicy P(D->getASTContext().getLangOpts());
P.adjustForCPlusPlusFwdDecl();
P.SuppressTypedefs = true;
P.SuppressUnwrittenScope = true;
std::string S;
llvm::raw_string_ostream SO(S);
D->print(SO, P);
O << SO.str() << ";\n";
O << SO.str();

if (const auto *ED = dyn_cast<EnumDecl>(D)) {
QualType T = ED->getIntegerType();
// Backup since getIntegerType() returns null for enum forward
// declaration with no fixed underlying type
if (T.isNull())
T = ED->getPromotionType();
O << " : " << T.getAsString();
}

O << ";\n";

// print closing braces for namespaces if needed
for (unsigned I = 0; I < NamespaceCnt; ++I)
Expand Down Expand Up @@ -1763,8 +1787,20 @@ void SYCLIntegrationHeader::emitForwardClassDecls(

switch (Arg.getKind()) {
case TemplateArgument::ArgKind::Type:
emitForwardClassDecls(O, Arg.getAsType(), KernelLocation, Printed);
case TemplateArgument::ArgKind::Integral: {
QualType T = (Arg.getKind() == TemplateArgument::ArgKind::Type)
? Arg.getAsType()
: Arg.getIntegralType();

// Handle Kernel Name Type templated using enum type and value.
if (const auto *ET = T->getAs<EnumType>()) {
const EnumDecl *ED = ET->getDecl();
if (!checkEnumTemplateParameter(ED, Diag, KernelLocation))
emitFwdDecl(O, ED, KernelLocation);
} else if (Arg.getKind() == TemplateArgument::ArgKind::Type)
emitForwardClassDecls(O, T, KernelLocation, Printed);
break;
}
case TemplateArgument::ArgKind::Pack: {
ArrayRef<TemplateArgument> Pack = Arg.getPackAsArray();

Expand Down Expand Up @@ -1823,6 +1859,97 @@ static std::string getCPPTypeString(QualType Ty) {
return eraseAnonNamespace(Ty.getAsString(P));
}

static void printArguments(ASTContext &Ctx, raw_ostream &ArgOS,
ArrayRef<TemplateArgument> Args,
const PrintingPolicy &P);

static void printArgument(ASTContext &Ctx, raw_ostream &ArgOS,
TemplateArgument Arg, const PrintingPolicy &P) {
switch (Arg.getKind()) {
case TemplateArgument::ArgKind::Pack: {
printArguments(Ctx, ArgOS, Arg.getPackAsArray(), P);
break;
}
case TemplateArgument::ArgKind::Integral: {
QualType T = Arg.getIntegralType();
const EnumType *ET = T->getAs<EnumType>();

if (ET) {
const llvm::APSInt &Val = Arg.getAsIntegral();
ArgOS << "(" << ET->getDecl()->getQualifiedNameAsString() << ")" << Val;
} else {
Arg.print(P, ArgOS);
}
break;
}
case TemplateArgument::ArgKind::Type: {
LangOptions LO;
PrintingPolicy TypePolicy(LO);
TypePolicy.SuppressTypedefs = true;
TypePolicy.SuppressTagKeyword = true;
QualType T = Arg.getAsType();
QualType FullyQualifiedType = TypeName::getFullyQualifiedType(T, Ctx, true);
ArgOS << FullyQualifiedType.getAsString(TypePolicy);
break;
}
default:
Arg.print(P, ArgOS);
}
}

static void printArguments(ASTContext &Ctx, raw_ostream &ArgOS,
ArrayRef<TemplateArgument> Args,
const PrintingPolicy &P) {
for (unsigned I = 0; I < Args.size(); I++) {
const TemplateArgument &Arg = Args[I];

if (I != 0)
ArgOS << ", ";

printArgument(Ctx, ArgOS, Arg, P);
}
}

static void printTemplateArguments(ASTContext &Ctx, raw_ostream &ArgOS,
ArrayRef<TemplateArgument> Args,
const PrintingPolicy &P) {
ArgOS << "<";
printArguments(Ctx, ArgOS, Args, P);
ArgOS << ">";
}

static std::string getKernelNameTypeString(QualType T) {

const CXXRecordDecl *RD = T->getAsCXXRecordDecl();

if (!RD)
return getCPPTypeString(T);

// If kernel name type is a template specialization with enum type
// template parameters, enumerators in name type string should be
// replaced with their underlying value since the enum definition
// is not visible in integration header.
if (const auto *TSD = dyn_cast<ClassTemplateSpecializationDecl>(RD)) {
LangOptions LO;
PrintingPolicy P(LO);
P.SuppressTypedefs = true;
SmallString<64> Buf;
llvm::raw_svector_ostream ArgOS(Buf);

// Print template class name
TSD->printQualifiedName(ArgOS, P, /*WithGlobalNsPrefix*/ true);

// Print template arguments substituting enumerators
ASTContext &Ctx = RD->getASTContext();
const TemplateArgumentList &Args = TSD->getTemplateArgs();
printTemplateArguments(Ctx, ArgOS, Args.asArray(), P);

return eraseAnonNamespace(ArgOS.str().str());
}

return getCPPTypeString(T);
}

void SYCLIntegrationHeader::emit(raw_ostream &O) {
O << "// This is auto-generated SYCL integration header.\n";
O << "\n";
Expand Down Expand Up @@ -1939,8 +2066,9 @@ void SYCLIntegrationHeader::emit(raw_ostream &O) {
O << "', '" << c;
O << "'> {\n";
} else {
O << "template <> struct KernelInfo<" << getCPPTypeString(K.NameType)
<< "> {\n";

O << "template <> struct KernelInfo<"
<< getKernelNameTypeString(K.NameType) << "> {\n";
}
O << " DLL_LOCAL\n";
O << " static constexpr const char* getName() { return \"" << K.Name
Expand Down
12 changes: 6 additions & 6 deletions clang/test/CodeGenSYCL/int_header1.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,14 @@
// CHECK:template <> struct KernelInfo<class KernelName> {
// CHECK:template <> struct KernelInfo<::nm1::nm2::KernelName0> {
// CHECK:template <> struct KernelInfo<::nm1::KernelName1> {
// CHECK:template <> struct KernelInfo<::nm1::KernelName3< ::nm1::nm2::KernelName0>> {
// CHECK:template <> struct KernelInfo<::nm1::KernelName3< ::nm1::KernelName1>> {
// CHECK:template <> struct KernelInfo<::nm1::KernelName4< ::nm1::nm2::KernelName0>> {
// CHECK:template <> struct KernelInfo<::nm1::KernelName4< ::nm1::KernelName1>> {
// CHECK:template <> struct KernelInfo<::nm1::KernelName3<::nm1::nm2::KernelName0>> {
// CHECK:template <> struct KernelInfo<::nm1::KernelName3<::nm1::KernelName1>> {
// CHECK:template <> struct KernelInfo<::nm1::KernelName4<::nm1::nm2::KernelName0>> {
// CHECK:template <> struct KernelInfo<::nm1::KernelName4<::nm1::KernelName1>> {
// CHECK:template <> struct KernelInfo<::nm1::KernelName3<KernelName5>> {
// CHECK:template <> struct KernelInfo<::nm1::KernelName4<KernelName7>> {
// CHECK:template <> struct KernelInfo<::nm1::KernelName8< ::nm1::nm2::C>> {
// CHECK:template <> struct KernelInfo<class TmplClassInAnonNS<class ClassInAnonNS>> {
// CHECK:template <> struct KernelInfo<::nm1::KernelName8<::nm1::nm2::C>> {
// CHECK:template <> struct KernelInfo<::TmplClassInAnonNS<ClassInAnonNS>> {

// This test checks if the SYCL device compiler is able to generate correct
// integration header when the kernel name class is expressed in different
Expand Down
4 changes: 2 additions & 2 deletions clang/test/CodeGenSYCL/integration_header.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,8 @@
//
// CHECK: template <> struct KernelInfo<class first_kernel> {
// CHECK: template <> struct KernelInfo<::second_namespace::second_kernel<char>> {
// CHECK: template <> struct KernelInfo<::third_kernel<1, int, ::point<X> >> {
// CHECK: template <> struct KernelInfo<::fourth_kernel< ::template_arg_ns::namespaced_arg<1> >> {
// CHECK: template <> struct KernelInfo<::third_kernel<1, int, ::point<X>>> {
// CHECK: template <> struct KernelInfo<::fourth_kernel<::template_arg_ns::namespaced_arg<1>>> {

#include "sycl.hpp"

Expand Down
8 changes: 4 additions & 4 deletions clang/test/CodeGenSYCL/kernel_name_with_typedefs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -117,16 +117,16 @@ int main() {
single_task<kernel_name2<const space::long_t, space::a_t>>(f);
// CHECK: template <> struct KernelInfo<::kernel_name2<const volatile long, const ::space::B>> {
single_task<kernel_name2<volatile space::clong_t, const space::b_t>>(f);
// CHECK: template <> struct KernelInfo<::kernel_name2< ::A, long>> {
// CHECK: template <> struct KernelInfo<::kernel_name2<::A, long>> {
single_task<kernel_name2<space::a_t, space::long_t>>(f);
// CHECK: template <> struct KernelInfo<::kernel_name2< ::space::B, int>> {
// CHECK: template <> struct KernelInfo<::kernel_name2<::space::B, int>> {
single_task<kernel_name2<space::b_t, int_t>>(f);
// full template specialization
// CHECK: template <> struct KernelInfo<::kernel_name2<int, const unsigned int>> {
single_task<kernel_name2<int_t, const uint_t>>(f);
// CHECK: template <> struct KernelInfo<::kernel_name2<const long, volatile const unsigned long>> {
// CHECK: template <> struct KernelInfo<::kernel_name2<const long, const volatile unsigned long>> {
single_task<kernel_name2<space::clong_t, volatile space::culong_t>>(f);
// CHECK: template <> struct KernelInfo<::kernel_name2< ::A, volatile ::space::B>> {
// CHECK: template <> struct KernelInfo<::kernel_name2<::A, volatile ::space::B>> {
single_task<kernel_name2<space::a_t, volatile space::b_t>>(f);
// CHECK: template <> struct KernelInfo<::kernel_name3<1>> {
single_task<kernel_name3<1>>(f);
Expand Down
Loading

0 comments on commit e7020a1

Please sign in to comment.