Add support for sycl_special_class attribute.

Special classes such as accessor, sampler, and stream need additional
implementation when they are passed from host to device.

This patch is adding a new attribute “sycl_special_class” used to mark
SYCL classes/struct that need the additional compiler handling.
This commit is contained in:
Zahira Ammarguellat 2021-11-23 14:58:03 -08:00
parent 90f185c964
commit 8ba9c794fe
11 changed files with 199 additions and 2 deletions

View File

@ -112,6 +112,9 @@ FIELD(HasVariantMembers, 1, NO_MERGE)
/// True if there no non-field members declared by the user.
FIELD(HasOnlyCMembers, 1, NO_MERGE)
/// True if there is an '__init' method defined by the user.
FIELD(HasInitMethod, 1, NO_MERGE)
/// True if any field has an in-class initializer, including those
/// within anonymous unions or structs.
FIELD(HasInClassInitializer, 1, NO_MERGE)

View File

@ -1139,6 +1139,9 @@ public:
/// \note This does NOT include a check for union-ness.
bool isEmpty() const { return data().Empty; }
void setInitMethod(bool Val) { data().HasInitMethod = Val; }
bool hasInitMethod() const { return data().HasInitMethod; }
bool hasPrivateFields() const {
return data().HasPrivateFields;
}

View File

