diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td index 04b3f5e88e394..76114d2f93925 100644 --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -1313,6 +1313,25 @@ def SYCLIntelNoGlobalWorkOffset : InheritableAttr { let PragmaAttributeSupport = 0; } +def SYCLIntelLoopFuse : InheritableAttr { + let Spellings = [CXX11<"intel", "loop_fuse">, + CXX11<"intel", "loop_fuse_independent">]; + let Args = [ExprArgument<"Value", /*optional=*/ 1>]; + let LangOpts = [SYCLIsDevice, SYCLIsHost]; + let Subjects = SubjectList<[Function], ErrorDiag>; + let Accessors = [Accessor<"isIndependent", + [CXX11<"intel", "loop_fuse_independent">]>]; + let Documentation = [SYCLIntelLoopFuseDocs]; + let AdditionalMembers = [{ + static unsigned getMinValue() { + return 0; + } + static unsigned getMaxValue() { + return 1024*1024; + } + }]; +} + def C11NoReturn : InheritableAttr { let Spellings = [Keyword<"_Noreturn">]; let Subjects = SubjectList<[Function], ErrorDiag>; diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td index 7a180ee646b46..8fccc785f2c82 100644 --- a/clang/include/clang/Basic/AttrDocs.td +++ b/clang/include/clang/Basic/AttrDocs.td @@ -2608,6 +2608,34 @@ loop should not be fused with any adjacent loop. }]; } +def SYCLIntelLoopFuseDocs : Documentation { + let Category = DocCatFunction; + let Heading = "loop_fuse, loop_fuse_independent"; + let Content = [{ +``[[intel::loop_fuse(N)]]`` and ``[[intel::loop_fuse_independent(N)]]`` attributes apply +to a function/lambda function. It is a strong request, to the extent possible, to fuse +the loops within the function, that are contained in at most N-1 other loops within the +function. If the optional parameter N is omitted, it is a strong request, to the extent +possible, to fuse loops within the function that are not contained in any other loop +within the function. ``[[intel::loop_fuse_independent(N)]]`` also guarantees that fusion +safety analysis can ignore negative-distance dependences between these loops. + +.. code-block:: c++ + + [[intel::loop_fuse(N)]] + int foo() {} + + [[intel::loop_fuse_independent(N)]] + int foo() {} + + +``[[intel::loop_fuse(N)]]`` and ``[[intel::loop_fuse_independent(N)]]`` takes one optional +parameter that is a constant unsigned integer expression. The parameter N may be a template +parameter. + + }]; +} + def SYCLDeviceIndirectlyCallableDocs : Documentation { let Category = DocCatFunction; let Heading = "intel::device_indirectly_callable"; diff --git a/clang/include/clang/Basic/AttributeCommonInfo.h b/clang/include/clang/Basic/AttributeCommonInfo.h index e58613d61af18..421e401220648 100644 --- a/clang/include/clang/Basic/AttributeCommonInfo.h +++ b/clang/include/clang/Basic/AttributeCommonInfo.h @@ -166,7 +166,8 @@ class AttributeCommonInfo { ParsedAttr == AT_SYCLIntelMaxWorkGroupSize || ParsedAttr == AT_SYCLIntelMaxGlobalWorkDim || ParsedAttr == AT_SYCLIntelNoGlobalWorkOffset || - ParsedAttr == AT_SYCLIntelUseStallEnableClusters) + ParsedAttr == AT_SYCLIntelUseStallEnableClusters || + ParsedAttr == AT_SYCLIntelLoopFuse) return true; return false; diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index d0a82c982c521..1ff5eb8544455 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -3363,6 +3363,8 @@ class Sema final { WebAssemblyImportModuleAttr *mergeImportModuleAttr( Decl *D, const WebAssemblyImportModuleAttr &AL); + SYCLIntelLoopFuseAttr * + mergeSYCLIntelLoopFuseAttr(Decl *D, const AttributeCommonInfo &CI, Expr *E); void mergeDeclAttributes(NamedDecl *New, Decl *Old, AvailabilityMergeKind AMK = AMK_Redeclaration); void MergeTypedefNameDecl(Scope *S, TypedefNameDecl *New, @@ -10204,6 +10206,8 @@ class Sema final { /// addSYCLIntelPipeIOAttr - Adds a pipe I/O attribute to a particular /// declaration. void addSYCLIntelPipeIOAttr(Decl *D, const AttributeCommonInfo &CI, Expr *ID); + void addSYCLIntelLoopFuseAttr(Decl *D, const AttributeCommonInfo &CI, + Expr *E); bool checkNSReturnsRetainedReturnType(SourceLocation loc, QualType type); bool checkAllowedSYCLInitializer(VarDecl *VD, diff --git a/clang/lib/CodeGen/CodeGenFunction.cpp b/clang/lib/CodeGen/CodeGenFunction.cpp index 5c947cc66b42d..4005402d53896 100644 --- a/clang/lib/CodeGen/CodeGenFunction.cpp +++ b/clang/lib/CodeGen/CodeGenFunction.cpp @@ -955,6 +955,19 @@ void CodeGenFunction::StartFunction(GlobalDecl GD, QualType RetTy, if (getLangOpts().SYCLIsHost && D && D->hasAttr()) Fn->addFnAttr("sycl_kernel"); + if (getLangOpts().SYCLIsDevice && D) { + if (const auto *A = D->getAttr()) { + Expr *E = A->getValue(); + llvm::Metadata *AttrMDArgs[] = { + llvm::ConstantAsMetadata::get(Builder.getInt32( + E->getIntegerConstantExpr(D->getASTContext())->getZExtValue())), + llvm::ConstantAsMetadata::get( + A->isIndependent() ? Builder.getInt32(1) : Builder.getInt32(0))}; + Fn->setMetadata("loop_fuse", + llvm::MDNode::get(getLLVMContext(), AttrMDArgs)); + } + } + if (getLangOpts().OpenCL || getLangOpts().SYCLIsDevice) { // Add metadata for a kernel function. if (const FunctionDecl *FD = dyn_cast_or_null(D)) { diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index e97f0c7be033e..198fb5014ddfe 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -2614,6 +2614,8 @@ static bool mergeDeclAttribute(Sema &S, NamedDecl *D, NewAttr = S.mergeImportModuleAttr(D, *IMA); else if (const auto *INA = dyn_cast(Attr)) NewAttr = S.mergeImportNameAttr(D, *INA); + else if (const auto *LFA = dyn_cast(Attr)) + NewAttr = S.mergeSYCLIntelLoopFuseAttr(D, *LFA, LFA->getValue()); else if (Attr->shouldInheritEvenIfAlreadyPresent() || !DeclHasAttr(D, Attr)) NewAttr = cast(Attr->clone(S.Context)); diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp index 07f67ad0d84e9..1afdf74813ce8 100644 --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -3091,6 +3091,90 @@ static void handleMaxGlobalWorkDimAttr(Sema &S, Decl *D, E); } +SYCLIntelLoopFuseAttr * +Sema::mergeSYCLIntelLoopFuseAttr(Decl *D, const AttributeCommonInfo &CI, + Expr *E) { + + if (const auto ExistingAttr = D->getAttr()) { + // [[intel::loop_fuse]] and [[intel::loop_fuse_independent]] are + // incompatible. + // FIXME: If additional spellings are provided for this attribute, + // this code will do the wrong thing. + if (ExistingAttr->getAttributeSpellingListIndex() != + CI.getAttributeSpellingListIndex()) { + Diag(CI.getLoc(), diag::err_attributes_are_not_compatible) + << CI << ExistingAttr; + Diag(ExistingAttr->getLocation(), diag::note_conflicting_attribute); + return nullptr; + } + + if (!E->isValueDependent()) { + Optional ArgVal = E->getIntegerConstantExpr(Context); + Optional ExistingArgVal = + ExistingAttr->getValue()->getIntegerConstantExpr(Context); + + assert(ArgVal && ExistingArgVal && + "Argument should be an integer constant expression"); + // Compare attribute argument value and warn if there is a mismatch. + if (ArgVal->getExtValue() != ExistingArgVal->getExtValue()) + Diag(ExistingAttr->getLoc(), diag::warn_duplicate_attribute) + << ExistingAttr; + } + + // If there is no mismatch, silently ignore duplicate attribute. + return nullptr; + } + return ::new (Context) SYCLIntelLoopFuseAttr(Context, CI, E); +} + +static bool checkSYCLIntelLoopFuseArgument(Sema &S, + const AttributeCommonInfo &CI, + Expr *E) { + // Dependent expressions are checked when instantiated. + if (E->isValueDependent()) + return false; + + Optional ArgVal = E->getIntegerConstantExpr(S.Context); + if (!ArgVal) { + S.Diag(E->getExprLoc(), diag::err_attribute_argument_type) + << CI << AANT_ArgumentIntegerConstant << E->getSourceRange(); + return true; + } + + SYCLIntelLoopFuseAttr TmpAttr(S.Context, CI, E); + ExprResult ICE; + + return S.checkRangedIntegralArgument(E, &TmpAttr, ICE); +} + +void Sema::addSYCLIntelLoopFuseAttr(Decl *D, const AttributeCommonInfo &CI, + Expr *E) { + assert(E && "argument has unexpected null value"); + + if (checkSYCLIntelLoopFuseArgument(*this, CI, E)) + return; + + // Attribute should not be added during host compilation. + if (getLangOpts().SYCLIsHost) + return; + + SYCLIntelLoopFuseAttr *NewAttr = mergeSYCLIntelLoopFuseAttr(D, CI, E); + + if (NewAttr) + D->addAttr(NewAttr); +} + +// Handles [[intel::loop_fuse]] and [[intel::loop_fuse_independent]]. +static void handleLoopFuseAttr(Sema &S, Decl *D, const ParsedAttr &Attr) { + // Default argument value is set to 1. + Expr *E = Attr.isArgExpr(0) + ? Attr.getArgAsExpr(0) + : IntegerLiteral::Create(S.Context, llvm::APInt(32, 1), + S.Context.IntTy, Attr.getLoc()); + + S.addSYCLIntelLoopFuseAttr(D, Attr, E); +} + static void handleVecTypeHint(Sema &S, Decl *D, const ParsedAttr &AL) { if (!AL.hasParsedType()) { S.Diag(AL.getLoc(), diag::err_attribute_wrong_number_arguments) << AL << 1; @@ -8379,6 +8463,9 @@ static void ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, case ParsedAttr::AT_SYCLIntelUseStallEnableClusters: handleUseStallEnableClustersAttr(S, D, AL); break; + case ParsedAttr::AT_SYCLIntelLoopFuse: + handleLoopFuseAttr(S, D, AL); + break; case ParsedAttr::AT_VecTypeHint: handleVecTypeHint(S, D, AL); break; diff --git a/clang/lib/Sema/SemaSYCL.cpp b/clang/lib/Sema/SemaSYCL.cpp index 5c66ceee82128..f94a146b0235e 100644 --- a/clang/lib/Sema/SemaSYCL.cpp +++ b/clang/lib/Sema/SemaSYCL.cpp @@ -570,6 +570,14 @@ class MarkDeviceFunction : public RecursiveASTVisitor { (KernelBody != FD) && !FD->hasAttr()) FD->addAttr(SYCLSimdAttr::CreateImplicit(SemaRef.getASTContext())); + // Attribute "loop_fuse" can be applied explicitly on kernel function. + // Attribute should not be propagated from device functions to kernel. + if (auto *A = FD->getAttr()) { + if (ParentFD == SYCLKernel) { + Attrs.insert(A); + } + } + // TODO: vec_len_hint should be handled here CallGraphNode *N = SYCLCG.getNode(FD); @@ -3335,6 +3343,7 @@ void Sema::MarkDevice(void) { case attr::Kind::SYCLIntelMaxGlobalWorkDim: case attr::Kind::SYCLIntelNoGlobalWorkOffset: case attr::Kind::SYCLIntelUseStallEnableClusters: + case attr::Kind::SYCLIntelLoopFuse: case attr::Kind::SYCLSimd: { if ((A->getKind() == attr::Kind::SYCLSimd) && KernelBody && !KernelBody->getAttr()) { diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp index 85fd57c459bf3..bbc7ee4ee0d9f 100644 --- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -592,6 +592,16 @@ static void instantiateSYCLIntelPipeIOAttr( S.addSYCLIntelPipeIOAttr(New, *Attr, Result.getAs()); } +static void instantiateSYCLIntelLoopFuseAttr( + Sema &S, const MultiLevelTemplateArgumentList &TemplateArgs, + const SYCLIntelLoopFuseAttr *Attr, Decl *New) { + EnterExpressionEvaluationContext Unevaluated( + S, Sema::ExpressionEvaluationContext::ConstantEvaluated); + ExprResult Result = S.SubstExpr(Attr->getValue(), TemplateArgs); + if (!Result.isInvalid()) + S.addSYCLIntelLoopFuseAttr(New, *Attr, Result.getAs()); +} + template static void instantiateIntelSYCLFunctionAttr( Sema &S, const MultiLevelTemplateArgumentList &TemplateArgs, @@ -775,6 +785,12 @@ void Sema::InstantiateAttrs(const MultiLevelTemplateArgumentList &TemplateArgs, *this, TemplateArgs, SYCLIntelMaxGlobalWorkDim, New); continue; } + if (const auto *SYCLIntelLoopFuse = + dyn_cast(TmplAttr)) { + instantiateSYCLIntelLoopFuseAttr(*this, TemplateArgs, SYCLIntelLoopFuse, + New); + continue; + } if (const auto *SYCLIntelNoGlobalWorkOffset = dyn_cast(TmplAttr)) { instantiateIntelSYCLFunctionAttr( @@ -6204,7 +6220,10 @@ static void processSYCLKernel(Sema &S, FunctionDecl *FD, MangleContext &MC) { if (S.LangOpts.SYCLIsDevice) { S.ConstructOpenCLKernel(FD, MC); } else if (S.LangOpts.SYCLIsHost) { - CXXRecordDecl *CRD = (*FD->param_begin())->getType()->getAsCXXRecordDecl(); + QualType KernelParamTy = (*FD->param_begin())->getType(); + const CXXRecordDecl *CRD = (KernelParamTy->isReferenceType() + ? KernelParamTy->getPointeeCXXRecordDecl() + : KernelParamTy->getAsCXXRecordDecl()); for (auto *Method : CRD->methods()) if (Method->getOverloadedOperator() == OO_Call && !Method->hasAttr()) diff --git a/clang/test/CodeGenSYCL/loop_fuse_device.cpp b/clang/test/CodeGenSYCL/loop_fuse_device.cpp new file mode 100644 index 0000000000000..2789f05223716 --- /dev/null +++ b/clang/test/CodeGenSYCL/loop_fuse_device.cpp @@ -0,0 +1,52 @@ +// RUN: %clang_cc1 -fsycl -fsycl-is-device -internal-isystem %S/Inputs -triple spir64-unknown-unknown-sycldevice -emit-llvm -o - %s | FileCheck %s + +#include "sycl.hpp" + +using namespace cl::sycl; +queue q; + +[[intel::loop_fuse(5)]] void foo() {} + +template +class KernelFunctor5 { +public: + [[intel::loop_fuse(SIZE)]] void operator()() const {} +}; + +void bar() { + + q.submit([&](handler &h) { + // Test template argument. + KernelFunctor5<5> f5; + h.single_task(f5); + + // Test different argument sizes. + // Emit 1 if there is no argument. + h.single_task( + []() [[intel::loop_fuse]]{}); + h.single_task( + []() [[intel::loop_fuse(0)]]{}); + h.single_task( + []() [[intel::loop_fuse(1)]]{}); + h.single_task( + []() [[intel::loop_fuse(10)]]{}); + + // Test attribute is not propagated. + h.single_task( + []() { foo(); }); + }); +} + +// CHECK: define spir_kernel void @"{{.*}}kernel_name_1"() {{.*}} !loop_fuse ![[LF5:[0-9]+]] +// CHECK: define spir_kernel void @"{{.*}}kernel_name_2"() {{.*}} !loop_fuse ![[LF1:[0-9]+]] +// CHECK: define spir_kernel void @"{{.*}}kernel_name_3"() {{.*}} !loop_fuse ![[LF0:[0-9]+]] +// CHECK: define spir_kernel void @"{{.*}}kernel_name_4"() {{.*}} !loop_fuse ![[LF1]] +// CHECK: define spir_kernel void @"{{.*}}kernel_name_5"() {{.*}} !loop_fuse ![[LF10:[0-9]+]] +// CHECK: define spir_kernel void @"{{.*}}kernel_name_6"() +// CHECK-NOT: !loop_fuse +// CHECK-SAME: { +// CHECK: define spir_func void @{{.*}}foo{{.*}} !loop_fuse ![[LF5]] +// CHECK: ![[LF5]] = !{i32 5, i32 0} +// CHECK: ![[LF1]] = !{i32 1, i32 0} +// CHECK: ![[LF0]] = !{i32 0, i32 0} +// CHECK: ![[LF10]] = !{i32 10, i32 0} diff --git a/clang/test/CodeGenSYCL/loop_fuse_ind_device.cpp b/clang/test/CodeGenSYCL/loop_fuse_ind_device.cpp new file mode 100644 index 0000000000000..a415abf74cf44 --- /dev/null +++ b/clang/test/CodeGenSYCL/loop_fuse_ind_device.cpp @@ -0,0 +1,52 @@ +// RUN: %clang_cc1 -fsycl -fsycl-is-device -internal-isystem %S/Inputs -triple spir64-unknown-unknown-sycldevice -emit-llvm -o - %s | FileCheck %s + +#include "sycl.hpp" + +using namespace cl::sycl; +queue q; + +[[intel::loop_fuse_independent(5)]] void foo() {} + +template +class KernelFunctor5 { +public: + [[intel::loop_fuse_independent(SIZE)]] void operator()() const {} +}; + +void bar() { + + q.submit([&](handler &h) { + // Test template argument. + KernelFunctor5<5> f5; + h.single_task(f5); + + // Test different argument sizes. + // Emit 1 if there is no argument. + h.single_task( + []() [[intel::loop_fuse_independent]]{}); + h.single_task( + []() [[intel::loop_fuse_independent(0)]]{}); + h.single_task( + []() [[intel::loop_fuse_independent(1)]]{}); + h.single_task( + []() [[intel::loop_fuse_independent(10)]]{}); + + // Test attribute is not propagated. + h.single_task( + []() { foo(); }); + }); +} + +// CHECK: define spir_kernel void @"{{.*}}kernel_name_1"() {{.*}} !loop_fuse ![[LFI5:[0-9]+]] +// CHECK: define spir_kernel void @"{{.*}}kernel_name_2"() {{.*}} !loop_fuse ![[LFI1:[0-9]+]] +// CHECK: define spir_kernel void @"{{.*}}kernel_name_3"() {{.*}} !loop_fuse ![[LFI0:[0-9]+]] +// CHECK: define spir_kernel void @"{{.*}}kernel_name_4"() {{.*}} !loop_fuse ![[LFI1]] +// CHECK: define spir_kernel void @"{{.*}}kernel_name_5"() {{.*}} !loop_fuse ![[LFI10:[0-9]+]] +// CHECK: define spir_kernel void @"{{.*}}kernel_name_6"() +// CHECK-NOT: !loop_fuse +// CHECK-SAME: { +// CHECK: define spir_func void @{{.*}}foo{{.*}} !loop_fuse ![[LFI5]] +// CHECK: ![[LFI5]] = !{i32 5, i32 1} +// CHECK: ![[LFI1]] = !{i32 1, i32 1} +// CHECK: ![[LFI0]] = !{i32 0, i32 1} +// CHECK: ![[LFI10]] = !{i32 10, i32 1} diff --git a/clang/test/CodeGenSYCL/loop_fusion_host.cpp b/clang/test/CodeGenSYCL/loop_fusion_host.cpp new file mode 100644 index 0000000000000..5245855ebd626 --- /dev/null +++ b/clang/test/CodeGenSYCL/loop_fusion_host.cpp @@ -0,0 +1,37 @@ +// RUN: %clang_cc1 -fsycl -fsycl-is-host -triple -x86_64-unknown-linux-gnu -disable-llvm-passes -emit-llvm -o - %s | FileCheck %s +// RUN: %clang_cc1 -fsycl -fsycl-is-host -triple -x86_64-unknown-linux-gnu -disable-llvm-passes -verify -Wno-sycl-2017-compat -DDIAG %s + +template +__attribute__((sycl_kernel)) void kernel(const Func &kernelFunc) { + kernelFunc(); +} + +template +class KernelFunctor5 { +public: + [[intel::loop_fuse(SIZE)]] void operator()() const {} +}; + +template +class KernelFunctor3 { +public: + [[intel::loop_fuse_independent(SIZE)]] void operator()() const {} +}; + +[[intel::loop_fuse]] void func1() {} +[[intel::loop_fuse_independent]] void func2() {} + +void foo() { + + KernelFunctor5<5> f5; + kernel(f5); + + KernelFunctor5<3> f3; + kernel(f5); +} +// CHECK-NOT: !loop_fuse + +#if defined(DIAG) +int baz(); +[[intel::loop_fuse(baz())]] void func3(); // expected-error{{'loop_fuse' attribute requires an integer constant}} +#endif diff --git a/clang/test/Misc/pragma-attribute-supported-attributes-list.test b/clang/test/Misc/pragma-attribute-supported-attributes-list.test index 222926ca2fb3b..f7950a13e34e1 100644 --- a/clang/test/Misc/pragma-attribute-supported-attributes-list.test +++ b/clang/test/Misc/pragma-attribute-supported-attributes-list.test @@ -142,6 +142,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: SYCLIntelLoopFuse (SubjectMatchRule_function) // 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) diff --git a/clang/test/SemaSYCL/loop_fusion.cpp b/clang/test/SemaSYCL/loop_fusion.cpp new file mode 100644 index 0000000000000..389b3f3d98317 --- /dev/null +++ b/clang/test/SemaSYCL/loop_fusion.cpp @@ -0,0 +1,62 @@ +// RUN: %clang_cc1 -fsycl -fsycl-is-device -verify %s + +[[intel::loop_fuse(5)]] int a; // expected-error{{'loop_fuse' attribute only applies to functions}} + +[[intel::loop_fuse("foo")]] void func() {} // expected-error{{'loop_fuse' attribute requires an integer constant}} + +[[intel::loop_fuse(1048577)]] void func1() {} // expected-error{{'loop_fuse' attribute requires integer constant between 0 and 1048576 inclusive}} +[[intel::loop_fuse_independent(-1)]] void func2() {} // expected-error{{'loop_fuse_independent' attribute requires integer constant between 0 and 1048576 inclusive}} + +[[intel::loop_fuse(0, 1)]] void func3() {} // expected-error{{'loop_fuse' attribute takes no more than 1 argument}} +[[intel::loop_fuse_independent(2, 3)]] void func4() {} // expected-error{{'loop_fuse_independent' attribute takes no more than 1 argument}} + +// No diagnostic is thrown since arguments match. Duplicate attribute is silently ignored. +[[intel::loop_fuse]] [[intel::loop_fuse]] void func5() {} +[[intel::loop_fuse_independent(10)]] [[intel::loop_fuse_independent(10)]] void func6() {} + +[[intel::loop_fuse]] [[intel::loop_fuse(10)]] void func7() {} // expected-warning {{attribute 'loop_fuse' is already applied with different parameters}} +[[intel::loop_fuse_independent(5)]] [[intel::loop_fuse_independent(10)]] void func8() {} // expected-warning {{attribute 'loop_fuse_independent' is already applied with different parameters}} + +[[intel::loop_fuse]] void func9(); +[[intel::loop_fuse]] void func9(); + +[[intel::loop_fuse_independent(10)]] void func10(); +[[intel::loop_fuse_independent(10)]] void func10(); + +[[intel::loop_fuse(1)]] void func11(); +[[intel::loop_fuse(3)]] void func11(); // expected-warning {{attribute 'loop_fuse' is already applied with different parameters}} + +[[intel::loop_fuse_independent(1)]] void func12(); +[[intel::loop_fuse_independent(3)]] void func12(); // expected-warning {{attribute 'loop_fuse_independent' is already applied with different parameters}} + +// expected-error@+2 {{'loop_fuse_independent' and 'loop_fuse' attributes are not compatible}} +// expected-note@+1 {{conflicting attribute is here}} +[[intel::loop_fuse]] [[intel::loop_fuse_independent]] void func13(); + +// expected-error@+2 {{'loop_fuse' and 'loop_fuse_independent' attributes are not compatible}} +// expected-note@+1 {{conflicting attribute is here}} +[[intel::loop_fuse_independent]] [[intel::loop_fuse]] void func14(); + +// expected-error@+2 {{'loop_fuse' and 'loop_fuse_independent' attributes are not compatible}} +// expected-note@+2 {{conflicting attribute is here}} +[[intel::loop_fuse]] void func15(); +[[intel::loop_fuse_independent]] void func15(); + +// expected-error@+2 {{'loop_fuse_independent' and 'loop_fuse' attributes are not compatible}} +// expected-note@+2 {{conflicting attribute is here}} +[[intel::loop_fuse_independent]] void func16(); +[[intel::loop_fuse]] void func16(); + +template +[[intel::loop_fuse(N)]] void func17(); // expected-error{{'loop_fuse' attribute requires integer constant between 0 and 1048576 inclusive}} + +template +[[intel::loop_fuse(Ty{})]] void func18() {} // expected-error{{'loop_fuse' attribute requires an integer constant}} + +void checkTemplates() { + func17<-1>(); // expected-note{{in instantiation of}} + func18(); // expected-note{{in instantiation of}} +} + +int baz(); +[[intel::loop_fuse(baz())]] void func19(); // expected-error{{'loop_fuse' attribute requires an integer constant}} diff --git a/clang/test/SemaSYCL/loop_fusion_ast.cpp b/clang/test/SemaSYCL/loop_fusion_ast.cpp new file mode 100644 index 0000000000000..1064995e3c0e5 --- /dev/null +++ b/clang/test/SemaSYCL/loop_fusion_ast.cpp @@ -0,0 +1,101 @@ +// RUN: %clang_cc1 -fsycl -fsycl-is-device -internal-isystem %S/Inputs -Wno-sycl-2017-compat -ast-dump %s | FileCheck %s + +#include "sycl.hpp" + +using namespace cl::sycl; +queue q; + +// CHECK: FunctionDecl {{.*}} func1 'void ()' +// CHECK-NEXT: CompoundStmt +// CHECK-NEXT: SYCLIntelLoopFuseAttr {{.*}} loop_fuse +// CHECK-NEXT: IntegerLiteral {{.*}} 'int' 1 +[[intel::loop_fuse]] void func1() {} + +// CHECK: FunctionDecl {{.*}} func2 'void ()' +// CHECK-NEXT: CompoundStmt +// CHECK-NEXT: SYCLIntelLoopFuseAttr {{.*}} loop_fuse +// CHECK-NEXT: IntegerLiteral {{.*}} 'int' 0 +[[intel::loop_fuse(0)]] void func2() {} + +// CHECK: FunctionDecl {{.*}} func3 'void ()' +// CHECK-NEXT: CompoundStmt +// CHECK-NEXT: SYCLIntelLoopFuseAttr {{.*}} loop_fuse_independent +// CHECK-NEXT: IntegerLiteral {{.*}} 'int' 1 +[[intel::loop_fuse_independent]] void func3() {} + +// CHECK: FunctionDecl {{.*}} func4 'void ()' +// CHECK-NEXT: CompoundStmt +// CHECK-NEXT: SYCLIntelLoopFuseAttr {{.*}} loop_fuse_independent +// CHECK-NEXT: IntegerLiteral {{.*}} 'int' 3 +[[intel::loop_fuse_independent(3)]] void func4() {} + +// CHECK: FunctionTemplateDecl {{.*}} func5 +// CHECK: FunctionDecl {{.*}} func5 'void ()' +// CHECK-NEXT: CompoundStmt +// CHECK_NEXT: SYCLIntelLoopFuseAttr {{.*}} loop_fuse +// CHECK_NEXT: DeclRefExpr {{.*}} 'int' NonTypeTemplateParm {{.*}} 'N' 'int' +// CHECK: FunctionDecl {{.*}} func5 'void ()' +// CHECK-NEXT: TemplateArgument integral 1 +// CHECK-NEXT: CompoundStmt +// CHECK-NEXT: SYCLIntelLoopFuseAttr {{.*}} loop_fuse +// CHECK-NEXT: SubstNonTypeTemplateParmExpr +// CHECK-NEXT: NonTypeTemplateParmDecl +// CHECK-NEXT: IntegerLiteral {{.*}} 'int' 1 +template +[[intel::loop_fuse(N)]] void func5() {} + +// CHECK: FunctionTemplateDecl {{.*}} func6 +// CHECK: FunctionDecl {{.*}} func6 'void ()' +// CHECK-NEXT: CompoundStmt +// CHECK_NEXT: SYCLIntelLoopFuseAttr {{.*}} loop_fuse_independent +// CHECK_NEXT: DeclRefExpr {{.*}} 'int' NonTypeTemplateParm {{.*}} 'N' 'int' +// CHECK: FunctionDecl {{.*}} func6 'void ()' +// CHECK-NEXT: TemplateArgument integral 5 +// CHECK-NEXT: CompoundStmt +// CHECK-NEXT: SYCLIntelLoopFuseAttr {{.*}} loop_fuse_independent +// CHECK-NEXT: SubstNonTypeTemplateParmExpr +// CHECK-NEXT: NonTypeTemplateParmDecl +// CHECK-NEXT: IntegerLiteral {{.*}} 'int' 5 +template +[[intel::loop_fuse_independent(N)]] void func6() {} + +class KernelFunctor { +public: + void operator()() const { + func1(); + func3(); + } +}; + +template +class KernelFunctor2 { +public: + [[intel::loop_fuse(N)]] void operator()() const { + } +}; + +void foo() { + q.submit([&](handler &h) { + // CHECK: FunctionDecl {{.*}}kernel_name_1 + // CHECK-NOT: SYCLIntelLoopFuseAttr + KernelFunctor f1; + h.single_task(f1); + + // CHECK: FunctionDecl {{.*}}kernel_name_2 + // CHECK: SYCLIntelLoopFuseAttr {{.*}} loop_fuse + // CHECK-NEXT: SubstNonTypeTemplateParmExpr + // CHECK-NEXT: NonTypeTemplateParmDecl + // CHECK-NEXT: IntegerLiteral {{.*}} 'int' 3 + KernelFunctor2<3> f2; + h.single_task(f2); + + // CHECK: FunctionDecl {{.*}}kernel_name_3 + // CHECK: SYCLIntelLoopFuseAttr {{.*}} loop_fuse_independent + // CHECK-NEXT: IntegerLiteral {{.*}} 'int' 1 + h.single_task( + []() [[intel::loop_fuse_independent]]{}); + }); + + func5<1>(); + func6<5>(); +}