Skip to content

[SYCL][FPGA] Add support for two new function attributes #3441

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 10 commits into from
Mar 31, 2021
Merged
Show file tree
Hide file tree
Changes from 6 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
16 changes: 10 additions & 6 deletions clang/include/clang/Basic/Attr.td
Original file line number Diff line number Diff line change
Expand Up @@ -1878,16 +1878,18 @@ def SYCLIntelFPGAIVDep : StmtAttr {
let Documentation = [SYCLIntelFPGAIVDepAttrDocs];
}

def SYCLIntelFPGAInitiationInterval : StmtAttr {
def SYCLIntelFPGAInitiationInterval : DeclOrStmtAttr {
let Spellings = [CXX11<"intelfpga","ii">,
CXX11<"intel","ii">,
CXX11<"intel", "initiation_interval">];
let Subjects = SubjectList<[ForStmt, CXXForRangeStmt, WhileStmt, DoStmt],
ErrorDiag, "'for', 'while', and 'do' statements">;
let Subjects = SubjectList<[ForStmt, CXXForRangeStmt, WhileStmt, DoStmt, Function],
ErrorDiag,
"'for', 'while', 'do' statements, and functions">;
let Args = [ExprArgument<"IntervalExpr", /*opt*/1>];
let LangOpts = [SYCLIsDevice, SilentlyIgnoreSYCLIsHost];
let HasCustomTypeTransform = 1;
let Documentation = [SYCLIntelFPGAInitiationIntervalAttrDocs];
let SupportsNonconformingLambdaSyntax = 1;
Copy link
Contributor Author

@smanna12 smanna12 Mar 30, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i checked with feature request author, the attribute needs to apply in this position.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for checking with the authors on these! Just to check -- when you ask their opinion, are you letting them know that a "yes" answer means their attribute isn't valid C++ code? (I have a suspicion that not everyone is thinking about the language design of these attributes so much as what immediate problem they think they're solving. Being asked if they want to add something that's not valid C++ may help them think a bit deeper about the problem.)

}

def SYCLIntelFPGAMaxConcurrency : StmtAttr {
Expand All @@ -1912,14 +1914,16 @@ def SYCLIntelFPGALoopCoalesce : StmtAttr {
let Documentation = [SYCLIntelFPGALoopCoalesceAttrDocs];
}

def SYCLIntelFPGADisableLoopPipelining : StmtAttr {
def SYCLIntelFPGADisableLoopPipelining : DeclOrStmtAttr {
let Spellings = [CXX11<"intelfpga","disable_loop_pipelining">,
CXX11<"intel","disable_loop_pipelining">];
let Subjects = SubjectList<[ForStmt, CXXForRangeStmt, WhileStmt, DoStmt],
ErrorDiag, "'for', 'while', and 'do' statements">;
let Subjects = SubjectList<[ForStmt, CXXForRangeStmt, WhileStmt, DoStmt, Function],
ErrorDiag,
"'for', 'while', 'do' statements, and functions">;
let LangOpts = [SYCLIsDevice, SilentlyIgnoreSYCLIsHost];
let HasCustomTypeTransform = 1;
let Documentation = [SYCLIntelFPGADisableLoopPipeliningAttrDocs];
let SupportsNonconformingLambdaSyntax = 1;
}

def SYCLIntelFPGAMaxInterleaving : StmtAttr {
Expand Down
20 changes: 13 additions & 7 deletions clang/include/clang/Basic/AttrDocs.td
Original file line number Diff line number Diff line change
Expand Up @@ -2832,9 +2832,9 @@ def SYCLIntelFPGAInitiationIntervalAttrDocs : Documentation {
let Category = DocCatVariable;
let Heading = "intel::initiation_interval";
let Content = [{
This attribute applies to a loop. Indicates that the loop should be pipelined
with an initiation interval of N. N must be a positive integer. Cannot be
applied multiple times to the same loop.
This attribute applies to a loop or a function. Indicates that the loop or
function should be pipelined with an initiation interval of N. N must be a
positive integer. Cannot be applied multiple times to the same loop or function.

The ``[[intel::ii]]`` attribute spelling is a deprecated synonym for
``[[intel::initiation_interval]]`` and will be removed in the future.
Expand All @@ -2846,10 +2846,13 @@ The ``[[intel::ii]]`` attribute spelling is a deprecated synonym for
[[intel::initiation_interval(4)]] for (int i = 0; i < 10; ++i) var++;
}

[[intel::initiation_interval(4)]] void foo1 { }

template<int N>
void bar() {
[[intel::initiation_interval(N)]] for(;;) { }
}
[[intel::initiation_interval(N)]] void bar1 { }

}];
}
Expand Down Expand Up @@ -2921,10 +2924,11 @@ def SYCLIntelFPGADisableLoopPipeliningAttrDocs : Documentation {
let Category = DocCatVariable;
let Heading = "intel::disable_loop_pipelining";
let Content = [{
This attribute applies to a loop. Disables pipelining of the loop data path,
causing the loop to be executed serially. Cannot be used on the same loop in
conjunction with max_interleaving, speculated_iterations, max_concurrency, ii
or ivdep.
This attribute applies to a loop or a function. Takes no arguments and
disables pipelining of the loop or function data path, causing the loop
or function to be executed serially. Cannot be used on the same loop or
function in conjunction with max_interleaving, speculated_iterations,
max_concurrency, initiation_interval, or ivdep.

.. code-block:: c++

Expand All @@ -2933,6 +2937,8 @@ or ivdep.
[[intel::disable_loop_pipelining] for (int i = 0; i < 10; ++i) var++;
}

[[intel::disable_loop_pipelining] void foo1() { }

}];
}

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 @@ -10263,6 +10263,11 @@ class Sema final {
IntelFPGAForcePow2DepthAttr *
MergeIntelFPGAForcePow2DepthAttr(Decl *D,
const IntelFPGAForcePow2DepthAttr &A);
void AddSYCLIntelFPGAInitiationIntervalAttr(Decl *D,
const AttributeCommonInfo &CI,
Expr *E);
SYCLIntelFPGAInitiationIntervalAttr *MergeSYCLIntelFPGAInitiationIntervalAttr(
Decl *D, const SYCLIntelFPGAInitiationIntervalAttr &A);

/// AddAlignedAttr - Adds an aligned attribute to a particular declaration.
void AddAlignedAttr(Decl *D, const AttributeCommonInfo &CI, Expr *E,
Expand Down
16 changes: 16 additions & 0 deletions clang/lib/CodeGen/CodeGenFunction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -738,6 +738,22 @@ void CodeGenFunction::EmitOpenCLKernelMetadata(const FunctionDecl *FD,
llvm::ConstantAsMetadata::get(Builder.getInt32(1))};
Fn->setMetadata("stall_enable", llvm::MDNode::get(Context, AttrMDArgs));
}

if (FD->hasAttr<SYCLIntelFPGADisableLoopPipeliningAttr>()) {
llvm::Metadata *AttrMDArgs[] = {
llvm::ConstantAsMetadata::get(Builder.getInt32(1))};
Fn->setMetadata("disable_loop_pipelining",
llvm::MDNode::get(Context, AttrMDArgs));
}

if (const auto *A = FD->getAttr<SYCLIntelFPGAInitiationIntervalAttr>()) {
const auto *CE = cast<ConstantExpr>(A->getIntervalExpr());
llvm::APSInt ArgVal = CE->getResultAsAPSInt();
llvm::Metadata *AttrMDArgs[] = {
llvm::ConstantAsMetadata::get(Builder.getInt32(ArgVal.getSExtValue()))};
Fn->setMetadata("initiation_interval",
llvm::MDNode::get(Context, AttrMDArgs));
}
}

/// Determine whether the function F ends with a return stmt.
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 @@ -2630,6 +2630,8 @@ static bool mergeDeclAttribute(Sema &S, NamedDecl *D,
NewAttr = S.MergeIntelFPGAMaxReplicatesAttr(D, *A);
else if (const auto *A = dyn_cast<IntelFPGAForcePow2DepthAttr>(Attr))
NewAttr = S.MergeIntelFPGAForcePow2DepthAttr(D, *A);
else if (const auto *A = dyn_cast<SYCLIntelFPGAInitiationIntervalAttr>(Attr))
NewAttr = S.MergeSYCLIntelFPGAInitiationIntervalAttr(D, *A);
else if (Attr->shouldInheritEvenIfAlreadyPresent() || !DeclHasAttr(D, Attr))
NewAttr = cast<InheritableAttr>(Attr->clone(S.Context));

Expand Down
105 changes: 105 additions & 0 deletions clang/lib/Sema/SemaDeclAttr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3330,6 +3330,105 @@ static void handleUseStallEnableClustersAttr(Sema &S, Decl *D,
handleSimpleAttribute<SYCLIntelUseStallEnableClustersAttr>(S, D, Attr);
}

// Handles disable_loop_pipelining attribute.
static void handleSYCLIntelFPGADisableLoopPipeliningAttr(Sema &S, Decl *D,
const ParsedAttr &A) {
S.CheckDeprecatedSYCLAttributeSpelling(A);

// [[intel::disable_loop_pipelining] and [[intel::initiation_interval()]]
// attributes are incompatible.
if (checkAttrMutualExclusion<SYCLIntelFPGAInitiationIntervalAttr>(S, D, A))
return;

D->addAttr(::new (S.Context)
SYCLIntelFPGADisableLoopPipeliningAttr(S.Context, A));
}

// Handles initiation_interval attribute.
void Sema::AddSYCLIntelFPGAInitiationIntervalAttr(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();
// This attribute requires a strictly positive value.
if (ArgVal <= 0) {
Diag(E->getExprLoc(), diag::err_attribute_requires_positive_integer)
<< CI << /*positive*/ 0;
return;
}
// Check to see if there's a duplicate attribute with different values
// already applied to the declaration.
if (const auto *DeclAttr =
D->getAttr<SYCLIntelFPGAInitiationIntervalAttr>()) {
// 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->getIntervalExpr())) {
if (ArgVal != DeclExpr->getResultAsAPSInt()) {
Diag(CI.getLoc(), diag::warn_duplicate_attribute) << CI;
Diag(DeclAttr->getLoc(), diag::note_previous_attribute);
}
// Drop the duplicate attribute.
return;
}
}
}

// [[intel::disable_loop_pipelining] and [[intel::initiation_interval()]]
// attributes are incompatible.
if (checkAttrMutualExclusion<SYCLIntelFPGADisableLoopPipeliningAttr>(*this, D,
CI))
return;

D->addAttr(::new (Context)
SYCLIntelFPGAInitiationIntervalAttr(Context, CI, E));
}

SYCLIntelFPGAInitiationIntervalAttr *
Sema::MergeSYCLIntelFPGAInitiationIntervalAttr(
Decl *D, const SYCLIntelFPGAInitiationIntervalAttr &A) {
// Check to see if there's a duplicate attribute with different values
// already applied to the declaration.
if (const auto *DeclAttr =
D->getAttr<SYCLIntelFPGAInitiationIntervalAttr>()) {
if (const auto *DeclExpr =
dyn_cast<ConstantExpr>(DeclAttr->getIntervalExpr())) {
if (const auto *MergeExpr = dyn_cast<ConstantExpr>(A.getIntervalExpr())) {
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;
}
}
}

// [[intel::initiation_interval()]] and [[intel::disable_loop_pipelining]
// attributes are incompatible.
if (checkAttrMutualExclusion<SYCLIntelFPGADisableLoopPipeliningAttr>(*this, D,
A))
return nullptr;

return ::new (Context)
SYCLIntelFPGAInitiationIntervalAttr(Context, A, A.getIntervalExpr());
}

static void handleSYCLIntelFPGAInitiationIntervalAttr(Sema &S, Decl *D,
const ParsedAttr &A) {
S.CheckDeprecatedSYCLAttributeSpelling(A);

S.AddSYCLIntelFPGAInitiationIntervalAttr(D, A, A.getArgAsExpr(0));
}

// Handle scheduler_target_fmax_mhz
void Sema::AddSYCLIntelSchedulerTargetFmaxMhzAttr(Decl *D,
const AttributeCommonInfo &CI,
Expand Down Expand Up @@ -9275,6 +9374,12 @@ static void ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D,
case ParsedAttr::AT_SYCLIntelLoopFuse:
handleSYCLIntelLoopFuseAttr(S, D, AL);
break;
case ParsedAttr::AT_SYCLIntelFPGADisableLoopPipelining:
handleSYCLIntelFPGADisableLoopPipeliningAttr(S, D, AL);
break;
case ParsedAttr::AT_SYCLIntelFPGAInitiationInterval:
handleSYCLIntelFPGAInitiationIntervalAttr(S, D, AL);
break;
case ParsedAttr::AT_VecTypeHint:
handleVecTypeHint(S, D, AL);
break;
Expand Down
20 changes: 20 additions & 0 deletions clang/lib/Sema/SemaSYCL.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -565,6 +565,24 @@ class MarkDeviceFunction : public RecursiveASTVisitor<MarkDeviceFunction> {
}
}

// Attribute "disable_loop_pipelining" can be applied explicitly on
// kernel function. Attribute should not be propagated from device
// functions to kernel.
if (auto *A = FD->getAttr<SYCLIntelFPGADisableLoopPipeliningAttr>()) {
if (ParentFD == SYCLKernel) {
Attrs.push_back(A);
}
}

// Attribute "initiation_interval" can be applied explicitly on
// kernel function. Attribute should not be propagated from device
// functions to kernel.
if (auto *A = FD->getAttr<SYCLIntelFPGAInitiationIntervalAttr>()) {
if (ParentFD == SYCLKernel) {
Attrs.push_back(A);
}
}

// TODO: vec_len_hint should be handled here

CallGraphNode *N = SYCLCG.getNode(FD);
Expand Down Expand Up @@ -3517,6 +3535,8 @@ void Sema::MarkDevice(void) {
case attr::Kind::SYCLIntelNoGlobalWorkOffset:
case attr::Kind::SYCLIntelUseStallEnableClusters:
case attr::Kind::SYCLIntelLoopFuse:
case attr::Kind::SYCLIntelFPGADisableLoopPipelining:
case attr::Kind::SYCLIntelFPGAInitiationInterval:
case attr::Kind::SYCLSimd: {
if ((A->getKind() == attr::Kind::SYCLSimd) && KernelBody &&
!KernelBody->getAttr<SYCLSimdAttr>()) {
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 @@ -713,6 +713,16 @@ static void instantiateIntelFPGAMaxReplicatesAttr(
S.AddIntelFPGAMaxReplicatesAttr(New, *A, Result.getAs<Expr>());
}

static void instantiateSYCLIntelFPGAInitiationIntervalAttr(
Sema &S, const MultiLevelTemplateArgumentList &TemplateArgs,
const SYCLIntelFPGAInitiationIntervalAttr *A, Decl *New) {
EnterExpressionEvaluationContext Unevaluated(
S, Sema::ExpressionEvaluationContext::ConstantEvaluated);
ExprResult Result = S.SubstExpr(A->getIntervalExpr(), TemplateArgs);
if (!Result.isInvalid())
S.AddSYCLIntelFPGAInitiationIntervalAttr(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 @@ -939,6 +949,12 @@ void Sema::InstantiateAttrs(const MultiLevelTemplateArgumentList &TemplateArgs,
*this, TemplateArgs, SYCLIntelMaxWorkGroupSize, New);
continue;
}
if (const auto *SYCLIntelFPGAInitiationInterval =
dyn_cast<SYCLIntelFPGAInitiationIntervalAttr>(TmplAttr)) {
instantiateSYCLIntelFPGAInitiationIntervalAttr(
*this, TemplateArgs, SYCLIntelFPGAInitiationInterval, New);
continue;
}
// Existing DLL attribute on the instantiation takes precedence.
if (TmplAttr->getKind() == attr::DLLExport ||
TmplAttr->getKind() == attr::DLLImport) {
Expand Down
36 changes: 36 additions & 0 deletions clang/test/CodeGenSYCL/disable_loop_pipelining.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// RUN: %clang_cc1 -fsycl-is-device -internal-isystem %S/Inputs -triple spir64-unknown-unknown-sycldevice -disable-llvm-passes -sycl-std=2020 -emit-llvm -o - %s | FileCheck %s

#include "sycl.hpp"

using namespace cl::sycl;
queue q;

class Foo {
public:
[[intel::disable_loop_pipelining]] void operator()() const {}
};

[[intel::disable_loop_pipelining]] void foo() {}

int main() {
q.submit([&](handler &h) {
// Test attribute is presented on function metadata.
Foo f;
h.single_task<class test_kernel1>(f);

// Test attribute is not propagated.
h.single_task<class test_kernel2>(
[]() { foo(); });

// Test attribute is applied on lambda.
h.single_task<class test_kernel3>(
[]() [[intel::disable_loop_pipelining]]{});
});
return 0;
}

// CHECK: define dso_local spir_kernel void @"{{.*}}test_kernel1"() #0 !kernel_arg_buffer_location ![[NUM4:[0-9]+]] !disable_loop_pipelining ![[NUM5:[0-9]+]]
// CHECK: define {{.*}}spir_kernel void @"{{.*}}test_kernel2"() #0 !kernel_arg_buffer_location ![[NUM4]]
// CHECK: define dso_local spir_kernel void @"{{.*}}test_kernel3"() #0 !kernel_arg_buffer_location ![[NUM4]] !disable_loop_pipelining ![[NUM5]]
// CHECK: ![[NUM4]] = !{}
// CHECK: ![[NUM5]] = !{i32 1}
Loading