Skip to content

[ESIMD] Add support for vectorizing scalar functions - FE part #3603

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
May 5, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions clang/include/clang/Basic/Attr.td
Original file line number Diff line number Diff line change
Expand Up @@ -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 = [];
Expand Down
2 changes: 2 additions & 0 deletions clang/include/clang/Basic/DiagnosticSemaKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -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 ">;
Expand Down
5 changes: 5 additions & 0 deletions clang/include/clang/Sema/Sema.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
8 changes: 8 additions & 0 deletions clang/lib/CodeGen/CGSYCLRuntime.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,14 @@ bool CGSYCLRuntime::actOnFunctionStart(const FunctionDecl &FD,
if (FD.hasAttr<SYCLSimdAttr>())
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<SYCLIntelESimdVectorizeAttr>())
if (const auto *DeclExpr = cast<ConstantExpr>(A->getValue())) {
SmallString<2> Str;
DeclExpr->getResultAsAPSInt().toString(Str);
F.addFnAttr("CMGenxSIMT", Str);
}

SYCLScopeAttr *Scope = FD.getAttr<SYCLScopeAttr>();
if (!Scope)
return false;
Expand Down
2 changes: 2 additions & 0 deletions clang/lib/Sema/SemaDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2627,6 +2627,8 @@ static bool mergeDeclAttribute(Sema &S, NamedDecl *D,
NewAttr = S.MergeIntelNamedSubGroupSizeAttr(D, *A);
else if (const auto *A = dyn_cast<SYCLIntelNumSimdWorkItemsAttr>(Attr))
NewAttr = S.MergeSYCLIntelNumSimdWorkItemsAttr(D, *A);
else if (const auto *A = dyn_cast<SYCLIntelESimdVectorizeAttr>(Attr))
NewAttr = S.MergeSYCLIntelESimdVectorizeAttr(D, *A);
else if (const auto *A = dyn_cast<SYCLIntelSchedulerTargetFmaxMhzAttr>(Attr))
NewAttr = S.MergeSYCLIntelSchedulerTargetFmaxMhzAttr(D, *A);
else if (const auto *A = dyn_cast<SYCLIntelNoGlobalWorkOffsetAttr>(Attr))
Expand Down
70 changes: 70 additions & 0 deletions clang/lib/Sema/SemaDeclAttr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<SYCLIntelESimdVectorizeAttr>()) {
// 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<ConstantExpr>(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<SYCLIntelESimdVectorizeAttr>()) {
if (const auto *DeclExpr = dyn_cast<ConstantExpr>(DeclAttr->getValue())) {
if (const auto *MergeExpr = dyn_cast<ConstantExpr>(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<VarDecl>(D);
if (VD->hasLocalStorage()) {
Expand Down Expand Up @@ -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;
Expand Down
16 changes: 16 additions & 0 deletions clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -733,6 +733,16 @@ static void instantiateSYCLIntelFPGAInitiationIntervalAttr(
S.AddSYCLIntelFPGAInitiationIntervalAttr(New, *A, Result.getAs<Expr>());
}

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<Expr>());
}

/// 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.
Expand Down Expand Up @@ -970,6 +980,12 @@ void Sema::InstantiateAttrs(const MultiLevelTemplateArgumentList &TemplateArgs,
*this, TemplateArgs, SYCLIntelFPGAInitiationInterval, New);
continue;
}
if (const auto *SYCLIntelESimdVectorize =
dyn_cast<SYCLIntelESimdVectorizeAttr>(TmplAttr)) {
instantiateSYCLIntelESimdVectorizeAttr(*this, TemplateArgs,
SYCLIntelESimdVectorize, New);
continue;
}
// Existing DLL attribute on the instantiation takes precedence.
if (TmplAttr->getKind() == attr::DLLExport ||
TmplAttr->getKind() == attr::DLLImport) {
Expand Down
18 changes: 18 additions & 0 deletions clang/test/CodeGenSYCL/esimd-vectorize-md.cpp
Original file line number Diff line number Diff line change
@@ -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" {{.*}}}
48 changes: 48 additions & 0 deletions clang/test/SemaSYCL/esimd-vectorize-attr-device.cpp
Original file line number Diff line number Diff line change
@@ -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 <int N>
[[intel::sycl_esimd_vectorize(N)]] void templateFunc();
11 changes: 11 additions & 0 deletions clang/test/SemaSYCL/esimd-vectorize-attr-host.cpp
Original file line number Diff line number Diff line change
@@ -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() {}
Original file line number Diff line number Diff line change
@@ -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 <typename Ty>
// 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<S>' requested here}}
func<S>();
//expected-note@+1{{in instantiation of function template specialization 'func<float>' requested here}}
func<float>();
//expected-note@+1{{in instantiation of function template specialization 'func<int>' requested here}}
func<int>();
}

// 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 <int SIZE>
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 <int N>
// expected-error@+1{{'sycl_esimd_vectorize' attribute argument must be 8, 16, or 32}}
[[intel::sycl_esimd_vectorize(N)]] void func3() {}

template <int N>
[[intel::sycl_esimd_vectorize(32)]] void func4(); // expected-note {{previous attribute is here}}

template <int N>
[[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{{$}}