@ -1191,6 +1191,13 @@ def SYCLKernel : InheritableAttr {
let Documentation = [SYCLKernelDocs];
}
def SYCLSpecialClass: InheritableAttr {
let Spellings = [Clang<"sycl_special_class">];
let Subjects = SubjectList<[CXXRecord]>;
let LangOpts = [SYCL];
let Documentation = [SYCLSpecialClassDocs];
}
def C11NoReturn : InheritableAttr {
let Spellings = [Keyword<"_Noreturn">];
let Subjects = SubjectList<[Function], ErrorDiag>;

View File

@ -409,6 +409,71 @@ The SYCL kernel in the previous code sample meets these expectations.
}];
}
def SYCLSpecialClassDocs : Documentation {
let Category = DocCatStmt;
let Content = [{
SYCL defines some special classes (accessor, sampler, and stream) which require
specific handling during the generation of the SPIR entry point.
The ``__attribute__((sycl_special_class))`` attribute is used in SYCL
headers to indicate that a class or a struct needs a specific handling when
it is passed from host to device.
Special classes will have a mandatory ``__init`` method and an optional
``__finalize`` method (the ``__finalize`` method is used only with the
``stream`` type). Kernel parameters types are extract from the ``__init`` method
parameters. The kernel function arguments list is derived from the
arguments of the ``__init`` method. The arguments of the ``__init`` method are
copied into the kernel function argument list and the ``__init`` and
``__finalize`` methods are called at the beginning and the end of the kernel,
respectively.
The ``__init`` and ``__finalize`` methods must be defined inside the
special class.
Please note that this is an attribute that is used as an internal
implementation detail and not intended to be used by external users.
The syntax of the attribute is as follows:
.. code-block:: c++
class __attribute__((sycl_special_class)) accessor {};
class [[clang::sycl_special_class]] accessor {};
This is a code example that illustrates the use of the attribute:
.. code-block:: c++
class __attribute__((sycl_special_class)) SpecialType {
int F1;
int F2;
void __init(int f1) {
F1 = f1;
F2 = f1;
}
void __finalize() {}
public:
SpecialType() = default;
int getF2() const { return F2; }
};
int main () {
SpecialType T;
cgh.single_task([=] {
T.getF2();
});
}
This would trigger the following kernel entry point in the AST:
.. code-block:: c++
void __sycl_kernel(int f1) {
SpecialType T;
T.__init(f1);
...
T.__finalize()
}
}];
}
def C11NoReturnDocs : Documentation {
let Category = DocCatFunction;
let Content = [{

View File

@ -11448,6 +11448,9 @@ def warn_sycl_kernel_num_of_function_params : Warning<
def warn_sycl_kernel_return_type : Warning<
"function template with 'sycl_kernel' attribute must have a 'void' return type">,
InGroup<IgnoredAttributes>;
def err_sycl_special_type_num_init_method : Error<
"types with 'sycl_special_class' attribute must have one and only one '__init' "
"method defined">;
def err_bit_int_bad_size : Error<"%select{signed|unsigned}0 _BitInt must "
"have a bit size of at least %select{2|1}0">;

View File

@ -111,7 +111,7 @@ CXXRecordDecl::DefinitionData::DefinitionData(CXXRecordDecl *D)
HasDeclaredCopyAssignmentWithConstParam(false),
IsAnyDestructorNoReturn(false), IsLambda(false),
IsParsingBaseSpecifiers(false), ComputedVisibleConversions(false),
HasODRHash(false), Definition(D) {}
HasODRHash(false), Definition(D), HasInitMethod(false) {}
CXXBaseSpecifier *CXXRecordDecl::DefinitionData::getBasesSlowCase() const {
return Bases.get(Definition->getASTContext().getExternalSource());

View File

@ -9170,6 +9170,13 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC,
Diag(D.getDeclSpec().getVirtualSpecLoc(), diag::err_virtual_in_union);
NewFD->setInvalidDecl();
}
if ((Parent->isClass() || Parent->isStruct()) &&
Parent->hasAttr<SYCLSpecialClassAttr>() &&
NewFD->getKind() == Decl::Kind::CXXMethod &&
NewFD->getName() == "__init" && D.isFunctionDefinition()) {
if (auto *Def = Parent->getDefinition())
Def->setInitMethod(true);
}
}
SetNestedNameSpecifier(*this, NewFD, D);
@ -16729,8 +16736,21 @@ void Sema::ActOnTagFinishDefinition(Scope *S, Decl *TagD,
RD->completeDefinition();
}
if (isa<CXXRecordDecl>(Tag)) {
if (auto *RD = dyn_cast<CXXRecordDecl>(Tag)) {
FieldCollector->FinishClass();
if (RD->hasAttr<SYCLSpecialClassAttr>()) {
auto *Def = RD->getDefinition();
assert(Def && "The record is expected to have a completed definition");
unsigned NumInitMethods = 0;
for (auto *Method : Def->methods()) {
if (!Method->getIdentifier())
continue;
if (Method->getName() == "__init")
NumInitMethods++;
}
if (NumInitMethods > 1 || !Def->hasInitMethod())
Diag(RD->getLocation(), diag::err_sycl_special_type_num_init_method);
}
}
// Exit this scope of this tag's definition.

View File

@ -8301,6 +8301,9 @@ static void ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D,
case ParsedAttr::AT_SYCLKernel:
handleSYCLKernelAttr(S, D, AL);
break;
case ParsedAttr::AT_SYCLSpecialClass:
handleSimpleAttribute<SYCLSpecialClassAttr>(S, D, AL);
break;
case ParsedAttr::AT_Format:
handleFormatAttr(S, D, AL);
break;

View File

@ -155,6 +155,7 @@
// CHECK-NEXT: ReturnTypestate (SubjectMatchRule_function, SubjectMatchRule_variable_is_parameter)
// CHECK-NEXT: ReturnsNonNull (SubjectMatchRule_objc_method, SubjectMatchRule_function)
// CHECK-NEXT: ReturnsTwice (SubjectMatchRule_function)
// CHECK-NEXT: SYCLSpecialClass (SubjectMatchRule_record)
// CHECK-NEXT: ScopedLockable (SubjectMatchRule_record)
// CHECK-NEXT: Section (SubjectMatchRule_function, SubjectMatchRule_variable_is_global, SubjectMatchRule_objc_method, SubjectMatchRule_objc_property)
// CHECK-NEXT: SetTypestate (SubjectMatchRule_function_is_member)

View File

@ -0,0 +1,12 @@
// RUN: %clang_cc1 -std=c++11 -fsyntax-only -fsycl-is-device -verify %s
// RUN: %clang_cc1 -std=c++11 -fsyntax-only -verify -x c++ %s
#ifndef __SYCL_DEVICE_ONLY__
// expected-warning@+5 {{'sycl_special_class' attribute ignored}}
#else
// expected-no-diagnostics
#endif
class __attribute__((sycl_special_class)) special_class {
void __init(){}
};

View File

@ -0,0 +1,80 @@
// RUN: %clang_cc1 -std=c++11 -fsyntax-only -fsycl-is-device -verify %s
// No diagnostics
class [[clang::sycl_special_class]] class1 {
void __init(){}
};
class __attribute__((sycl_special_class)) class2 {
void __init(){}
};
class class3;
class [[clang::sycl_special_class]] class3 {
void __init(){}
};
class class4;
class __attribute__((sycl_special_class)) class4 {
void __init(){}
};
struct [[clang::sycl_special_class]] struct1 {
void __init(){}
};
struct __attribute__((sycl_special_class)) struct2 {
void __init(){}
};
class __attribute__((sycl_special_class)) class5;
class class5 {
void __init(){}
};
// Must have one and only one __init method defined
class __attribute__((sycl_special_class)) class6 { // expected-error {{types with 'sycl_special_class' attribute must have one and only one '__init' method defined}}
class6() {}
};
class [[clang::sycl_special_class]] class7 { // expected-error {{types with 'sycl_special_class' attribute must have one and only one '__init' method defined}}
void __init();
};
class [[clang::sycl_special_class]] class8 { // expected-error {{types with 'sycl_special_class' attribute must have one and only one '__init' method defined}}
void __init();
int func() {}
void __init(int a){}
};
struct __attribute__((sycl_special_class)) struct3;
struct struct3 {}; // expected-error {{types with 'sycl_special_class' attribute must have one and only one '__init' method defined}}
// Only classes
[[clang::sycl_special_class]] int var1 = 0; // expected-warning {{'sycl_special_class' attribute only applies to classes}}
__attribute__((sycl_special_class)) int var2 = 0; // expected-warning {{'sycl_special_class' attribute only applies to classes}}
[[clang::sycl_special_class]] void foo1(); // expected-warning {{'sycl_special_class' attribute only applies to classes}}
__attribute__((sycl_special_class)) void foo2(); // expected-warning {{'sycl_special_class' attribute only applies to classes}}
// Attribute takes no arguments
class [[clang::sycl_special_class(1)]] class9{}; // expected-error {{'sycl_special_class' attribute takes no arguments}}
class __attribute__((sycl_special_class(1))) class10 {}; // expected-error {{'sycl_special_class' attribute takes no arguments}}
// __init method must be defined inside the CXXRecordDecl.
class [[clang::sycl_special_class]] class11 { // expected-error {{types with 'sycl_special_class' attribute must have one and only one '__init' method defined}}
void __init();
};
void class11::__init(){}
class __attribute__((sycl_special_class)) class12 { // expected-error {{types with 'sycl_special_class' attribute must have one and only one '__init' method defined}}
void __init();
};
void class12::__init(){}
struct [[clang::sycl_special_class]] struct4 { // expected-error {{types with 'sycl_special_class' attribute must have one and only one '__init' method defined}}
void __init();
};
void struct4::__init(){}
struct __attribute__((sycl_special_class)) struct5 { // expected-error {{types with 'sycl_special_class' attribute must have one and only one '__init' method defined}}
void __init();
};
void struct5::__init(){}