Skip to content

Commit 9053642

Browse files
MrSidimsbader
authored andcommitted
[SYCL][FPGA] Implement kernel attribute max_work_group_size (#883)
Applies to a device function/lambda function. Indicates the maximum dimensions of a work group. Values must be positive integers. This is similar to reqd_work_group_size, but allows work groups that are smaller or equal to the specified sizes. Signed-off-by: Dmitry Sidorov <[email protected]>
1 parent 9e2be76 commit 9053642

File tree

9 files changed

+193
-4
lines changed

9 files changed

+193
-4
lines changed

clang/include/clang/Basic/Attr.td

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1123,6 +1123,17 @@ def SYCLIntelNumSimdWorkItems : InheritableAttr {
11231123
let PragmaAttributeSupport = 0;
11241124
}
11251125

1126+
def SYCLIntelMaxWorkGroupSize : InheritableAttr {
1127+
let Spellings = [CXX11<"intelfpga","max_work_group_size">];
1128+
let Args = [UnsignedArgument<"XDim">,
1129+
UnsignedArgument<"YDim">,
1130+
UnsignedArgument<"ZDim">];
1131+
let LangOpts = [SYCLIsDevice, SYCLIsHost];
1132+
let Subjects = SubjectList<[Function], ErrorDiag>;
1133+
let Documentation = [SYCLIntelMaxWorkGroupSizeAttrDocs];
1134+
let PragmaAttributeSupport = 0;
1135+
}
1136+
11261137
def C11NoReturn : InheritableAttr {
11271138
let Spellings = [Keyword<"_Noreturn">];
11281139
let Subjects = SubjectList<[Function], ErrorDiag>;

clang/include/clang/Basic/AttrDocs.td

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1980,6 +1980,19 @@ device kernel, the attribute is ignored and it is not propagated to a kernel.
19801980
}];
19811981
}
19821982

1983+
def SYCLIntelMaxWorkGroupSizeAttrDocs : Documentation {
1984+
let Category = DocCatFunction;
1985+
let Heading = "max_work_group_size (IntelFPGA)";
1986+
let Content = [{
1987+
Applies to a device function/lambda function. Indicates the maximum dimensions
1988+
of a work group. Values must be positive integers. This is similar to
1989+
reqd_work_group_size, but allows work groups that are smaller or equal to the
1990+
specified sizes.
1991+
If ``intelfpga::max_work_group_size`` is applied to a function called from a
1992+
device kernel, the attribute is ignored and it is not propagated to a kernel.
1993+
}];
1994+
}
1995+
19831996
def SYCLFPGAPipeDocs : Documentation {
19841997
let Category = DocCatStmt;
19851998
let Heading = "pipe (read_only, write_only)";

clang/include/clang/Basic/AttributeCommonInfo.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,8 @@ class AttributeCommonInfo {
155155
auto ParsedAttr = getParsedKind();
156156
if (ParsedAttr == AT_SYCLIntelKernelArgsRestrict ||
157157
(ParsedAttr == AT_ReqdWorkGroupSize && isCXX11Attribute()) ||
158-
ParsedAttr == AT_SYCLIntelNumSimdWorkItems)
158+
ParsedAttr == AT_SYCLIntelNumSimdWorkItems ||
159+
ParsedAttr == AT_SYCLIntelMaxWorkGroupSize)
159160
return true;
160161

161162
return false;

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10155,6 +10155,9 @@ def err_sycl_non_std_layout_type : Error<
1015510155
"kernel parameter has non-standard layout class/struct type">;
1015610156
def err_conflicting_sycl_kernel_attributes : Error<
1015710157
"conflicting attributes applied to a SYCL kernel">;
10158+
def err_conflicting_sycl_function_attributes : Error<
10159+
"%0 attribute conflicts with '%1' attribute">;
10160+
1015810161
def err_sycl_attibute_cannot_be_applied_here
1015910162
: Error<"%0 attribute cannot be applied to a "
1016010163
"%select{static function or function in an anonymous namespace"

clang/lib/CodeGen/CodeGenFunction.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -595,6 +595,16 @@ void CodeGenFunction::EmitOpenCLKernelMetadata(const FunctionDecl *FD,
595595
Fn->setMetadata("num_simd_work_items",
596596
llvm::MDNode::get(Context, AttrMDArgs));
597597
}
598+
599+
if (const SYCLIntelMaxWorkGroupSizeAttr *A =
600+
FD->getAttr<SYCLIntelMaxWorkGroupSizeAttr>()) {
601+
llvm::Metadata *AttrMDArgs[] = {
602+
llvm::ConstantAsMetadata::get(Builder.getInt32(A->getXDim())),
603+
llvm::ConstantAsMetadata::get(Builder.getInt32(A->getYDim())),
604+
llvm::ConstantAsMetadata::get(Builder.getInt32(A->getZDim()))};
605+
Fn->setMetadata("max_work_group_size",
606+
llvm::MDNode::get(Context, AttrMDArgs));
607+
}
598608
}
599609

