Index: clang/include/clang/AST/CXXRecordDeclDefinitionBits.def =================================================================== --- clang/include/clang/AST/CXXRecordDeclDefinitionBits.def +++ clang/include/clang/AST/CXXRecordDeclDefinitionBits.def @@ -112,6 +112,9 @@ /// 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) Index: clang/include/clang/AST/DeclCXX.h =================================================================== --- clang/include/clang/AST/DeclCXX.h +++ clang/include/clang/AST/DeclCXX.h @@ -1140,6 +1140,9 @@ /// \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; } Index: clang/include/clang/Basic/Attr.td =================================================================== --- clang/include/clang/Basic/Attr.td +++ clang/include/clang/Basic/Attr.td @@ -1191,6 +1191,13 @@ 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>; Index: clang/include/clang/Basic/AttrDocs.td =================================================================== --- clang/include/clang/Basic/AttrDocs.td +++ clang/include/clang/Basic/AttrDocs.td @@ -409,6 +409,67 @@ }]; } +def SYCLSpecialClassDocs : Documentation { + let Category = DocCatStmt; + let Content = [{ +SYCL defines some special classes (accessor, sampler and stream) that require +specific handling during the generation of 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/struct will have a mandatory __init method and an optional __finalize +method (__finalize method method is used only with stream type). __init method gives the +user additional information required. For instance, 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 __init and __finalize methods are called +at the beginning and the end of the kernel, respectively. +Please note that this is an attribute that is used for internal implementation and not intended +to be used by external users. + +The sntax 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 = [{ Index: clang/include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- clang/include/clang/Basic/DiagnosticSemaKinds.td +++ clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -11425,6 +11425,8 @@ def warn_sycl_kernel_return_type : Warning< "function template with 'sycl_kernel' attribute must have a 'void' return type">, InGroup; +def err_sycl_special_type_missing_init_method : Error< + "types with 'sycl_special_class' attribute must have an '__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">; Index: clang/lib/AST/DeclCXX.cpp =================================================================== --- clang/lib/AST/DeclCXX.cpp +++ clang/lib/AST/DeclCXX.cpp @@ -111,7 +111,7 @@ 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()); Index: clang/lib/Sema/SemaDecl.cpp =================================================================== --- clang/lib/Sema/SemaDecl.cpp +++ clang/lib/Sema/SemaDecl.cpp @@ -9112,6 +9112,13 @@ Diag(D.getDeclSpec().getVirtualSpecLoc(), diag::err_virtual_in_union); NewFD->setInvalidDecl(); } + if ((Parent->isClass() || Parent->isStruct()) && + Parent->hasAttr() && + NewFD->getKind() == Decl::Kind::CXXMethod && + NewFD->getName() == "__init" && D.isFunctionDefinition()) { + if (auto *def = Parent->getDefinition()) + def->SetInitMethod(true); + } } SetNestedNameSpecifier(*this, NewFD, D); @@ -16665,8 +16672,13 @@ RD->completeDefinition(); } - if (isa(Tag)) { + if (auto *RD = dyn_cast(Tag)) { FieldCollector->FinishClass(); + if (RD->hasAttr()) { + auto *def = RD->getDefinition(); + if (def && !def->hasInitMethod()) + Diag(RD->getLocation(), diag::err_sycl_special_type_missing_init_method); + } } // Exit this scope of this tag's definition. Index: clang/lib/Sema/SemaDeclAttr.cpp =================================================================== --- clang/lib/Sema/SemaDeclAttr.cpp +++ clang/lib/Sema/SemaDeclAttr.cpp @@ -7757,6 +7757,13 @@ handleSimpleAttribute(S, D, AL); } +static void handleSYCLSpecialClassAttr(Sema &S, Decl *D, const ParsedAttr &AL) { + // The 'sycl_special_class' attribute applies only to records. + const auto *RD = cast(D); + assert(RD && "Record type is expected"); + handleSimpleAttribute(S, D, AL); +} + static void handleDestroyAttr(Sema &S, Decl *D, const ParsedAttr &A) { if (!cast(D)->hasGlobalStorage()) { S.Diag(D->getLocation(), diag::err_destroy_attr_on_non_static_var) @@ -8180,6 +8187,9 @@ case ParsedAttr::AT_SYCLKernel: handleSYCLKernelAttr(S, D, AL); break; + case ParsedAttr::AT_SYCLSpecialClass: + handleSYCLSpecialClassAttr(S, D, AL); + break; case ParsedAttr::AT_Format: handleFormatAttr(S, D, AL); break; Index: clang/test/Misc/pragma-attribute-supported-attributes-list.test =================================================================== --- clang/test/Misc/pragma-attribute-supported-attributes-list.test +++ clang/test/Misc/pragma-attribute-supported-attributes-list.test @@ -154,6 +154,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) Index: clang/test/SemaSYCL/special-class-attribute-on-non-sycl.cpp =================================================================== --- /dev/null +++ clang/test/SemaSYCL/special-class-attribute-on-non-sycl.cpp @@ -0,0 +1,13 @@ +// 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() {}; +}; + Index: clang/test/SemaSYCL/special-class-attribute.cpp =================================================================== --- /dev/null +++ clang/test/SemaSYCL/special-class-attribute.cpp @@ -0,0 +1,60 @@ +// 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() {}; +}; + +class [[clang::sycl_special_class]] struct1 { + void __init() {}; +}; +struct __attribute__((sycl_special_class)) struct2 { + void __init() {}; +}; + +class [[clang::sycl_special_class]] class5 { + void __init() {}; +}; +class special5 {}; + +class __attribute__((sycl_special_class)) class6; +class class6 { + void __init() {}; +}; + + +// Must have __init method defined +class __attribute__((sycl_special_class)) class7 { // expected-error {{types with 'sycl_special_class' attribute must have an '__init' method defined}} + class7() {} +}; +class [[clang::sycl_special_class]] class8 { // expected-error {{types with 'sycl_special_class' attribute must have an '__init' method defined}} + void __init(); +}; + +struct __attribute__((sycl_special_class)) struct3; +struct struct3 {}; // expected-error {{types with 'sycl_special_class' attribute must have an '__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}}