diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td index f2d7ba59a183c..b26389a812771 100644 --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -1854,11 +1854,14 @@ def SYCLIntelFPGAInitiationInterval : StmtAttr { let Documentation = [SYCLIntelFPGAInitiationIntervalAttrDocs]; } -def SYCLIntelFPGAMaxConcurrency : StmtAttr { - let Spellings = [CXX11<"intelfpga","max_concurrency">, - CXX11<"intel","max_concurrency">]; - let Subjects = SubjectList<[ForStmt, CXXForRangeStmt, WhileStmt, DoStmt], - ErrorDiag, "'for', 'while', and 'do' statements">; +def SYCLIntelFPGAMaxConcurrency : DeclOrStmtAttr { + let Spellings = [ + CXX11<"intelfpga", "max_concurrency">, CXX11<"intel", "max_concurrency"> + ]; + let Subjects = + SubjectList<[ForStmt, CXXForRangeStmt, WhileStmt, DoStmt, Function], + ErrorDiag, + "'for', 'while', 'do' statements, and (device) functions">; let Args = [ExprArgument<"NThreadsExpr">]; let LangOpts = [SYCLIsDevice, SilentlyIgnoreSYCLIsHost]; let HasCustomTypeTransform = 1; diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td index c0821ebd48491..c6550be98d28f 100644 --- a/clang/include/clang/Basic/AttrDocs.td +++ b/clang/include/clang/Basic/AttrDocs.td @@ -2799,10 +2799,10 @@ def SYCLIntelFPGAMaxConcurrencyAttrDocs : Documentation { let Category = DocCatVariable; let Heading = "intel::max_concurrency"; let Content = [{ -This attribute applies to a loop. Indicates that the loop should allow no more -than N threads or iterations to execute it simultaneously. N must be a non -negative integer. '0' indicates the max_concurrency case to be unbounded. Cannot -be applied multiple times to the same loop. +This attribute applies to a loop or a function. Indicates that the loop/function +should allow no more than N threads or iterations to execute it simultaneously. +N must be a non negative integer. '0' indicates the max_concurrency case to be +unbounded. Cannot be applied multiple times to the same loop. .. code-block:: c++ @@ -2811,10 +2811,13 @@ be applied multiple times to the same loop. [[intel::max_concurrency(2)]] for (int i = 0; i != 10; ++i) a[i] = 0; } + [[intel::max_concurrency(2)]] void foo1 { } + template void bar() { [[intel::max_concurrency(N)]] for(;;) { } } + [[intel::max_concurrency(N)]] void bar1() { } }]; } diff --git a/clang/include/clang/Basic/AttributeCommonInfo.h b/clang/include/clang/Basic/AttributeCommonInfo.h index e5f237c773627..e46742782ae1b 100644 --- a/clang/include/clang/Basic/AttributeCommonInfo.h +++ b/clang/include/clang/Basic/AttributeCommonInfo.h @@ -167,7 +167,8 @@ class AttributeCommonInfo { ParsedAttr == AT_SYCLIntelMaxGlobalWorkDim || ParsedAttr == AT_SYCLIntelNoGlobalWorkOffset || ParsedAttr == AT_SYCLIntelUseStallEnableClusters || - ParsedAttr == AT_SYCLIntelLoopFuse || ParsedAttr == AT_SYCLSimd) + ParsedAttr == AT_SYCLIntelLoopFuse || ParsedAttr == AT_SYCLSimd || + ParsedAttr == AT_SYCLIntelFPGAMaxConcurrency) return true; return false; diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index d1e2a22702bb5..48a22ff109038 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -134,8 +134,8 @@ def err_intel_fpga_reg_limitations : Error < "__builtin_intel_fpga_reg">; def illegal_type_declared_here : Note< "field with illegal type declared here">; -def err_sycl_loop_attr_duplication : Error< - "duplicate %select{unroll|Intel FPGA}0 loop attribute %1">; +def err_sycl_attr_duplication : Error< + "duplicate %select{unroll|Intel FPGA}0 %select{loop|function}1 attribute %2">; def err_loop_unroll_compatibility : Error< "incompatible loop unroll instructions: '%0' and '%1'">; def err_pipe_attribute_arg_not_allowed : Error< diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 2a3b9e3aaf6f2..3254c47f8aa1e 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -10241,6 +10241,9 @@ class Sema final { IntelFPGAMaxReplicatesAttr * MergeIntelFPGAMaxReplicatesAttr(Decl *D, const IntelFPGAMaxReplicatesAttr &A); + SYCLIntelFPGAMaxConcurrencyAttr *MergeSYCLIntelFPGAMaxConcurrencyAttr( + Decl *D, const SYCLIntelFPGAMaxConcurrencyAttr &A); + /// AddAlignedAttr - Adds an aligned attribute to a particular declaration. void AddAlignedAttr(Decl *D, const AttributeCommonInfo &CI, Expr *E, bool IsPackExpansion); @@ -10295,6 +10298,11 @@ class Sema final { /// declaration. void addSYCLIntelPipeIOAttr(Decl *D, const AttributeCommonInfo &CI, Expr *ID); + /// AddSYCLIntelFPGAMaxConcurrencyAttr - Adds a max_concurrency attribute to a + /// particular declaration. + void AddSYCLIntelFPGAMaxConcurrencyAttr(Decl *D, + const AttributeCommonInfo &CI, + Expr *E); bool checkNSReturnsRetainedReturnType(SourceLocation loc, QualType type); bool checkAllowedSYCLInitializer(VarDecl *VD, bool CheckValueDependent = false); diff --git a/clang/lib/CodeGen/CodeGenFunction.cpp b/clang/lib/CodeGen/CodeGenFunction.cpp index 7343c96890e86..b8b0a8574ec05 100644 --- a/clang/lib/CodeGen/CodeGenFunction.cpp +++ b/clang/lib/CodeGen/CodeGenFunction.cpp @@ -730,6 +730,14 @@ void CodeGenFunction::EmitOpenCLKernelMetadata(const FunctionDecl *FD, llvm::ConstantAsMetadata::get(Builder.getInt32(1))}; Fn->setMetadata("stall_enable", llvm::MDNode::get(Context, AttrMDArgs)); } + + if (const auto *A = FD->getAttr()) { + const auto *CE = cast(A->getNThreadsExpr()); + llvm::APSInt ArgVal = CE->getResultAsAPSInt(); + llvm::Metadata *AttrMDArgs[] = { + llvm::ConstantAsMetadata::get(Builder.getInt32(ArgVal.getSExtValue()))}; + Fn->setMetadata("max_concurrency", llvm::MDNode::get(Context, AttrMDArgs)); + } } /// Determine whether the function F ends with a return stmt. diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index a4d507879d703..a4fc4c03a95d7 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -2628,6 +2628,8 @@ static bool mergeDeclAttribute(Sema &S, NamedDecl *D, NewAttr = S.MergeSYCLIntelNoGlobalWorkOffsetAttr(D, *A); else if (const auto *A = dyn_cast(Attr)) NewAttr = S.MergeIntelFPGAMaxReplicatesAttr(D, *A); + else if (const auto *A = dyn_cast(Attr)) + NewAttr = S.MergeSYCLIntelFPGAMaxConcurrencyAttr(D, *A); 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 1ab711a56efb0..8cd8aa8306f35 100644 --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -6294,6 +6294,74 @@ static void handleSYCLIntelPipeIOAttr(Sema &S, Decl *D, S.addSYCLIntelPipeIOAttr(D, Attr, E); } +SYCLIntelFPGAMaxConcurrencyAttr *Sema::MergeSYCLIntelFPGAMaxConcurrencyAttr( + Decl *D, const SYCLIntelFPGAMaxConcurrencyAttr &A) { + // Check to see if there's a duplicate attribute with different values + // already applied to the declaration. + if (const auto *DeclAttr = D->getAttr()) { + const auto *DeclExpr = dyn_cast(DeclAttr->getNThreadsExpr()); + const auto *MergeExpr = dyn_cast(A.getNThreadsExpr()); + if (DeclExpr && MergeExpr && + DeclExpr->getResultAsAPSInt() != MergeExpr->getResultAsAPSInt()) { + Diag(DeclAttr->getLocation(), diag::err_sycl_attr_duplication) + << 1 << 1 << DeclAttr; + return nullptr; + } + } + // TODO + // max_concurrency and disable_component_pipelining attributes can't be applied + // to the same function. + // if (checkAttrMutualExclusion(S, D, AL)) + // return; + + return ::new (Context) + SYCLIntelFPGAMaxConcurrencyAttr(Context, A, A.getNThreadsExpr()); +} + +void Sema::AddSYCLIntelFPGAMaxConcurrencyAttr(Decl *D, + const AttributeCommonInfo &CI, + Expr *E) { + if (!E->isValueDependent()) { + llvm::APSInt ArgVal; + ExprResult Res = VerifyIntegerConstantExpression(E, &ArgVal); + if (Res.isInvalid()) + return; + E = Res.get(); + + // This attribute requires a strictly positive value. + if (ArgVal <= 0) { + Diag(E->getExprLoc(), diag::err_attribute_requires_positive_integer) + << CI << /*positive*/ 0; + return; + } + + if (const auto *DeclAttr = D->getAttr()) { + const auto *DeclExpr = + dyn_cast(DeclAttr->getNThreadsExpr()); + if (DeclExpr && ArgVal != DeclExpr->getResultAsAPSInt()) { + Diag(CI.getLoc(), diag::warn_duplicate_attribute) << CI; + Diag(DeclAttr->getLoc(), diag::note_previous_attribute); + return; + } + } + } + + D->addAttr(::new (Context) SYCLIntelFPGAMaxConcurrencyAttr(Context, CI, E)); +} + +static void handleSYCLIntelFPGAMaxConcurrencyAttr(Sema &S, Decl *D, + const ParsedAttr &A) { + S.CheckDeprecatedSYCLAttributeSpelling(A); + // TODO + // max_concurrency and disable_component_pipelining attributes can't be + // applied to the same function. if + // (checkAttrMutualExclusion(S, D, AL)) + // return; + + Expr *E = A.getArgAsExpr(0); + S.AddSYCLIntelFPGAMaxConcurrencyAttr(D, A, E); +} + namespace { struct IntrinToName { uint32_t Id; @@ -9547,6 +9615,9 @@ static void ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, case ParsedAttr::AT_SYCLIntelPipeIO: handleSYCLIntelPipeIOAttr(S, D, AL); break; + case ParsedAttr::AT_SYCLIntelFPGAMaxConcurrency: + handleSYCLIntelFPGAMaxConcurrencyAttr(S, D, AL); + break; // Swift attributes. case ParsedAttr::AT_SwiftAsyncName: diff --git a/clang/lib/Sema/SemaSYCL.cpp b/clang/lib/Sema/SemaSYCL.cpp index a971793928f4c..f807d9dc5516c 100644 --- a/clang/lib/Sema/SemaSYCL.cpp +++ b/clang/lib/Sema/SemaSYCL.cpp @@ -560,6 +560,13 @@ class MarkDeviceFunction : public RecursiveASTVisitor { } } + // Attribute "max_concurrency" is applied to device functions only. The + // attribute is not propagated to the caller. + if (auto *A = FD->getAttr()) + if (ParentFD == SYCLKernel) { + Attrs.insert(A); + } + // TODO: vec_len_hint should be handled here CallGraphNode *N = SYCLCG.getNode(FD); @@ -3316,7 +3323,8 @@ void Sema::MarkDevice(void) { case attr::Kind::SYCLIntelNoGlobalWorkOffset: case attr::Kind::SYCLIntelUseStallEnableClusters: case attr::Kind::SYCLIntelLoopFuse: - case attr::Kind::SYCLSimd: { + case attr::Kind::SYCLSimd: + case attr::Kind::SYCLIntelFPGAMaxConcurrency: { if ((A->getKind() == attr::Kind::SYCLSimd) && KernelBody && !KernelBody->getAttr()) { // Usual kernel can't call ESIMD functions. diff --git a/clang/lib/Sema/SemaStmtAttr.cpp b/clang/lib/Sema/SemaStmtAttr.cpp index e4e679099cadb..35acce5e58633 100644 --- a/clang/lib/Sema/SemaStmtAttr.cpp +++ b/clang/lib/Sema/SemaStmtAttr.cpp @@ -627,8 +627,8 @@ CheckForDuplicationSYCLLoopAttribute(Sema &S, for (const auto *I : Attrs) { if (LoopAttr && isa(I)) { // Cannot specify same type of attribute twice. - S.Diag(I->getLocation(), diag::err_sycl_loop_attr_duplication) - << isIntelFPGAAttr << LoopAttr; + S.Diag(I->getLocation(), diag::err_sycl_attr_duplication) + << isIntelFPGAAttr << 0 << LoopAttr; } if (isa(I)) LoopAttr = cast(I); diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp index d56ca1b873c12..d9178935fd0ee 100644 --- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -694,6 +694,17 @@ static void instantiateIntelSYCLFunctionAttr( S.addIntelSingleArgAttr(New, *Attr, Result.getAs()); } +template +static void instantiateSYCLIntelFPGAMaxConcurrencyAttr( + Sema &S, const MultiLevelTemplateArgumentList &TemplateArgs, + const SYCLIntelFPGAMaxConcurrencyAttr *A, Decl *New) { + EnterExpressionEvaluationContext Unevaluated( + S, Sema::ExpressionEvaluationContext::ConstantEvaluated); + ExprResult Result = S.SubstExpr(A->getNThreadsExpr(), TemplateArgs); + if (!Result.isInvalid()) + S.AddSYCLIntelFPGAMaxConcurrencyAttr(New, *A, Result.getAs()); +} + static void instantiateIntelFPGAPrivateCopiesAttr( Sema &S, const MultiLevelTemplateArgumentList &TemplateArgs, const IntelFPGAPrivateCopiesAttr *A, Decl *New) { @@ -940,6 +951,13 @@ void Sema::InstantiateAttrs(const MultiLevelTemplateArgumentList &TemplateArgs, *this, TemplateArgs, SYCLIntelMaxWorkGroupSize, New); continue; } + if (const auto *SYCLIntelMaxConcurrency = + dyn_cast(TmplAttr)) { + instantiateSYCLIntelFPGAMaxConcurrencyAttr< + SYCLIntelFPGAMaxConcurrencyAttr>(*this, TemplateArgs, + SYCLIntelMaxConcurrency, New); + continue; + } // Existing DLL attribute on the instantiation takes precedence. if (TmplAttr->getKind() == attr::DLLExport || TmplAttr->getKind() == attr::DLLImport) { diff --git a/clang/test/CodeGenSYCL/intel-fpga-loops.cpp b/clang/test/CodeGenSYCL/intel-fpga-loops.cpp index 72f4d934e2e33..61fd4f1417239 100644 --- a/clang/test/CodeGenSYCL/intel-fpga-loops.cpp +++ b/clang/test/CodeGenSYCL/intel-fpga-loops.cpp @@ -3,8 +3,6 @@ // CHECK: br label %for.cond, !llvm.loop ![[MD_DLP:[0-9]+]] // CHECK: br label %for.cond, !llvm.loop ![[MD_II:[0-9]+]] // CHECK: br label %for.cond2, !llvm.loop ![[MD_II_2:[0-9]+]] -// CHECK: br label %for.cond, !llvm.loop ![[MD_MC:[0-9]+]] -// CHECK: br label %for.cond2, !llvm.loop ![[MD_MC_2:[0-9]+]] // CHECK: br label %for.cond, !llvm.loop ![[MD_LC:[0-9]+]] // CHECK: br label %for.cond2, !llvm.loop ![[MD_LC_2:[0-9]+]] // CHECK: br label %for.cond13, !llvm.loop ![[MD_LC_3:[0-9]+]] @@ -35,19 +33,6 @@ void ii() { a[i] = 0; } -template -void max_concurrency() { - int a[10]; - // CHECK: ![[MD_MC]] = distinct !{![[MD_MC]], ![[MP]], ![[MD_max_concurrency:[0-9]+]]} - // CHECK-NEXT: ![[MD_max_concurrency]] = !{!"llvm.loop.max_concurrency.count", i32 0} - [[intel::max_concurrency(A)]] for (int i = 0; i != 10; ++i) - a[i] = 0; - // CHECK: ![[MD_MC_2]] = distinct !{![[MD_MC_2]], ![[MP]], ![[MD_max_concurrency_2:[0-9]+]]} - // CHECK-NEXT: ![[MD_max_concurrency_2]] = !{!"llvm.loop.max_concurrency.count", i32 4} - [[intel::max_concurrency(4)]] for (int i = 0; i != 10; ++i) - a[i] = 0; -} - template void loop_coalesce() { int a[10]; @@ -100,7 +85,6 @@ int main() { kernel_single_task([]() { disable_loop_pipelining(); ii<4>(); - max_concurrency<0>(); loop_coalesce<2>(); max_interleaving<3>(); speculated_iterations<4>(); diff --git a/clang/test/CodeGenSYCL/max-concurrency.cpp b/clang/test/CodeGenSYCL/max-concurrency.cpp new file mode 100644 index 0000000000000..395c3623dc3f0 --- /dev/null +++ b/clang/test/CodeGenSYCL/max-concurrency.cpp @@ -0,0 +1,89 @@ +// RUN: %clang_cc1 -fsycl-is-device -internal-isystem %S/Inputs -disable-llvm-passes -triple spir64-unknown-unknown-sycldevice -sycl-std=2020 -emit-llvm -o - %s | FileCheck %s + +#include "sycl.hpp" + +// CHECK: br label %for.cond, !llvm.loop ![[MD_MC:[0-9]+]] +// CHECK: br label %for.cond2, !llvm.loop ![[MD_MC_1:[0-9]+]] + +// CHECK: define {{.*}}spir_kernel void @"{{.*}}kernel_name1"() #0 {{.*}} !max_concurrency ![[NUM1:[0-9]+]] +// CHECK: define {{.*}}spir_kernel void @"{{.*}}kernel_name3"() #0 {{.*}} !max_concurrency ![[NUM2:[0-9]+]] +// CHECK: define {{.*}}spir_kernel void @"{{.*}}kernel_name4"() #0 {{.*}} !max_concurrency ![[NUM1:[0-9]+]] + +template +void max_concurrency() { + int a[10]; + // CHECK: ![[MD_MC]] = distinct !{![[MD_MC]], ![[MP:[0-9]+]], ![[MD_max_concurrency:[0-9]+]]} + // CHECK-NEXT: ![[MP]] = !{!"llvm.loop.mustprogress"} + // CHECK-NEXT: ![[MD_max_concurrency]] = !{!"llvm.loop.max_concurrency.count", i32 5} + [[intel::max_concurrency(A)]] for (int i = 0; i != 10; ++i) + a[i] = 0; + // CHECK: ![[MD_MC_1]] = distinct !{![[MD_MC_1]], ![[MP]], ![[MD_max_concurrency_1:[0-9]+]]} + // CHECK-NEXT: ![[MD_max_concurrency_1]] = !{!"llvm.loop.max_concurrency.count", i32 4} + [[intel::max_concurrency(4)]] for (int i = 0; i != 10; ++i) + a[i] = 0; +} + +// CHECK: ![[NUM1]] = !{i32 4} +// CHECK: ![[NUM2]] = !{i32 3} + +template +__attribute__((sycl_kernel)) void kernel_single_task_1(const Func &kernelFunc) { + kernelFunc(); +} + +using namespace cl::sycl; + +class Functor1 { +public: + [[intel::max_concurrency(4)]] void operator()() const {} +}; + +[[intel::max_concurrency(2)]] void foo() {} + +class Functor2 { +public: + void operator()() const { + foo(); + } +}; + +template +class Functor3 { +public: + [[intel::max_concurrency(NT)]] void operator()() const {} +}; + +template +[[intel::max_concurrency(NT)]] void func() {} + +int main() { + queue q; + + kernel_single_task_1([]() { + max_concurrency<5>(); + }); + + q.submit([&](handler &h) { + Functor1 f1; + h.single_task(f1); + + Functor2 f2; + h.single_task(f2); + + h.single_task( + []() [[intel::max_concurrency(3)]]{}); + + Functor3<4> f3; + h.single_task(f3); + + h.single_task([]() { + func<2>(); + }); + + }); + + + return 0; +} + + diff --git a/clang/test/SemaSYCL/intel-fpga-loops.cpp b/clang/test/SemaSYCL/intel-fpga-loops.cpp index 2f3b580f14ab3..53af8b7b2efdf 100644 --- a/clang/test/SemaSYCL/intel-fpga-loops.cpp +++ b/clang/test/SemaSYCL/intel-fpga-loops.cpp @@ -10,7 +10,7 @@ void foo() { [[intel::ivdep]] int a[10]; // expected-error@+1 {{'initiation_interval' attribute only applies to 'for', 'while', and 'do' statements}} [[intel::initiation_interval(2)]] int c[10]; - // expected-error@+1 {{'max_concurrency' attribute only applies to 'for', 'while', and 'do' statements}} + // expected-error@+1 {{'max_concurrency' attribute only applies to 'for', 'while', 'do' statements, and (device) functions}} [[intel::max_concurrency(2)]] int d[10]; // expected-error@+1 {{'disable_loop_pipelining' attribute only applies to 'for', 'while', and 'do' statements}} [[intel::disable_loop_pipelining]] int g[10]; diff --git a/clang/test/SemaSYCL/max-concurrency.cpp b/clang/test/SemaSYCL/max-concurrency.cpp new file mode 100644 index 0000000000000..16e65fbaed4ed --- /dev/null +++ b/clang/test/SemaSYCL/max-concurrency.cpp @@ -0,0 +1,125 @@ +// RUN: %clang_cc1 -fsycl-is-device -internal-isystem %S/Inputs -sycl-std=2020 -fsyntax-only -ast-dump -verify -pedantic %s | FileCheck %s + +#include "sycl.hpp" + +using namespace cl::sycl; +queue q; + +class Functor1 { +public: + [[intel::max_concurrency(4)]] void operator()() const {} +}; + +[[intel::max_concurrency]] void foo() {} // expected-error {{'max_concurrency' attribute takes one argument}} + +class Functor2 { +public: + void operator()() const { + foo(); + } +}; + +template +class Functor3 { +public: + [[intel::max_concurrency(NT)]] void operator()() const {} + // expected-error@+1 {{'max_concurrency' attribute only applies to 'for', 'while', 'do' statements, and (device) functions}} + [[intel::max_concurrency(2)]] int a[10]; +}; + +// expected-error@+1 {{'max_concurrency' attribute takes one argument}} +[[intel::max_concurrency(3, 3)]] void goo() {} + +class Functor4 { +public: + void operator() () const { + goo(); + } +}; + +// expected-error@+1 {{'max_concurrency' attribute requires a positive integral compile time constant expression}} +[[intel::max_concurrency(-1)]] void bar() {} +class Functor5 { +public: + void operator() () const { + bar(); + } +}; + +// expected-error@+1 {{integral constant expression must have integral or unscoped enumeration type, not 'const char [16]'}} +[[intel::max_concurrency("numberofthreads")]] void zoo() {} + +template +[[intel::max_concurrency(NT)]] void func() {} + +[[intel::max_concurrency(8)]] void dup(); +[[intel::max_concurrency(9)]] void dup() {} // expected-error {{duplicate Intel FPGA function attribute 'max_concurrency'}} + +int main() { + queue q; + + q.submit([&](handler &h) { + Functor1 f1; + h.single_task(f1); + + Functor2 f2; + h.single_task(f2); + + h.single_task( + []() [[intel::max_concurrency(3)]]{}); + + Functor3<4> f3; + h.single_task(f3); + + h.single_task([]() { + func<5>(); + }); + + }); +} + +// CHECK: CXXMethodDecl {{.*}}used operator() {{.*}} +// CHECK: SYCLIntelFPGAMaxConcurrencyAttr {{.*}} +// CHECK: ConstantExpr {{.*}} 'int' +// CHECK: value: Int 4 +// CHECK: IntegerLiteral {{.*}}4{{$}} +// CHECK: CXXMethodDecl {{.*}}operator() {{.*}} +// CHECK: SYCLIntelFPGAMaxConcurrencyAttr {{.*}} +// CHECK: DeclRefExpr {{.*}} 'int' NonTypeTemplateParm {{.*}} 'NT' 'int' +// CHECK: CXXMethodDecl {{.*}}{{.*}}used operator() {{.*}} +// CHECK: SYCLIntelFPGAMaxConcurrencyAttr {{.*}} +// CHECK: ConstantExpr {{.*}} 'int' +// CHECK: value: Int 4 +// CHECK: IntegerLiteral {{.*}}4{{$}} +// CHECK: FunctionDecl {{.*}}{{.*}}func {{.*}} +// CHECK: SYCLIntelFPGAMaxConcurrencyAttr {{.*}} +// CHECK: FunctionDecl {{.*}}{{.*}}used func 'void ()' +// CHECK: SYCLIntelFPGAMaxConcurrencyAttr {{.*}} +// CHECK: ConstantExpr {{.*}} 'int' +// CHECK: value: Int 5 +// CHECK: IntegerLiteral {{.*}}5{{$}} +// CHECK: FunctionDecl {{.*}}{{.*}}dup {{.*}} +// CHECK: SYCLIntelFPGAMaxConcurrencyAttr {{.*}} +// CHECK: ConstantExpr {{.*}} 'int' +// CHECK: value: Int 8 +// CHECK: IntegerLiteral {{.*}}8{{$}} +// CHECK: FunctionDecl {{.*}}{{.*}}dup {{.*}} +// CHECK: SYCLIntelFPGAMaxConcurrencyAttr {{.*}} +// CHECK: ConstantExpr {{.*}} 'int' +// CHECK: value: Int 9 +// CHECK: IntegerLiteral {{.*}}9{{$}} +// CHECK: FunctionDecl {{.*}}{{.*}}kernel_name1{{.*}} +// CHECK: SYCLIntelFPGAMaxConcurrencyAttr {{.*}} +// CHECK: ConstantExpr {{.*}} 'int' +// CHECK: value: Int 4 +// CHECK: IntegerLiteral{{.*}}4{{$}} +// CHECK: FunctionDecl {{.*}}{{.*}}kernel_name3{{.*}} +// CHECK: SYCLIntelFPGAMaxConcurrencyAttr {{.*}} +// CHECK: ConstantExpr {{.*}} 'int' +// CHECK: value: Int 3 +// CHECK: IntegerLiteral{{.*}}3{{$}} +// CHECK: FunctionDecl {{.*}}{{.*}}kernel_name4{{.*}} +// CHECK: SYCLIntelFPGAMaxConcurrencyAttr {{.*}} +// CHECK: ConstantExpr {{.*}} 'int' +// CHECK: value: Int 4 +// CHECK: IntegerLiteral{{.*}}4{{$}}