Skip to content

[SYCL][FPGA] Implement num_simd_work_items kernel attribute #871

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 1 commit into from
Dec 5, 2019
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
9 changes: 9 additions & 0 deletions clang/include/clang/Basic/Attr.td
Original file line number Diff line number Diff line change
Expand Up @@ -1109,6 +1109,15 @@ def SYCLIntelKernelArgsRestrict : InheritableAttr {
let Documentation = [ SYCLIntelKernelArgsRestrictDocs ];
}

def SYCLIntelNumSimdWorkItems : InheritableAttr {
let Spellings = [CXX11<"intelfpga","num_simd_work_items">];
let Args = [UnsignedArgument<"Number">];
let LangOpts = [SYCLIsDevice, SYCLIsHost];
let Subjects = SubjectList<[Function], ErrorDiag>;
let Documentation = [SYCLIntelNumSimdWorkItemsAttrDocs];
let PragmaAttributeSupport = 0;
}

def C11NoReturn : InheritableAttr {
let Spellings = [Keyword<"_Noreturn">];
let Subjects = SubjectList<[Function], ErrorDiag>;
Expand Down
11 changes: 11 additions & 0 deletions clang/include/clang/Basic/AttrDocs.td
Original file line number Diff line number Diff line change
Expand Up @@ -1885,6 +1885,17 @@ function, and no effect otherwise.
}];
}

def SYCLIntelNumSimdWorkItemsAttrDocs : Documentation {
let Category = DocCatFunction;
let Heading = "num_simd_work_items (IntelFPGA)";
let Content = [{
Applies to a device function/lambda function. Indicates the number of work
items that should be processed in parallel. Valid values are positive integers.
If ``intelfpga::num_simd_work_items`` is applied to a function called from a
device kernel, the attribute is ignored and it is not propagated to a kernel.
}];
}

def SYCLFPGAPipeDocs : Documentation {
let Category = DocCatStmt;
let Heading = "pipe (read_only, write_only)";
Expand Down
3 changes: 2 additions & 1 deletion clang/include/clang/Basic/AttributeCommonInfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,8 @@ class AttributeCommonInfo {
// to the call operator declaration.
auto ParsedAttr = getParsedKind();
if (ParsedAttr == AT_SYCLIntelKernelArgsRestrict ||
(ParsedAttr == AT_ReqdWorkGroupSize && isCXX11Attribute()))
(ParsedAttr == AT_ReqdWorkGroupSize && isCXX11Attribute()) ||
ParsedAttr == AT_SYCLIntelNumSimdWorkItems)
return true;

return false;
Expand Down
8 changes: 8 additions & 0 deletions clang/lib/CodeGen/CodeGenFunction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -587,6 +587,14 @@ void CodeGenFunction::EmitOpenCLKernelMetadata(const FunctionDecl *FD,
Fn->setMetadata("intel_reqd_sub_group_size",
llvm::MDNode::get(Context, AttrMDArgs));
}

if (const SYCLIntelNumSimdWorkItemsAttr *A =
FD->getAttr<SYCLIntelNumSimdWorkItemsAttr>()) {
llvm::Metadata *AttrMDArgs[] = {
llvm::ConstantAsMetadata::get(Builder.getInt32(A->getNumber()))};
Fn->setMetadata("num_simd_work_items",
llvm::MDNode::get(Context, AttrMDArgs));
}
}

/// Determine whether the function F ends with a return stmt.
Expand Down
28 changes: 28 additions & 0 deletions clang/lib/Sema/SemaDeclAttr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2903,6 +2903,31 @@ static void handleSubGroupSize(Sema &S, Decl *D, const ParsedAttr &AL) {
IntelReqdSubGroupSizeAttr(S.Context, AL, SGSize));
}

// Handles num_simd_work_items.
static void handleNumSimdWorkItemsAttr(Sema &S, Decl *D,
const ParsedAttr &Attr) {
if (D->isInvalidDecl())
return;

uint32_t NumSimdWorkItems = 0;
const Expr *E = Attr.getArgAsExpr(0);
if (!checkUInt32Argument(S, Attr, E, NumSimdWorkItems, 0,
/*StrictlyUnsigned=*/true))
return;

if (NumSimdWorkItems == 0) {
S.Diag(Attr.getLoc(), diag::err_attribute_argument_is_zero)
<< Attr << E->getSourceRange();
return;
}

if (D->getAttr<SYCLIntelNumSimdWorkItemsAttr>())
S.Diag(Attr.getLoc(), diag::warn_duplicate_attribute) << Attr;

D->addAttr(::new (S.Context) SYCLIntelNumSimdWorkItemsAttr(
S.Context, Attr, NumSimdWorkItems));
}

