diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td index 66eb9a3fdb10f..4a05eb3b4168f 100644 --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -1239,6 +1239,23 @@ def SYCLSimdAccessorPtr : InheritableAttr { let Documentation = [Undocumented]; } +// The attribute denotes that it is a function written in a scalar fashion, which +// is used in ESIMD context and needs to be vectorized by a vector backend compiler. +// For now, this attribute will be used only in internal implementation of +// specific public ESIMD APIs. It is not supposed to be used directly in the +// user code, hence it is undocumented. +// The argument of the attribute specifies the number of SIMD lanes, for which +// the function should be vectorized. +def SYCLIntelESimdVectorize : InheritableAttr { + let Spellings = [CXX11<"intel", "sycl_esimd_vectorize">]; + let Args = [ExprArgument<"Value">]; + let Subjects = SubjectList<[Function], ErrorDiag>; + let LangOpts = [SYCLIsDevice]; + let Documentation = [Undocumented]; + let SupportsNonconformingLambdaSyntax = 1; + let PragmaAttributeSupport = 0; +} + def SYCLScope : Attr { // No spelling, as this attribute can't be created in the source code. let Spellings = []; diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 15dc631014d7f..02ad14b6d36ab 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -11230,6 +11230,8 @@ def err_esimd_global_in_sycl_context : Error< "ESIMD globals cannot be used in a SYCL context">; def err_sycl_device_function_is_called_from_esimd : Error< "SYCL device function cannot be called from an ESIMD context">; +def err_sycl_esimd_vectorize_unsupported_value : Error< + "%0 attribute argument must be 8, 16, or 32">; def err_nullptr_t_type_in_sycl_kernel : Error<"%0 is an invalid kernel name, " "'std::nullptr_t' is declared in the 'std' namespace ">; diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index f1414c3af6e6a..1a40d77537476 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -10282,6 +10282,11 @@ class Sema final { SYCLIntelNumSimdWorkItemsAttr * MergeSYCLIntelNumSimdWorkItemsAttr(Decl *D, const SYCLIntelNumSimdWorkItemsAttr &A); + void AddSYCLIntelESimdVectorizeAttr(Decl *D, const AttributeCommonInfo &CI, + Expr *E); + SYCLIntelESimdVectorizeAttr * + MergeSYCLIntelESimdVectorizeAttr(Decl *D, + const SYCLIntelESimdVectorizeAttr &A); void AddSYCLIntelSchedulerTargetFmaxMhzAttr(Decl *D, const AttributeCommonInfo &CI, Expr *E); diff --git a/clang/lib/CodeGen/CGSYCLRuntime.cpp b/clang/lib/CodeGen/CGSYCLRuntime.cpp index 1cdf825838547..d1af26a9c4d7e 100644 --- a/clang/lib/CodeGen/CGSYCLRuntime.cpp +++ b/clang/lib/CodeGen/CGSYCLRuntime.cpp @@ -64,6 +64,14 @@ bool CGSYCLRuntime::actOnFunctionStart(const FunctionDecl &FD, if (FD.hasAttr()) F.setMetadata("sycl_explicit_simd", llvm::MDNode::get(F.getContext(), {})); + // Set the function attribute expected by the vector backend compiler. + if (const auto *A = FD.getAttr()) + if (const auto *DeclExpr = cast(A->getValue())) { + SmallString<2> Str; + DeclExpr->getResultAsAPSInt().toString(Str); + F.addFnAttr("CMGenxSIMT", Str); + } + SYCLScopeAttr *Scope = FD.getAttr(); if (!Scope) return false; diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index fd832859189f2..4569d408f327b 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -2627,6 +2627,8 @@ static bool mergeDeclAttribute(Sema &S, NamedDecl *D, NewAttr = S.MergeIntelNamedSubGroupSizeAttr(D, *A); else if (const auto *A = dyn_cast(Attr)) NewAttr = S.MergeSYCLIntelNumSimdWorkItemsAttr(D, *A); + else if (const auto *A = dyn_cast(Attr)) + NewAttr = S.MergeSYCLIntelESimdVectorizeAttr(D, *A); else if (const auto *A = dyn_cast(Attr)) NewAttr = S.MergeSYCLIntelSchedulerTargetFmaxMhzAttr(D, *A); else if (const auto *A = dyn_cast(Attr)) diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp index e5bab0ceef73e..548fa364d08d9 100644 --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -5087,6 +5087,73 @@ static void handleSYCLRegisterNumAttr(Sema &S, Decl *D, const ParsedAttr &AL) { D->addAttr(::new (S.Context) SYCLRegisterNumAttr(S.Context, AL, RegNo)); } +void Sema::AddSYCLIntelESimdVectorizeAttr(Decl *D, + const AttributeCommonInfo &CI, + Expr *E) { + if (!E->isValueDependent()) { + // Validate that we have an integer constant expression and then store the + // converted constant expression into the semantic attribute so that we + // don't have to evaluate it again later. + llvm::APSInt ArgVal; + ExprResult Res = VerifyIntegerConstantExpression(E, &ArgVal); + if (Res.isInvalid()) + return; + E = Res.get(); + + if (ArgVal != 8 && ArgVal != 16 && ArgVal != 32) { + Diag(E->getExprLoc(), diag::err_sycl_esimd_vectorize_unsupported_value) + << CI; + return; + } + + // Check to see if there's a duplicate attribute with different values + // already applied to the declaration. + if (const auto *DeclAttr = D->getAttr()) { + // If the other attribute argument is instantiation dependent, we won't + // have converted it to a constant expression yet and thus we test + // whether this is a null pointer. + if (const auto *DeclExpr = dyn_cast(DeclAttr->getValue())) { + if (ArgVal != DeclExpr->getResultAsAPSInt()) { + Diag(CI.getLoc(), diag::warn_duplicate_attribute) << CI; + Diag(DeclAttr->getLoc(), diag::note_previous_attribute); + } + // Drop the duplicate attribute. + return; + } + } + } + + D->addAttr(::new (Context) SYCLIntelESimdVectorizeAttr(Context, CI, E)); +} + +SYCLIntelESimdVectorizeAttr * +Sema::MergeSYCLIntelESimdVectorizeAttr(Decl *D, + const SYCLIntelESimdVectorizeAttr &A) { + // Check to see if there's a duplicate attribute with different values + // already applied to the declaration. + if (const auto *DeclAttr = D->getAttr()) { + if (const auto *DeclExpr = dyn_cast(DeclAttr->getValue())) { + if (const auto *MergeExpr = dyn_cast(A.getValue())) { + if (DeclExpr->getResultAsAPSInt() != MergeExpr->getResultAsAPSInt()) { + Diag(DeclAttr->getLoc(), diag::warn_duplicate_attribute) << &A; + Diag(A.getLoc(), diag::note_previous_attribute); + } + // Do not add a duplicate attribute. + return nullptr; + } + } + } + return ::new (Context) SYCLIntelESimdVectorizeAttr(Context, A, A.getValue()); +} + +static void handleSYCLIntelESimdVectorizeAttr(Sema &S, Decl *D, + const ParsedAttr &A) { + S.CheckDeprecatedSYCLAttributeSpelling(A); + + Expr *E = A.getArgAsExpr(0); + S.AddSYCLIntelESimdVectorizeAttr(D, A, E); +} + static void handleConstantAttr(Sema &S, Decl *D, const ParsedAttr &AL) { const auto *VD = cast(D); if (VD->hasLocalStorage()) { @@ -9107,6 +9174,9 @@ static void ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, case ParsedAttr::AT_SYCLRegisterNum: handleSYCLRegisterNumAttr(S, D, AL); break; + case ParsedAttr::AT_SYCLIntelESimdVectorize: + handleSYCLIntelESimdVectorizeAttr(S, D, AL); + break; case ParsedAttr::AT_Format: handleFormatAttr(S, D, AL); break; diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp index 5effaaf5a3d58..51e274ee6adc0 100644 --- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -733,6 +733,16 @@ static void instantiateSYCLIntelFPGAInitiationIntervalAttr( S.AddSYCLIntelFPGAInitiationIntervalAttr(New, *A, Result.getAs()); } +static void instantiateSYCLIntelESimdVectorizeAttr( + Sema &S, const MultiLevelTemplateArgumentList &TemplateArgs, + const SYCLIntelESimdVectorizeAttr *A, Decl *New) { + EnterExpressionEvaluationContext Unevaluated( + S, Sema::ExpressionEvaluationContext::ConstantEvaluated); + ExprResult Result = S.SubstExpr(A->getValue(), TemplateArgs); + if (!Result.isInvalid()) + S.AddSYCLIntelESimdVectorizeAttr(New, *A, Result.getAs()); +} + /// Determine whether the attribute A might be relevent to the declaration D. /// If not, we can skip instantiating it. The attribute may or may not have /// been instantiated yet. @@ -970,6 +980,12 @@ void Sema::InstantiateAttrs(const MultiLevelTemplateArgumentList &TemplateArgs, *this, TemplateArgs, SYCLIntelFPGAInitiationInterval, New); continue; } + if (const auto *SYCLIntelESimdVectorize = + dyn_cast(TmplAttr)) { + instantiateSYCLIntelESimdVectorizeAttr(*this, TemplateArgs, + SYCLIntelESimdVectorize, 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/esimd-vectorize-md.cpp b/clang/test/CodeGenSYCL/esimd-vectorize-md.cpp new file mode 100644 index 0000000000000..1446e4e94122d --- /dev/null +++ b/clang/test/CodeGenSYCL/esimd-vectorize-md.cpp @@ -0,0 +1,18 @@ +// RUN: %clang_cc1 -disable-llvm-passes -triple spir64-unknown-unknown-sycldevice \ +// RUN: -fsycl-is-device -S -emit-llvm %s -o - | FileCheck %s + +// This test checks the generation of CMGenxSIMT function attributes + +[[intel::sycl_esimd_vectorize(32)]] __attribute__((sycl_device)) void foo1() {} +// CHECK: @_Z4foo1v() #[[ATTR1:[0-9]+]] + +[[intel::sycl_esimd_vectorize(8)]] [[intel::sycl_esimd_vectorize(16)]] __attribute__((sycl_device)) void foo2() {} +// CHECK: @_Z4foo2v() #[[ATTR2:[0-9]+]] + +[[intel::sycl_esimd_vectorize(8)]] __attribute__((sycl_device)) void foo3(); +[[intel::sycl_esimd_vectorize(16)]] __attribute__((sycl_device)) void foo3() {} +// CHECK: @_Z4foo3v() #[[ATTR3:[0-9]+]] + +// CHECK: attributes #[[ATTR1]] = { {{.*}} "CMGenxSIMT"="32" {{.*}}} +// CHECK: attributes #[[ATTR2]] = { {{.*}} "CMGenxSIMT"="8" {{.*}}} +// CHECK: attributes #[[ATTR3]] = { {{.*}} "CMGenxSIMT"="16" {{.*}}} diff --git a/clang/test/SemaSYCL/esimd-vectorize-attr-device.cpp b/clang/test/SemaSYCL/esimd-vectorize-attr-device.cpp new file mode 100644 index 0000000000000..e15cb67c980e7 --- /dev/null +++ b/clang/test/SemaSYCL/esimd-vectorize-attr-device.cpp @@ -0,0 +1,48 @@ +// RUN: %clang_cc1 -fsycl-is-device -fsyntax-only -verify %s + +// This test check the semantics of sycl_esimd_vectorize attribute + +// expected-error@+1{{'sycl_esimd_vectorize' attribute argument must be 8, 16, or 32}} +[[intel::sycl_esimd_vectorize(17)]] void foo1() {} +// expected-error@+1{{integral constant expression must have integral or unscoped enumeration type, not 'float'}} +[[intel::sycl_esimd_vectorize(3.f)]] void foo3() {} + +[[intel::sycl_esimd_vectorize(8)]] void foo4() {} +[[intel::sycl_esimd_vectorize(16)]] void foo5() {} +[[intel::sycl_esimd_vectorize(32)]] void foo6() {} + +// We explicitly do not support a GNU spelling for this attribute, which is why it is +// treated as an unknown attribute. +// expected-warning@+1{{unknown attribute 'sycl_esimd_vectorize' ignored}} +__attribute__((sycl_esimd_vectorize(8))) void foo7() {} + +// expected-note@+2{{previous attribute is here}} +// expected-warning@+1{{attribute 'sycl_esimd_vectorize' is already applied with different arguments}} +[[intel::sycl_esimd_vectorize(8)]] [[intel::sycl_esimd_vectorize(16)]] void foo8() {} + +// expected-note@+1{{previous attribute is here}} +[[intel::sycl_esimd_vectorize(8)]] void foo9(); +// expected-warning@+1{{attribute 'sycl_esimd_vectorize' is already applied with different arguments}} +[[intel::sycl_esimd_vectorize(16)]] void foo9() {} + +// No diagnostic is emitted because the arguments match. +[[intel::sycl_esimd_vectorize(16)]] void foo10(); +[[intel::sycl_esimd_vectorize(16)]] void foo10() {} + +// expected-error@+1{{'sycl_esimd_vectorize' attribute only applies to functions}} +[[intel::sycl_esimd_vectorize(8)]] int glob = 0; + +class A { + [[intel::sycl_esimd_vectorize(8)]] void func2() {} +}; + +struct Functor { + [[intel::sycl_esimd_vectorize(8)]] void operator()(float) const {} +}; + +void test() { + auto f2 = []() [[intel::sycl_esimd_vectorize(8)]]{}; +} + +template +[[intel::sycl_esimd_vectorize(N)]] void templateFunc(); diff --git a/clang/test/SemaSYCL/esimd-vectorize-attr-host.cpp b/clang/test/SemaSYCL/esimd-vectorize-attr-host.cpp new file mode 100644 index 0000000000000..dbb4a7b81501d --- /dev/null +++ b/clang/test/SemaSYCL/esimd-vectorize-attr-host.cpp @@ -0,0 +1,11 @@ +// RUN: %clang_cc1 -fsycl-is-host -fsyntax-only -verify %s + +// This test check the semantics of sycl_esimd_vectorize attribute + +// expected-warning@+1{{'sycl_esimd_vectorize' attribute ignored}} +[[intel::sycl_esimd_vectorize(17)]] void foo1() {} + +// We explicitly do not support a GNU spelling for this attribute, which is why it is +// treated as an unknown attribute. +// expected-warning@+1{{unknown attribute 'sycl_esimd_vectorize' ignored}} +__attribute__((sycl_esimd_vectorize(8))) void foo2() {} diff --git a/clang/test/SemaSYCL/sycl-device-sycl_esimd_vectorize-template.cpp b/clang/test/SemaSYCL/sycl-device-sycl_esimd_vectorize-template.cpp new file mode 100644 index 0000000000000..bf9fbde4e443d --- /dev/null +++ b/clang/test/SemaSYCL/sycl-device-sycl_esimd_vectorize-template.cpp @@ -0,0 +1,95 @@ +// RUN: %clang_cc1 -fsycl-is-device -fsyntax-only -ast-dump -verify -pedantic %s | FileCheck %s + +// Test that checks template parameter support for 'sycl_esimd_vectorize' attribute on sycl device. + +// Test wrong function template instantiation and ensure that the type +// is checked properly when instantiating from the template definition. +template +// expected-error@+3{{integral constant expression must have integral or unscoped enumeration type, not 'S'}} +// expected-error@+2{{integral constant expression must have integral or unscoped enumeration type, not 'float'}} +// expected-error@+1{{'sycl_esimd_vectorize' attribute argument must be 8, 16, or 32}} +[[intel::sycl_esimd_vectorize(Ty{})]] void func() {} + +struct S {}; +void test() { + //expected-note@+1{{in instantiation of function template specialization 'func' requested here}} + func(); + //expected-note@+1{{in instantiation of function template specialization 'func' requested here}} + func(); + //expected-note@+1{{in instantiation of function template specialization 'func' requested here}} + func(); +} + +// Test a non-constant expression. +// expected-note@+1{{declared here}} +int foo(); +// expected-error@+2{{expression is not an integral constant expression}} +// expected-note@+1{{non-constexpr function 'foo' cannot be used in a constant expression}} +[[intel::sycl_esimd_vectorize(foo() + 12)]] void func1(); + +// Test a constant expression. +constexpr int bar() { return 0; } +[[intel::sycl_esimd_vectorize(bar() + 16)]] void func2(); // OK + +// Test template parameter support on member function of class template. +template +class KernelFunctor { +public: + // expected-error@+1{{'sycl_esimd_vectorize' attribute argument must be 8, 16, or 32}} + [[intel::sycl_esimd_vectorize(SIZE)]] void operator()() {} +}; + +int main() { + //expected-note@+1{{in instantiation of template class 'KernelFunctor<-1>' requested here}} + KernelFunctor<-1>(); + // no error expected + KernelFunctor<8>(); + return 0; +} + +// CHECK: ClassTemplateDecl {{.*}} {{.*}} KernelFunctor +// CHECK: ClassTemplateSpecializationDecl {{.*}} {{.*}} class KernelFunctor definition +// CHECK: CXXRecordDecl {{.*}} {{.*}} implicit class KernelFunctor +// CHECK: SYCLIntelESimdVectorizeAttr {{.*}} +// CHECK: SubstNonTypeTemplateParmExpr {{.*}} +// CHECK-NEXT: NonTypeTemplateParmDecl {{.*}} +// CHECK-NEXT: IntegerLiteral{{.*}}8{{$}} + +// Test template parameter support on function. +template +// expected-error@+1{{'sycl_esimd_vectorize' attribute argument must be 8, 16, or 32}} +[[intel::sycl_esimd_vectorize(N)]] void func3() {} + +template +[[intel::sycl_esimd_vectorize(32)]] void func4(); // expected-note {{previous attribute is here}} + +template +[[intel::sycl_esimd_vectorize(N)]] void func4() {} // expected-warning {{attribute 'sycl_esimd_vectorize' is already applied with different arguments}} + +int check() { + // no error expected. + func3<8>(); + //expected-note@+1{{in instantiation of function template specialization 'func3<-1>' requested here}} + func3<-1>(); + //expected-note@+1 {{in instantiation of function template specialization 'func4<16>' requested here}} + func4<16>(); + return 0; +} + +// No diagnostic is emitted because the arguments match. Duplicate attribute is silently ignored. +[[intel::sycl_esimd_vectorize(8)]] +[[intel::sycl_esimd_vectorize(8)]] void func5() {} + +// CHECK: FunctionTemplateDecl {{.*}} {{.*}} func3 +// CHECK: NonTypeTemplateParmDecl {{.*}} {{.*}} referenced 'int' depth 0 index 0 N +// CHECK: FunctionDecl {{.*}} {{.*}} func3 'void ()' +// CHECK: SYCLIntelESimdVectorizeAttr {{.*}} +// CHECK: SubstNonTypeTemplateParmExpr {{.*}} +// CHECK-NEXT: NonTypeTemplateParmDecl {{.*}} +// CHECK-NEXT: IntegerLiteral{{.*}}8{{$}} + +// CHECK: FunctionDecl {{.*}} {{.*}} func5 'void ()' +// CHECK: SYCLIntelESimdVectorizeAttr {{.*}} +// CHECK-NEXT: ConstantExpr {{.*}} 'int' +// CHECK-NEXT: value: Int 8 +// CHECK-NEXT: IntegerLiteral{{.*}}8{{$}}