Skip to content

Commit 3fc66cc

Browse files
[SYCL] [ESIMD] Add support for vectorizing scalar functions - FE part (#3603)
This patch is one of the few patches to provide a functionality to reuse existing scalar function implementations in a vector context. For example, based on scalar version of `float cos(float)`, implement simd<float,N> cos(simd<float,N>) - "widened" version of the original scalar function. To achieve that, we mark such functions with `sycl_esimd_widen` attribute which will be later used by vector compiler backend to widen it. For now, this functionality will be used only in internal implementation of specific public ESIMD APIs. It is not supposed to be used directly in the user code. It is a temporary solution to enable ESIMD math functions by reusing existing scalar implementations. Later it is supposed to be replaced by a more generic mechanism.
1 parent 25433ba commit 3fc66cc

11 files changed

+292
-0
lines changed

clang/include/clang/Basic/Attr.td

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1239,6 +1239,23 @@ def SYCLSimdAccessorPtr : InheritableAttr {
12391239
let Documentation = [Undocumented];
12401240
}
12411241

1242+
// The attribute denotes that it is a function written in a scalar fashion, which
1243+
// is used in ESIMD context and needs to be vectorized by a vector backend compiler.
1244+
// For now, this attribute will be used only in internal implementation of
1245+
// specific public ESIMD APIs. It is not supposed to be used directly in the
1246+
// user code, hence it is undocumented.
1247+
// The argument of the attribute specifies the number of SIMD lanes, for which
1248+
// the function should be vectorized.
1249+
def SYCLIntelESimdVectorize : InheritableAttr {
1250+
let Spellings = [CXX11<"intel", "sycl_esimd_vectorize">];
1251+
let Args = [ExprArgument<"Value">];
1252+
let Subjects = SubjectList<[Function], ErrorDiag>;
1253+
let LangOpts = [SYCLIsDevice];
1254+
let Documentation = [Undocumented];
1255+
let SupportsNonconformingLambdaSyntax = 1;
1256+
let PragmaAttributeSupport = 0;
1257+
}
1258+
12421259
def SYCLScope : Attr {
12431260
// No spelling, as this attribute can't be created in the source code.
12441261
let Spellings = [];

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11291,6 +11291,8 @@ def err_esimd_global_in_sycl_context : Error<
1129111291
"ESIMD globals cannot be used in a SYCL context">;
1129211292
def err_sycl_device_function_is_called_from_esimd : Error<
1129311293
"SYCL device function cannot be called from an ESIMD context">;
11294+
def err_sycl_esimd_vectorize_unsupported_value : Error<
11295+
"%0 attribute argument must be 8, 16, or 32">;
1129411296

1129511297
def err_nullptr_t_type_in_sycl_kernel : Error<"%0 is an invalid kernel name, "
1129611298
"'std::nullptr_t' is declared in the 'std' namespace ">;

clang/include/clang/Sema/Sema.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10289,6 +10289,11 @@ class Sema final {
1028910289
SYCLIntelNumSimdWorkItemsAttr *
1029010290
MergeSYCLIntelNumSimdWorkItemsAttr(Decl *D,
1029110291
const SYCLIntelNumSimdWorkItemsAttr &A);
10292+
void AddSYCLIntelESimdVectorizeAttr(Decl *D, const AttributeCommonInfo &CI,
10293+
Expr *E);
10294+
SYCLIntelESimdVectorizeAttr *
10295+
MergeSYCLIntelESimdVectorizeAttr(Decl *D,
10296+
const SYCLIntelESimdVectorizeAttr &A);
1029210297
void AddSYCLIntelSchedulerTargetFmaxMhzAttr(Decl *D,
1029310298
const AttributeCommonInfo &CI,
1029410299
Expr *E);

clang/lib/CodeGen/CGSYCLRuntime.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,14 @@ bool CGSYCLRuntime::actOnFunctionStart(const FunctionDecl &FD,
6666
if (FD.hasAttr<SYCLSimdAttr>())
6767
F.setMetadata("sycl_explicit_simd", llvm::MDNode::get(F.getContext(), {}));
6868

69+
// Set the function attribute expected by the vector backend compiler.
70+
if (const auto *A = FD.getAttr<SYCLIntelESimdVectorizeAttr>())
71+
if (const auto *DeclExpr = cast<ConstantExpr>(A->getValue())) {
72+
SmallString<2> Str;
73+
DeclExpr->getResultAsAPSInt().toString(Str);
74+
F.addFnAttr("CMGenxSIMT", Str);
75+
}
76+
6977
SYCLScopeAttr *Scope = FD.getAttr<SYCLScopeAttr>();
7078
if (!Scope)
7179
return false;

clang/lib/Sema/SemaDecl.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2627,6 +2627,8 @@ static bool mergeDeclAttribute(Sema &S, NamedDecl *D,
26272627
NewAttr = S.MergeIntelNamedSubGroupSizeAttr(D, *A);
26282628
else if (const auto *A = dyn_cast<SYCLIntelNumSimdWorkItemsAttr>(Attr))
26292629
NewAttr = S.MergeSYCLIntelNumSimdWorkItemsAttr(D, *A);
2630+
else if (const auto *A = dyn_cast<SYCLIntelESimdVectorizeAttr>(Attr))
2631+
NewAttr = S.MergeSYCLIntelESimdVectorizeAttr(D, *A);
26302632
else if (const auto *A = dyn_cast<SYCLIntelSchedulerTargetFmaxMhzAttr>(Attr))
26312633
NewAttr = S.MergeSYCLIntelSchedulerTargetFmaxMhzAttr(D, *A);
26322634
else if (const auto *A = dyn_cast<SYCLIntelNoGlobalWorkOffsetAttr>(Attr))

clang/lib/Sema/SemaDeclAttr.cpp

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5087,6 +5087,73 @@ static void handleSYCLRegisterNumAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
50875087
D->addAttr(::new (S.Context) SYCLRegisterNumAttr(S.Context, AL, RegNo));
50885088
}
50895089

5090+
void Sema::AddSYCLIntelESimdVectorizeAttr(Decl *D,
5091+
const AttributeCommonInfo &CI,
5092+
Expr *E) {
5093+
if (!E->isValueDependent()) {
5094+
// Validate that we have an integer constant expression and then store the
5095+
// converted constant expression into the semantic attribute so that we
5096+
// don't have to evaluate it again later.
5097+
llvm::APSInt ArgVal;
5098+
ExprResult Res = VerifyIntegerConstantExpression(E, &ArgVal);
5099+
if (Res.isInvalid())
5100+
return;
5101+
E = Res.get();
5102+
5103+
if (ArgVal != 8 && ArgVal != 16 && ArgVal != 32) {
5104+
Diag(E->getExprLoc(), diag::err_sycl_esimd_vectorize_unsupported_value)
5105+
<< CI;
5106+
return;
5107+
}
5108+
5109+
// Check to see if there's a duplicate attribute with different values
5110+
// already applied to the declaration.
5111+
if (const auto *DeclAttr = D->getAttr<SYCLIntelESimdVectorizeAttr>()) {
5112+
// If the other attribute argument is instantiation dependent, we won't
5113+
// have converted it to a constant expression yet and thus we test
5114+
// whether this is a null pointer.
5115+
if (const auto *DeclExpr = dyn_cast<ConstantExpr>(DeclAttr->getValue())) {
5116+
if (ArgVal != DeclExpr->getResultAsAPSInt()) {
5117+
Diag(CI.getLoc(), diag::warn_duplicate_attribute) << CI;
5118+
Diag(DeclAttr->getLoc(), diag::note_previous_attribute);
5119+
}
5120+
// Drop the duplicate attribute.
5121+
return;
5122+
}
5123+
}
5124+
}
5125+
5126+
D->addAttr(::new (Context) SYCLIntelESimdVectorizeAttr(Context, CI, E));
5127+
}
5128+
5129+
SYCLIntelESimdVectorizeAttr *
5130+
Sema::MergeSYCLIntelESimdVectorizeAttr(Decl *D,
5131+
const SYCLIntelESimdVectorizeAttr &A) {
5132+
// Check to see if there's a duplicate attribute with different values
5133+
// already applied to the declaration.
5134+
if (const auto *DeclAttr = D->getAttr<SYCLIntelESimdVectorizeAttr>()) {
5135+
if (const auto *DeclExpr = dyn_cast<ConstantExpr>(DeclAttr->getValue())) {
5136+
if (const auto *MergeExpr = dyn_cast<ConstantExpr>(A.getValue())) {
5137+
if (DeclExpr->getResultAsAPSInt() != MergeExpr->getResultAsAPSInt()) {
5138+
Diag(DeclAttr->getLoc(), diag::warn_duplicate_attribute) << &A;
5139+
Diag(A.getLoc(), diag::note_previous_attribute);
5140+
}
5141+
// Do not add a duplicate attribute.
5142+
return nullptr;
5143+
}
5144+
}
5145+
}
5146+
return ::new (Context) SYCLIntelESimdVectorizeAttr(Context, A, A.getValue());
5147+
}
5148+
5149+
static void handleSYCLIntelESimdVectorizeAttr(Sema &S, Decl *D,
5150+
const ParsedAttr &A) {
5151+
S.CheckDeprecatedSYCLAttributeSpelling(A);
5152+
5153+
Expr *E = A.getArgAsExpr(0);
5154+
S.AddSYCLIntelESimdVectorizeAttr(D, A, E);
5155+
}
5156+
50905157
static void handleConstantAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
50915158
const auto *VD = cast<VarDecl>(D);
50925159
if (VD->hasLocalStorage()) {
@@ -9107,6 +9174,9 @@ static void ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D,
91079174
case ParsedAttr::AT_SYCLRegisterNum:
91089175
handleSYCLRegisterNumAttr(S, D, AL);
91099176
break;
9177+
case ParsedAttr::AT_SYCLIntelESimdVectorize:
9178+
handleSYCLIntelESimdVectorizeAttr(S, D, AL);
9179+
break;
91109180
case ParsedAttr::AT_Format:
91119181
handleFormatAttr(S, D, AL);
91129182
break;

clang/lib/Sema/SemaTemplateInstantiateDecl.cpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -733,6 +733,16 @@ static void instantiateSYCLIntelFPGAInitiationIntervalAttr(
733733
S.AddSYCLIntelFPGAInitiationIntervalAttr(New, *A, Result.getAs<Expr>());
734734
}
735735

736+
static void instantiateSYCLIntelESimdVectorizeAttr(
737+
Sema &S, const MultiLevelTemplateArgumentList &TemplateArgs,
738+
const SYCLIntelESimdVectorizeAttr *A, Decl *New) {
739+
EnterExpressionEvaluationContext Unevaluated(
740+
S, Sema::ExpressionEvaluationContext::ConstantEvaluated);
741+
ExprResult Result = S.SubstExpr(A->getValue(), TemplateArgs);
742+
if (!Result.isInvalid())
743+
S.AddSYCLIntelESimdVectorizeAttr(New, *A, Result.getAs<Expr>());
744+
}
745+
736746
/// Determine whether the attribute A might be relevent to the declaration D.
737747
/// If not, we can skip instantiating it. The attribute may or may not have
738748
/// been instantiated yet.
@@ -970,6 +980,12 @@ void Sema::InstantiateAttrs(const MultiLevelTemplateArgumentList &TemplateArgs,
970980
*this, TemplateArgs, SYCLIntelFPGAInitiationInterval, New);
971981
continue;
972982
}
983+
if (const auto *SYCLIntelESimdVectorize =
984+
dyn_cast<SYCLIntelESimdVectorizeAttr>(TmplAttr)) {
985+
instantiateSYCLIntelESimdVectorizeAttr(*this, TemplateArgs,
986+
SYCLIntelESimdVectorize, New);
987+
continue;
988+
}
973989
// Existing DLL attribute on the instantiation takes precedence.
974990
if (TmplAttr->getKind() == attr::DLLExport ||
975991
TmplAttr->getKind() == attr::DLLImport) {
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// RUN: %clang_cc1 -disable-llvm-passes -triple spir64-unknown-unknown-sycldevice \
2+
// RUN: -fsycl-is-device -S -emit-llvm %s -o - | FileCheck %s
3+
4+
// This test checks the generation of CMGenxSIMT function attributes
5+
6+
[[intel::sycl_esimd_vectorize(32)]] __attribute__((sycl_device)) void foo1() {}
7+
// CHECK: @_Z4foo1v() #[[ATTR1:[0-9]+]]
8+
9+
[[intel::sycl_esimd_vectorize(8)]] [[intel::sycl_esimd_vectorize(16)]] __attribute__((sycl_device)) void foo2() {}
10+
// CHECK: @_Z4foo2v() #[[ATTR2:[0-9]+]]
11+
12+
[[intel::sycl_esimd_vectorize(8)]] __attribute__((sycl_device)) void foo3();
13+
[[intel::sycl_esimd_vectorize(16)]] __attribute__((sycl_device)) void foo3() {}
14+
// CHECK: @_Z4foo3v() #[[ATTR3:[0-9]+]]
15+
16+
// CHECK: attributes #[[ATTR1]] = { {{.*}} "CMGenxSIMT"="32" {{.*}}}
17+
// CHECK: attributes #[[ATTR2]] = { {{.*}} "CMGenxSIMT"="8" {{.*}}}
18+
// CHECK: attributes #[[ATTR3]] = { {{.*}} "CMGenxSIMT"="16" {{.*}}}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
// RUN: %clang_cc1 -fsycl-is-device -fsyntax-only -verify %s
2+
3+
// This test check the semantics of sycl_esimd_vectorize attribute
4+
5+
// expected-error@+1{{'sycl_esimd_vectorize' attribute argument must be 8, 16, or 32}}
6+
[[intel::sycl_esimd_vectorize(17)]] void foo1() {}
7+
// expected-error@+1{{integral constant expression must have integral or unscoped enumeration type, not 'float'}}
8+
[[intel::sycl_esimd_vectorize(3.f)]] void foo3() {}
9+
10+
[[intel::sycl_esimd_vectorize(8)]] void foo4() {}
11+
[[intel::sycl_esimd_vectorize(16)]] void foo5() {}
12+
[[intel::sycl_esimd_vectorize(32)]] void foo6() {}
13+
14+
// We explicitly do not support a GNU spelling for this attribute, which is why it is
15+
// treated as an unknown attribute.
16+
// expected-warning@+1{{unknown attribute 'sycl_esimd_vectorize' ignored}}
17+
__attribute__((sycl_esimd_vectorize(8))) void foo7() {}
18+
19+
// expected-note@+2{{previous attribute is here}}
20+
// expected-warning@+1{{attribute 'sycl_esimd_vectorize' is already applied with different arguments}}
21+
[[intel::sycl_esimd_vectorize(8)]] [[intel::sycl_esimd_vectorize(16)]] void foo8() {}
22+
23+
// expected-note@+1{{previous attribute is here}}
24+
[[intel::sycl_esimd_vectorize(8)]] void foo9();
25+
// expected-warning@+1{{attribute 'sycl_esimd_vectorize' is already applied with different arguments}}
26+
[[intel::sycl_esimd_vectorize(16)]] void foo9() {}
27+
28+
// No diagnostic is emitted because the arguments match.
29+
[[intel::sycl_esimd_vectorize(16)]] void foo10();
30+
[[intel::sycl_esimd_vectorize(16)]] void foo10() {}
31+
32+
// expected-error@+1{{'sycl_esimd_vectorize' attribute only applies to functions}}
33+
[[intel::sycl_esimd_vectorize(8)]] int glob = 0;
34+
35+
class A {
36+
[[intel::sycl_esimd_vectorize(8)]] void func2() {}
37+
};
38+
39+
struct Functor {
40+
[[intel::sycl_esimd_vectorize(8)]] void operator()(float) const {}
41+
};
42+
43+
void test() {
44+
auto f2 = []() [[intel::sycl_esimd_vectorize(8)]]{};
45+
}
46+
47+
template <int N>
48+
[[intel::sycl_esimd_vectorize(N)]] void templateFunc();
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// RUN: %clang_cc1 -fsycl-is-host -fsyntax-only -verify %s
2+
3+
// This test check the semantics of sycl_esimd_vectorize attribute
4+
5+
// expected-warning@+1{{'sycl_esimd_vectorize' attribute ignored}}
6+
[[intel::sycl_esimd_vectorize(17)]] void foo1() {}
7+
8+
// We explicitly do not support a GNU spelling for this attribute, which is why it is
9+
// treated as an unknown attribute.
10+
// expected-warning@+1{{unknown attribute 'sycl_esimd_vectorize' ignored}}
11+
__attribute__((sycl_esimd_vectorize(8))) void foo2() {}
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
// RUN: %clang_cc1 -fsycl-is-device -fsyntax-only -ast-dump -verify -pedantic %s | FileCheck %s
2+
3+
// Test that checks template parameter support for 'sycl_esimd_vectorize' attribute on sycl device.
4+
5+
// Test wrong function template instantiation and ensure that the type
6+
// is checked properly when instantiating from the template definition.
7+
template <typename Ty>
8+
// expected-error@+3{{integral constant expression must have integral or unscoped enumeration type, not 'S'}}
9+
// expected-error@+2{{integral constant expression must have integral or unscoped enumeration type, not 'float'}}
10+
// expected-error@+1{{'sycl_esimd_vectorize' attribute argument must be 8, 16, or 32}}
11+
[[intel::sycl_esimd_vectorize(Ty{})]] void func() {}
12+
13+
struct S {};
14+
void test() {
15+
//expected-note@+1{{in instantiation of function template specialization 'func<S>' requested here}}
16+
func<S>();
17+
//expected-note@+1{{in instantiation of function template specialization 'func<float>' requested here}}
18+
func<float>();
19+
//expected-note@+1{{in instantiation of function template specialization 'func<int>' requested here}}
20+
func<int>();
21+
}
22+
23+
// Test a non-constant expression.
24+
// expected-note@+1{{declared here}}
25+
int foo();
26+
// expected-error@+2{{expression is not an integral constant expression}}
27+
// expected-note@+1{{non-constexpr function 'foo' cannot be used in a constant expression}}
28+
[[intel::sycl_esimd_vectorize(foo() + 12)]] void func1();
29+
30+
// Test a constant expression.
31+
constexpr int bar() { return 0; }
32+
[[intel::sycl_esimd_vectorize(bar() + 16)]] void func2(); // OK
33+
34+
// Test template parameter support on member function of class template.
35+
template <int SIZE>
36+
class KernelFunctor {
37+
public:
38+
// expected-error@+1{{'sycl_esimd_vectorize' attribute argument must be 8, 16, or 32}}
39+
[[intel::sycl_esimd_vectorize(SIZE)]] void operator()() {}
40+
};
41+
42+
int main() {
43+
//expected-note@+1{{in instantiation of template class 'KernelFunctor<-1>' requested here}}
44+
KernelFunctor<-1>();
45+
// no error expected
46+
KernelFunctor<8>();
47+
return 0;
48+
}
49+
50+
// CHECK: ClassTemplateDecl {{.*}} {{.*}} KernelFunctor
51+
// CHECK: ClassTemplateSpecializationDecl {{.*}} {{.*}} class KernelFunctor definition
52+
// CHECK: CXXRecordDecl {{.*}} {{.*}} implicit class KernelFunctor
53+
// CHECK: SYCLIntelESimdVectorizeAttr {{.*}}
54+
// CHECK: SubstNonTypeTemplateParmExpr {{.*}}
55+
// CHECK-NEXT: NonTypeTemplateParmDecl {{.*}}
56+
// CHECK-NEXT: IntegerLiteral{{.*}}8{{$}}
57+
58+
// Test template parameter support on function.
59+
template <int N>
60+
// expected-error@+1{{'sycl_esimd_vectorize' attribute argument must be 8, 16, or 32}}
61+
[[intel::sycl_esimd_vectorize(N)]] void func3() {}
62+
63+
template <int N>
64+
[[intel::sycl_esimd_vectorize(32)]] void func4(); // expected-note {{previous attribute is here}}
65+
66+
template <int N>
67+
[[intel::sycl_esimd_vectorize(N)]] void func4() {} // expected-warning {{attribute 'sycl_esimd_vectorize' is already applied with different arguments}}
68+
69+
int check() {
70+
// no error expected.
71+
func3<8>();
72+
//expected-note@+1{{in instantiation of function template specialization 'func3<-1>' requested here}}
73+
func3<-1>();
74+
//expected-note@+1 {{in instantiation of function template specialization 'func4<16>' requested here}}
75+
func4<16>();
76+
return 0;
77+
}
78+
79+
// No diagnostic is emitted because the arguments match. Duplicate attribute is silently ignored.
80+
[[intel::sycl_esimd_vectorize(8)]]
81+
[[intel::sycl_esimd_vectorize(8)]] void func5() {}
82+
83+
// CHECK: FunctionTemplateDecl {{.*}} {{.*}} func3
84+
// CHECK: NonTypeTemplateParmDecl {{.*}} {{.*}} referenced 'int' depth 0 index 0 N
85+
// CHECK: FunctionDecl {{.*}} {{.*}} func3 'void ()'
86+
// CHECK: SYCLIntelESimdVectorizeAttr {{.*}}
87+
// CHECK: SubstNonTypeTemplateParmExpr {{.*}}
88+
// CHECK-NEXT: NonTypeTemplateParmDecl {{.*}}
89+
// CHECK-NEXT: IntegerLiteral{{.*}}8{{$}}
90+
91+
// CHECK: FunctionDecl {{.*}} {{.*}} func5 'void ()'
92+
// CHECK: SYCLIntelESimdVectorizeAttr {{.*}}
93+
// CHECK-NEXT: ConstantExpr {{.*}} 'int'
94+
// CHECK-NEXT: value: Int 8
95+
// CHECK-NEXT: IntegerLiteral{{.*}}8{{$}}

0 commit comments

Comments
 (0)