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 @@ -1139,6 +1139,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,71 @@ }]; } +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 = [{ Index: clang/include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- clang/include/clang/Basic/DiagnosticSemaKinds.td +++ clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -11448,6 +11448,9 @@ 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_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">; 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 @@ -9167,6 +9167,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); @@ -16726,8 +16733,21 @@ RD->completeDefinition(); } - if (isa(Tag)) { + if (auto *RD = dyn_cast(Tag)) { FieldCollector->FinishClass(); + if (RD->hasAttr()) { + 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. Index: clang/lib/Sema/SemaDeclAttr.cpp =================================================================== --- clang/lib/Sema/SemaDeclAttr.cpp +++ clang/lib/Sema/SemaDeclAttr.cpp @@ -8301,6 +8301,9 @@ case ParsedAttr::AT_SYCLKernel: handleSYCLKernelAttr(S, D, AL); break; + case ParsedAttr::AT_SYCLSpecialClass: + handleSimpleAttribute(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 @@ -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) 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,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(){} +}; Index: clang/test/SemaSYCL/special-class-attribute.cpp =================================================================== --- /dev/null +++ clang/test/SemaSYCL/special-class-attribute.cpp @@ -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(){}