600610
/// Determine whether the function F ends with a return stmt.

clang/lib/Sema/SemaDeclAttr.cpp

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2856,9 +2856,41 @@ static void handleWeakImportAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
28562856
D->addAttr(::new (S.Context) WeakImportAttr(S.Context, AL));
28572857
}
28582858

2859-
// Handles reqd_work_group_size and work_group_size_hint.
2859+
// Checks correctness of mutual usage of different work_group_size attributes:
2860+
// reqd_work_group_size, max_work_group_size. Values of reqd_work_group_size
2861+
// arguments shall be equal or less than values coming from max_work_group_size.
2862+
static bool checkWorkGroupSizeValues(Sema &S, Decl *D, const ParsedAttr &Attr,
2863+
uint32_t WGSize[3]) {
2864+
if (const SYCLIntelMaxWorkGroupSizeAttr *A =
2865+
D->getAttr<SYCLIntelMaxWorkGroupSizeAttr>()) {
2866+
if (!(WGSize[0] <= A->getXDim() && WGSize[1] <= A->getYDim() &&
2867+
WGSize[2] <= A->getZDim())) {
2868+
S.Diag(Attr.getLoc(), diag::err_conflicting_sycl_function_attributes)
2869+
<< Attr << A->getSpelling();
2870+
D->setInvalidDecl();
2871+
return false;
2872+
}
2873+
}
2874+
2875+
if (const ReqdWorkGroupSizeAttr *A = D->getAttr<ReqdWorkGroupSizeAttr>()) {
2876+
if (!(WGSize[0] >= A->getXDim() && WGSize[1] >= A->getYDim() &&
2877+
WGSize[2] >= A->getZDim())) {
2878+
S.Diag(Attr.getLoc(), diag::err_conflicting_sycl_function_attributes)
2879+
<< Attr << A->getSpelling();
2880+
D->setInvalidDecl();
2881+
return false;
2882+
}
2883+
}
2884+
2885+
return true;
2886+
}
2887+
2888+
// Handles reqd_work_group_size, work_group_size_hint and max_work_group_size
28602889
template <typename WorkGroupAttr>
28612890
static void handleWorkGroupSize(Sema &S, Decl *D, const ParsedAttr &AL) {
2891+
if (D->isInvalidDecl())
2892+
return;
2893+
28622894
uint32_t WGSize[3];
28632895
for (unsigned i = 0; i < 3; ++i) {
28642896
const Expr *E = AL.getArgAsExpr(i);
@@ -2872,6 +2904,9 @@ static void handleWorkGroupSize(Sema &S, Decl *D, const ParsedAttr &AL) {
28722904
}
28732905
}
28742906

2907+
if (!checkWorkGroupSizeValues(S, D, AL, WGSize))
2908+
return;
2909+
28752910
WorkGroupAttr *Existing = D->getAttr<WorkGroupAttr>();
28762911
if (Existing && !(Existing->getXDim() == WGSize[0] &&
28772912
Existing->getYDim() == WGSize[1] &&
@@ -7442,6 +7477,9 @@ static void ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D,
74427477
case ParsedAttr::AT_ReqdWorkGroupSize:
74437478
handleWorkGroupSize<ReqdWorkGroupSizeAttr>(S, D, AL);
74447479
break;
7480+
case ParsedAttr::AT_SYCLIntelMaxWorkGroupSize:
7481+
handleWorkGroupSize<SYCLIntelMaxWorkGroupSizeAttr>(S, D, AL);
7482+
break;
74457483
case ParsedAttr::AT_IntelReqdSubGroupSize:
74467484
handleSubGroupSize(S, D, AL);
74477485
break;
@@ -7916,6 +7954,9 @@ void Sema::ProcessDeclAttributeList(Scope *S, Decl *D,
79167954
} else if (const auto *A = D->getAttr<WorkGroupSizeHintAttr>()) {
79177955
Diag(D->getLocation(), diag::err_opencl_kernel_attr) << A;
79187956
D->setInvalidDecl();
7957+
} else if (const auto *A = D->getAttr<SYCLIntelMaxWorkGroupSizeAttr>()) {
7958+
Diag(D->getLocation(), diag::err_opencl_kernel_attr) << A;
7959+
D->setInvalidDecl();
79197960
} else if (const auto *A = D->getAttr<VecTypeHintAttr>()) {
79207961
Diag(D->getLocation(), diag::err_opencl_kernel_attr) << A;
79217962
D->setInvalidDecl();

clang/lib/Sema/SemaSYCL.cpp

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -428,7 +428,6 @@ class MarkDeviceFunction : public RecursiveASTVisitor<MarkDeviceFunction> {
428428
Attrs.insert(A);
429429
if (auto *A = FD->getAttr<ReqdWorkGroupSizeAttr>())
430430
Attrs.insert(A);
431-
432431
// Allow the following kernel attributes only on lambda functions and
433432
// function objects that are called directly from a kernel (i.e. the one
434433
// passed to the parallel_for function). For all other cases,
@@ -449,6 +448,14 @@ class MarkDeviceFunction : public RecursiveASTVisitor<MarkDeviceFunction> {
449448
FD->dropAttr<SYCLIntelNumSimdWorkItemsAttr>();
450449
}
451450
}
451+
if (auto *A = FD->getAttr<SYCLIntelMaxWorkGroupSizeAttr>()) {
452+
if (ParentFD == SYCLKernel) {
453+
Attrs.insert(A);
454+
} else {
455+
SemaRef.Diag(A->getLocation(), diag::warn_attribute_ignored) << A;
456+
FD->dropAttr<SYCLIntelMaxWorkGroupSizeAttr>();
457+
}
458+
}
452459

453460
// TODO: vec_len_hint should be handled here
454461

@@ -1348,7 +1355,8 @@ void Sema::MarkDevice(void) {
13481355
break;
13491356
}
13501357
case attr::Kind::SYCLIntelKernelArgsRestrict:
1351-
case attr::Kind::SYCLIntelNumSimdWorkItems: {
1358+
case attr::Kind::SYCLIntelNumSimdWorkItems:
1359+
case attr::Kind::SYCLIntelMaxWorkGroupSize: {
13521360
SYCLKernel->addAttr(A);
13531361
break;
13541362
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// RUN: %clang_cc1 -std=c++11 -triple spir64-unknown-unknown-sycldevice -disable-llvm-passes -fsycl-is-device -emit-llvm -o - %s | FileCheck %s
2+
3+
class Foo {
4+
public:
5+
[[intelfpga::max_work_group_size(1, 1, 1)]] void operator()() {}
6+
};
7+
8+
template <typename name, typename Func>
9+
__attribute__((sycl_kernel)) void kernel(Func kernelFunc) {
10+
kernelFunc();
11+
}
12+
13+
void bar() {
14+
Foo boo;
15+
kernel<class kernel_name1>(boo);
16+
17+
kernel<class kernel_name2>(
18+
[]() [[intelfpga::max_work_group_size(8, 8, 8)]] {});
19+
}
20+
21+
// CHECK: define spir_kernel void @{{.*}}kernel_name1() {{.*}} !max_work_group_size ![[NUM1:[0-9]+]]
22+
// CHECK: define spir_kernel void @{{.*}}kernel_name2() {{.*}} !max_work_group_size ![[NUM8:[0-9]+]]
23+
// CHECK: ![[NUM1]] = !{i32 1, i32 1, i32 1}
24+
// CHECK: ![[NUM8]] = !{i32 8, i32 8, i32 8}
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
// RUN: %clang %s -fsyntax-only -fsycl-device-only -DTRIGGER_ERROR -Xclang -verify
2+
// RUN: %clang %s -fsyntax-only -Xclang -ast-dump -fsycl-device-only | FileCheck %s
3+
// RUN: %clang_cc1 -fsycl-is-host -fsyntax-only -verify %s
4+
5+
#ifndef __SYCL_DEVICE_ONLY__
6+
struct FuncObj {
7+
[[intelfpga::max_work_group_size(1, 1, 1)]] // expected-no-diagnostics
8+
void operator()() {}
9+
};
10+
11+
template <typename name, typename Func>
12+
void kernel(Func kernelFunc) {
13+
kernelFunc();
14+
}
15+
16+
void foo() {
17+
kernel<class test_kernel1>(
18+
FuncObj());
19+
}
20+
21+
#else // __SYCL_DEVICE_ONLY__
22+
23+
[[intelfpga::max_work_group_size(2, 2, 2)]] // expected-warning{{'max_work_group_size' attribute ignored}}
24+
void func_ignore() {}
25+
26+
struct FuncObj {
27+
[[intelfpga::max_work_group_size(4, 4, 4)]]
28+
void operator()() {}
29+
};
30+
31+
#ifdef TRIGGER_ERROR
32+
struct DAFuncObj {
33+
[[intelfpga::max_work_group_size(4, 4, 4)]]
34+
[[cl::reqd_work_group_size(8, 8, 4)]] // expected-error{{'reqd_work_group_size' attribute conflicts with 'max_work_group_size' attribute}}
35+
void operator()() {}
36+
};
37+
#endif // TRIGGER_ERROR
38+
39+
template <typename name, typename Func>
40+
__attribute__((sycl_kernel)) void kernel(Func kernelFunc) {
41+
kernelFunc();
42+
}
43+
44+
int main() {
45+
// CHECK-LABEL: FunctionDecl {{.*}} _ZTSZ4mainE12test_kernel1
46+
// CHECK: SYCLIntelMaxWorkGroupSizeAttr {{.*}} 4 4 4
47+
kernel<class test_kernel1>(
48+
FuncObj());
49+
50+
// CHECK-LABEL: FunctionDecl {{.*}} _ZTSZ4mainE12test_kernel2
51+
// CHECK: SYCLIntelMaxWorkGroupSizeAttr {{.*}} 8 8 8
52+
kernel<class test_kernel2>(
53+
[]() [[intelfpga::max_work_group_size(8, 8, 8)]] {});
54+
55+
// CHECK-LABEL: FunctionDecl {{.*}} _ZTSZ4mainE12test_kernel3
56+
// CHECK-NOT: SYCLIntelMaxWorkGroupSizeAttr {{.*}}
57+
kernel<class test_kernel3>(
58+
[]() {func_ignore();});
59+
60+
#ifdef TRIGGER_ERROR
61+
[[intelfpga::max_work_group_size(1, 1, 1)]] int Var = 0; // expected-error{{'max_work_group_size' attribute only applies to functions}}
62+
63+
kernel<class test_kernel4>(
64+
[]() [[intelfpga::max_work_group_size(0, 1, 3)]] {}); // expected-error{{'max_work_group_size' attribute must be greater than 0}}
65+
66+
kernel<class test_kernel5>(
67+
[]() [[intelfpga::max_work_group_size(-8, 8, 1)]] {}); // expected-error{{'max_work_group_size' attribute requires a non-negative integral compile time constant expression}}
68+
69+
kernel<class test_kernel6>(
70+
[]() [[intelfpga::max_work_group_size(16, 16, 16),
71+
intelfpga::max_work_group_size(2, 2, 2)]] {}); // expected-warning{{attribute 'max_work_group_size' is already applied with different parameters}}
72+
73+
kernel<class test_kernel7>(
74+
DAFuncObj());
75+
76+
#endif // TRIGGER_ERROR
77+
}
78+
#endif // __SYCL_DEVICE_ONLY__

0 commit comments

Comments
 (0)