static void handleVecTypeHint(Sema &S, Decl *D, const ParsedAttr &AL) {
if (!AL.hasParsedType()) {
S.Diag(AL.getLoc(), diag::err_attribute_wrong_number_arguments) << AL << 1;
Expand Down Expand Up @@ -7286,6 +7311,9 @@ static void ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D,
case ParsedAttr::AT_IntelReqdSubGroupSize:
handleSubGroupSize(S, D, AL);
break;
case ParsedAttr::AT_SYCLIntelNumSimdWorkItems:
handleNumSimdWorkItemsAttr(S, D, AL);
break;
case ParsedAttr::AT_VecTypeHint:
handleVecTypeHint(S, D, AL);
break;
Expand Down
20 changes: 15 additions & 5 deletions clang/lib/Sema/SemaSYCL.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -428,18 +428,27 @@ class MarkDeviceFunction : public RecursiveASTVisitor<MarkDeviceFunction> {
Attrs.insert(A);
if (auto *A = FD->getAttr<ReqdWorkGroupSizeAttr>())
Attrs.insert(A);

// Allow the following kernel attributes only on lambda functions and
// function objects that are called directly from a kernel (i.e. the one
// passed to the parallel_for function). For all other cases,
// emit a warning and ignore.
if (auto *A = FD->getAttr<SYCLIntelKernelArgsRestrictAttr>()) {
// Allow the intel::kernel_args_restrict only on the lambda (function
// object) function, that is called directly from a kernel (i.e. the one
// passed to the parallel_for function). Emit a warning and ignore all
// other cases.
if (ParentFD == SYCLKernel) {
Attrs.insert(A);
} else {
SemaRef.Diag(A->getLocation(), diag::warn_attribute_ignored) << A;
FD->dropAttr<SYCLIntelKernelArgsRestrictAttr>();
}
}
if (auto *A = FD->getAttr<SYCLIntelNumSimdWorkItemsAttr>()) {
if (ParentFD == SYCLKernel) {
Attrs.insert(A);
} else {
SemaRef.Diag(A->getLocation(), diag::warn_attribute_ignored) << A;
FD->dropAttr<SYCLIntelNumSimdWorkItemsAttr>();
}
}

// TODO: vec_len_hint should be handled here

Expand Down Expand Up @@ -1338,7 +1347,8 @@ void Sema::MarkDevice(void) {
}
break;
}
case attr::Kind::SYCLIntelKernelArgsRestrict: {
case attr::Kind::SYCLIntelKernelArgsRestrict:
case attr::Kind::SYCLIntelNumSimdWorkItems: {
SYCLKernel->addAttr(A);
break;
}
Expand Down
26 changes: 26 additions & 0 deletions clang/test/CodeGenSYCL/num-simd-work-items.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// RUN: %clang_cc1 -std=c++11 -triple spir64-unknown-unknown-sycldevice -disable-llvm-passes -fsycl-is-device -emit-llvm -o - %s | FileCheck %s

class Foo {
public:
[[intelfpga::num_simd_work_items(1)]] void operator()() {}
};

template <typename name, typename Func>
__attribute__((sycl_kernel)) void kernel(Func kernelFunc) {
kernelFunc();
}

void bar() {
Foo boo;
kernel<class kernel_name1>(boo);

kernel<class kernel_name2>(
[]() [[intelfpga::num_simd_work_items(42)]] {});

}

// CHECK: define spir_kernel void @{{.*}}kernel_name1() {{.*}} !num_simd_work_items ![[NUM1:[0-9]+]]
// CHECK: define spir_kernel void @{{.*}}kernel_name2() {{.*}} !num_simd_work_items ![[NUM42:[0-9]+]]
// CHECK: ![[NUM1]] = !{i32 1}
// CHECK: ![[NUM42]] = !{i32 42}

65 changes: 65 additions & 0 deletions clang/test/SemaSYCL/num_simd_work_items.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
// RUN: %clang %s -fsyntax-only -fsycl-device-only -DTRIGGER_ERROR -Xclang -verify
// RUN: %clang %s -fsyntax-only -Xclang -ast-dump -fsycl-device-only | FileCheck %s
// RUN: %clang_cc1 -fsycl-is-host -fsyntax-only -verify %s

#ifndef __SYCL_DEVICE_ONLY__
struct FuncObj {
[[intelfpga::num_simd_work_items(42)]] // expected-no-diagnostics
void operator()() {}
};

template <typename name, typename Func>
void kernel(Func kernelFunc) {
kernelFunc();
}

void foo() {
kernel<class test_kernel1>(
FuncObj());
}

#else // __SYCL_DEVICE_ONLY__

[[intelfpga::num_simd_work_items(2)]] // expected-warning{{'num_simd_work_items' attribute ignored}}
void func_ignore() {}

struct FuncObj {
[[intelfpga::num_simd_work_items(42)]]
void operator()() {}
};

template <typename name, typename Func>
__attribute__((sycl_kernel)) void kernel(Func kernelFunc) {
kernelFunc();
}

int main() {
// CHECK-LABEL: FunctionDecl {{.*}} _ZTSZ4mainE12test_kernel1
// CHECK: SYCLIntelNumSimdWorkItemsAttr {{.*}} 42
kernel<class test_kernel1>(
FuncObj());

// CHECK-LABEL: FunctionDecl {{.*}} _ZTSZ4mainE12test_kernel2
// CHECK: SYCLIntelNumSimdWorkItemsAttr {{.*}} 8
kernel<class test_kernel2>(
[]() [[intelfpga::num_simd_work_items(8)]] {});

// CHECK-LABEL: FunctionDecl {{.*}} _ZTSZ4mainE12test_kernel3
// CHECK-NOT: SYCLIntelNumSimdWorkItemsAttr {{.*}} 2
kernel<class test_kernel3>(
[]() {func_ignore();});

#ifdef TRIGGER_ERROR
[[intelfpga::num_simd_work_items(0)]] int Var = 0; // expected-error{{'num_simd_work_items' attribute only applies to functions}}

kernel<class test_kernel4>(
[]() [[intelfpga::num_simd_work_items(0)]] {}); // expected-error{{'num_simd_work_items' attribute must be greater than 0}}

kernel<class test_kernel5>(
[]() [[intelfpga::num_simd_work_items(-42)]] {}); // expected-error{{'num_simd_work_items' attribute requires a non-negative integral compile time constant expression}}

kernel<class test_kernel6>(
[]() [[intelfpga::num_simd_work_items(1), intelfpga::num_simd_work_items(2)]] {}); // expected-warning{{attribute 'num_simd_work_items' is already applied with different parameters}}
#endif // TRIGGER_ERROR
}
#endif // __SYCL_DEVICE_ONLY__