Skip to content

Commit 4c6ea6e

Browse files
authored
[SYCL][FPGA] Add support for two new function attributes (#3441)
This patch implements the support for two new FPGA function attributes: 1. disable_loop_pipelining 2. initiation_interval Both attributes alreday exist for loops as statement attributes. Function attribute "initiation_interval" takes a single unsigned integer argument. Syntax: [[intel::initiation_interval(n)]] void foo() {} The argument can be constexpr. The parameter n is a template parameter. The LLVM IR representation will be function metadata: !initiation_interval !0 !0 = !{!i32 n} Function attribute "disable_loop_pipelining" takes no arguments. Syntax: [[intel::disable_loop_pipelining]] void foo() {} The LLVM IR representation will be function metadata: !disable_loop_pipelining !0 !0 = !{!i32 1} Both attributes are only valid in device code and do not get propagated to the caller. The function attributes apply to a lambda function, or function definition. Signed-off-by: Soumi Manna <[email protected]>
1 parent 3473c1a commit 4c6ea6e

15 files changed

+493
-15
lines changed

clang/include/clang/Basic/Attr.td

+10-6
Original file line numberDiff line numberDiff line change
@@ -1878,16 +1878,18 @@ def SYCLIntelFPGAIVDep : StmtAttr {
18781878
let Documentation = [SYCLIntelFPGAIVDepAttrDocs];
18791879
}
18801880

1881-
def SYCLIntelFPGAInitiationInterval : StmtAttr {
1881+
def SYCLIntelFPGAInitiationInterval : DeclOrStmtAttr {
18821882
let Spellings = [CXX11<"intelfpga","ii">,
18831883
CXX11<"intel","ii">,
18841884
CXX11<"intel", "initiation_interval">];
1885-
let Subjects = SubjectList<[ForStmt, CXXForRangeStmt, WhileStmt, DoStmt],
1886-
ErrorDiag, "'for', 'while', and 'do' statements">;
1885+
let Subjects = SubjectList<[ForStmt, CXXForRangeStmt, WhileStmt, DoStmt, Function],
1886+
ErrorDiag,
1887+
"'for', 'while', 'do' statements, and functions">;
18871888
let Args = [ExprArgument<"IntervalExpr", /*opt*/1>];
18881889
let LangOpts = [SYCLIsDevice, SilentlyIgnoreSYCLIsHost];
18891890
let HasCustomTypeTransform = 1;
18901891
let Documentation = [SYCLIntelFPGAInitiationIntervalAttrDocs];
1892+
let SupportsNonconformingLambdaSyntax = 1;
18911893
}
18921894

18931895
def SYCLIntelFPGAMaxConcurrency : StmtAttr {
@@ -1912,14 +1914,16 @@ def SYCLIntelFPGALoopCoalesce : StmtAttr {
19121914
let Documentation = [SYCLIntelFPGALoopCoalesceAttrDocs];
19131915
}
19141916

1915-
def SYCLIntelFPGADisableLoopPipelining : StmtAttr {
1917+
def SYCLIntelFPGADisableLoopPipelining : DeclOrStmtAttr {
19161918
let Spellings = [CXX11<"intelfpga","disable_loop_pipelining">,
19171919
CXX11<"intel","disable_loop_pipelining">];
1918-
let Subjects = SubjectList<[ForStmt, CXXForRangeStmt, WhileStmt, DoStmt],
1919-
ErrorDiag, "'for', 'while', and 'do' statements">;
1920+
let Subjects = SubjectList<[ForStmt, CXXForRangeStmt, WhileStmt, DoStmt, Function],
1921+
ErrorDiag,
1922+
"'for', 'while', 'do' statements, and functions">;
19201923
let LangOpts = [SYCLIsDevice, SilentlyIgnoreSYCLIsHost];
19211924
let HasCustomTypeTransform = 1;
19221925
let Documentation = [SYCLIntelFPGADisableLoopPipeliningAttrDocs];
1926+
let SupportsNonconformingLambdaSyntax = 1;
19231927
}
19241928

19251929
def SYCLIntelFPGAMaxInterleaving : StmtAttr {

clang/include/clang/Basic/AttrDocs.td

+17-7
Original file line numberDiff line numberDiff line change
@@ -2832,9 +2832,11 @@ def SYCLIntelFPGAInitiationIntervalAttrDocs : Documentation {
28322832
let Category = DocCatVariable;
28332833
let Heading = "intel::initiation_interval";
28342834
let Content = [{
2835-
This attribute applies to a loop. Indicates that the loop should be pipelined
2836-
with an initiation interval of N. N must be a positive integer. Cannot be
2837-
applied multiple times to the same loop.
2835+
This attribute applies to a loop or a function. Indicates that the loop or
2836+
function should be pipelined with an initiation interval of N. N must be a
2837+
positive integer. Cannot be applied multiple times to the same loop or function.
2838+
Cannot be used on the same loop or function in conjunction with
2839+
disable_loop_pipelining.
28382840

28392841
The ``[[intel::ii]]`` attribute spelling is a deprecated synonym for
28402842
``[[intel::initiation_interval]]`` and will be removed in the future.
@@ -2846,11 +2848,16 @@ The ``[[intel::ii]]`` attribute spelling is a deprecated synonym for
28462848
[[intel::initiation_interval(4)]] for (int i = 0; i < 10; ++i) var++;
28472849
}
28482850

2851+
[[intel::initiation_interval(4)]] void foo1 { }
2852+
28492853
template<int N>
28502854
void bar() {
28512855
[[intel::initiation_interval(N)]] for(;;) { }
28522856
}
28532857

2858+
template<int N>
2859+
[[intel::initiation_interval(N)]] void bar1 { }
2860+
28542861
}];
28552862
}
28562863

@@ -2921,10 +2928,11 @@ def SYCLIntelFPGADisableLoopPipeliningAttrDocs : Documentation {
29212928
let Category = DocCatVariable;
29222929
let Heading = "intel::disable_loop_pipelining";
29232930
let Content = [{
2924-
This attribute applies to a loop. Disables pipelining of the loop data path,
2925-
causing the loop to be executed serially. Cannot be used on the same loop in
2926-
conjunction with max_interleaving, speculated_iterations, max_concurrency, ii
2927-
or ivdep.
2931+
This attribute applies to a loop or a function. Takes no arguments and
2932+
disables pipelining of the loop or function data path, causing the loop
2933+
or function to be executed serially. Cannot be used on the same loop or
2934+
function in conjunction with max_interleaving, speculated_iterations,
2935+
max_concurrency, initiation_interval, or ivdep.
29282936

29292937
.. code-block:: c++
29302938

@@ -2933,6 +2941,8 @@ or ivdep.
29332941
[[intel::disable_loop_pipelining] for (int i = 0; i < 10; ++i) var++;
29342942
}
29352943

2944+
[[intel::disable_loop_pipelining] void foo1() { }
2945+
29362946
}];
29372947
}
29382948

clang/include/clang/Sema/Sema.h

+5
Original file line numberDiff line numberDiff line change
@@ -10298,6 +10298,11 @@ class Sema final {
1029810298
IntelFPGAForcePow2DepthAttr *
1029910299
MergeIntelFPGAForcePow2DepthAttr(Decl *D,
1030010300
const IntelFPGAForcePow2DepthAttr &A);
10301+
void AddSYCLIntelFPGAInitiationIntervalAttr(Decl *D,
10302+
const AttributeCommonInfo &CI,
10303+
Expr *E);
10304+
SYCLIntelFPGAInitiationIntervalAttr *MergeSYCLIntelFPGAInitiationIntervalAttr(
10305+
Decl *D, const SYCLIntelFPGAInitiationIntervalAttr &A);
1030110306

1030210307
/// AddAlignedAttr - Adds an aligned attribute to a particular declaration.
1030310308
void AddAlignedAttr(Decl *D, const AttributeCommonInfo &CI, Expr *E,

clang/lib/CodeGen/CodeGenFunction.cpp

+16
Original file line numberDiff line numberDiff line change
@@ -738,6 +738,22 @@ void CodeGenFunction::EmitOpenCLKernelMetadata(const FunctionDecl *FD,
738738
llvm::ConstantAsMetadata::get(Builder.getInt32(1))};
739739
Fn->setMetadata("stall_enable", llvm::MDNode::get(Context, AttrMDArgs));
740740
}
741+
742+
if (FD->hasAttr<SYCLIntelFPGADisableLoopPipeliningAttr>()) {
743+
llvm::Metadata *AttrMDArgs[] = {
744+
llvm::ConstantAsMetadata::get(Builder.getInt32(1))};
745+
Fn->setMetadata("disable_loop_pipelining",
746+
llvm::MDNode::get(Context, AttrMDArgs));
747+
}
748+
749+
if (const auto *A = FD->getAttr<SYCLIntelFPGAInitiationIntervalAttr>()) {
750+
const auto *CE = cast<ConstantExpr>(A->getIntervalExpr());
751+
llvm::APSInt ArgVal = CE->getResultAsAPSInt();
752+
llvm::Metadata *AttrMDArgs[] = {
753+
llvm::ConstantAsMetadata::get(Builder.getInt32(ArgVal.getSExtValue()))};
754+
Fn->setMetadata("initiation_interval",
755+
llvm::MDNode::get(Context, AttrMDArgs));
756+
}
741757
}
742758

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

clang/lib/Sema/SemaDecl.cpp

+2
Original file line numberDiff line numberDiff line change
@@ -2630,6 +2630,8 @@ static bool mergeDeclAttribute(Sema &S, NamedDecl *D,
26302630
NewAttr = S.MergeIntelFPGAMaxReplicatesAttr(D, *A);
26312631
else if (const auto *A = dyn_cast<IntelFPGAForcePow2DepthAttr>(Attr))
26322632
NewAttr = S.MergeIntelFPGAForcePow2DepthAttr(D, *A);
2633+
else if (const auto *A = dyn_cast<SYCLIntelFPGAInitiationIntervalAttr>(Attr))
2634+
NewAttr = S.MergeSYCLIntelFPGAInitiationIntervalAttr(D, *A);
26332635
else if (Attr->shouldInheritEvenIfAlreadyPresent() || !DeclHasAttr(D, Attr))
26342636
NewAttr = cast<InheritableAttr>(Attr->clone(S.Context));
26352637

clang/lib/Sema/SemaDeclAttr.cpp

+105
Original file line numberDiff line numberDiff line change
@@ -3330,6 +3330,105 @@ static void handleUseStallEnableClustersAttr(Sema &S, Decl *D,
33303330
handleSimpleAttribute<SYCLIntelUseStallEnableClustersAttr>(S, D, Attr);
33313331
}
33323332

3333+
// Handles disable_loop_pipelining attribute.
3334+
static void handleSYCLIntelFPGADisableLoopPipeliningAttr(Sema &S, Decl *D,
3335+
const ParsedAttr &A) {
3336+
S.CheckDeprecatedSYCLAttributeSpelling(A);
3337+
3338+
// [[intel::disable_loop_pipelining] and [[intel::initiation_interval()]]
3339+
// attributes are incompatible.
3340+
if (checkAttrMutualExclusion<SYCLIntelFPGAInitiationIntervalAttr>(S, D, A))
3341+
return;
3342+
3343+
D->addAttr(::new (S.Context)
3344+
SYCLIntelFPGADisableLoopPipeliningAttr(S.Context, A));
3345+
}
3346+
3347+
// Handles initiation_interval attribute.
3348+
void Sema::AddSYCLIntelFPGAInitiationIntervalAttr(Decl *D,
3349+
const AttributeCommonInfo &CI,
3350+
Expr *E) {
3351+
if (!E->isValueDependent()) {
3352+
// Validate that we have an integer constant expression and then store the
3353+
// converted constant expression into the semantic attribute so that we
3354+
// don't have to evaluate it again later.
3355+
llvm::APSInt ArgVal;
3356+
ExprResult Res = VerifyIntegerConstantExpression(E, &ArgVal);
3357+
if (Res.isInvalid())
3358+
return;
3359+
E = Res.get();
3360+
// This attribute requires a strictly positive value.
3361+
if (ArgVal <= 0) {
3362+
Diag(E->getExprLoc(), diag::err_attribute_requires_positive_integer)
3363+
<< CI << /*positive*/ 0;
3364+
return;
3365+
}
3366+
// Check to see if there's a duplicate attribute with different values
3367+
// already applied to the declaration.
3368+
if (const auto *DeclAttr =
3369+
D->getAttr<SYCLIntelFPGAInitiationIntervalAttr>()) {
3370+
// If the other attribute argument is instantiation dependent, we won't
3371+
// have converted it to a constant expression yet and thus we test
3372+
// whether this is a null pointer.
3373+
if (const auto *DeclExpr =
3374+
dyn_cast<ConstantExpr>(DeclAttr->getIntervalExpr())) {
3375+
if (ArgVal != DeclExpr->getResultAsAPSInt()) {
3376+
Diag(CI.getLoc(), diag::warn_duplicate_attribute) << CI;
3377+
Diag(DeclAttr->getLoc(), diag::note_previous_attribute);
3378+
}
3379+
// Drop the duplicate attribute.
3380+
return;
3381+
}
3382+
}
3383+
}
3384+
3385+
// [[intel::disable_loop_pipelining] and [[intel::initiation_interval()]]
3386+
// attributes are incompatible.
3387+
if (checkAttrMutualExclusion<SYCLIntelFPGADisableLoopPipeliningAttr>(*this, D,
3388+
CI))
3389+
return;
3390+
3391+
D->addAttr(::new (Context)
3392+
SYCLIntelFPGAInitiationIntervalAttr(Context, CI, E));
3393+
}
3394+
3395+
SYCLIntelFPGAInitiationIntervalAttr *
3396+
Sema::MergeSYCLIntelFPGAInitiationIntervalAttr(
3397+
Decl *D, const SYCLIntelFPGAInitiationIntervalAttr &A) {
3398+
// Check to see if there's a duplicate attribute with different values
3399+
// already applied to the declaration.
3400+
if (const auto *DeclAttr =
3401+
D->getAttr<SYCLIntelFPGAInitiationIntervalAttr>()) {
3402+
if (const auto *DeclExpr =
3403+
dyn_cast<ConstantExpr>(DeclAttr->getIntervalExpr())) {
3404+
if (const auto *MergeExpr = dyn_cast<ConstantExpr>(A.getIntervalExpr())) {
3405+
if (DeclExpr->getResultAsAPSInt() != MergeExpr->getResultAsAPSInt()) {
3406+
Diag(DeclAttr->getLoc(), diag::warn_duplicate_attribute) << &A;
3407+
Diag(A.getLoc(), diag::note_previous_attribute);
3408+
}
3409+
// Do not add a duplicate attribute.
3410+
return nullptr;
3411+
}
3412+
}
3413+
}
3414+
3415+
// [[intel::initiation_interval()]] and [[intel::disable_loop_pipelining]
3416+
// attributes are incompatible.
3417+
if (checkAttrMutualExclusion<SYCLIntelFPGADisableLoopPipeliningAttr>(*this, D,
3418+
A))
3419+
return nullptr;
3420+
3421+
return ::new (Context)
3422+
SYCLIntelFPGAInitiationIntervalAttr(Context, A, A.getIntervalExpr());
3423+
}
3424+
3425+
static void handleSYCLIntelFPGAInitiationIntervalAttr(Sema &S, Decl *D,
3426+
const ParsedAttr &A) {
3427+
S.CheckDeprecatedSYCLAttributeSpelling(A);
3428+
3429+
S.AddSYCLIntelFPGAInitiationIntervalAttr(D, A, A.getArgAsExpr(0));
3430+
}
3431+
33333432
// Handle scheduler_target_fmax_mhz
33343433
void Sema::AddSYCLIntelSchedulerTargetFmaxMhzAttr(Decl *D,
33353434
const AttributeCommonInfo &CI,
@@ -9275,6 +9374,12 @@ static void ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D,
92759374
case ParsedAttr::AT_SYCLIntelLoopFuse:
92769375
handleSYCLIntelLoopFuseAttr(S, D, AL);
92779376
break;
9377+
case ParsedAttr::AT_SYCLIntelFPGADisableLoopPipelining:
9378+
handleSYCLIntelFPGADisableLoopPipeliningAttr(S, D, AL);
9379+
break;
9380+
case ParsedAttr::AT_SYCLIntelFPGAInitiationInterval:
9381+
handleSYCLIntelFPGAInitiationIntervalAttr(S, D, AL);
9382+
break;
92789383
case ParsedAttr::AT_VecTypeHint:
92799384
handleVecTypeHint(S, D, AL);
92809385
break;

clang/lib/Sema/SemaSYCL.cpp

+20
Original file line numberDiff line numberDiff line change
@@ -565,6 +565,24 @@ class MarkDeviceFunction : public RecursiveASTVisitor<MarkDeviceFunction> {
565565
}
566566
}
567567

568+
// Attribute "disable_loop_pipelining" can be applied explicitly on
569+
// kernel function. Attribute should not be propagated from device
570+
// functions to kernel.
571+
if (auto *A = FD->getAttr<SYCLIntelFPGADisableLoopPipeliningAttr>()) {
572+
if (ParentFD == SYCLKernel) {
573+
Attrs.push_back(A);
574+
}
575+
}
576+
577+
// Attribute "initiation_interval" can be applied explicitly on
578+
// kernel function. Attribute should not be propagated from device
579+
// functions to kernel.
580+
if (auto *A = FD->getAttr<SYCLIntelFPGAInitiationIntervalAttr>()) {
581+
if (ParentFD == SYCLKernel) {
582+
Attrs.push_back(A);
583+
}
584+
}
585+
568586
// TODO: vec_len_hint should be handled here
569587

570588
CallGraphNode *N = SYCLCG.getNode(FD);
@@ -3517,6 +3535,8 @@ void Sema::MarkDevice(void) {
35173535
case attr::Kind::SYCLIntelNoGlobalWorkOffset:
35183536
case attr::Kind::SYCLIntelUseStallEnableClusters:
35193537
case attr::Kind::SYCLIntelLoopFuse:
3538+
case attr::Kind::SYCLIntelFPGADisableLoopPipelining:
3539+
case attr::Kind::SYCLIntelFPGAInitiationInterval:
35203540
case attr::Kind::SYCLSimd: {
35213541
if ((A->getKind() == attr::Kind::SYCLSimd) && KernelBody &&
35223542
!KernelBody->getAttr<SYCLSimdAttr>()) {

clang/lib/Sema/SemaTemplateInstantiateDecl.cpp

+16
Original file line numberDiff line numberDiff line change
@@ -713,6 +713,16 @@ static void instantiateIntelFPGAMaxReplicatesAttr(
713713
S.AddIntelFPGAMaxReplicatesAttr(New, *A, Result.getAs<Expr>());
714714
}
715715

716+
static void instantiateSYCLIntelFPGAInitiationIntervalAttr(
717+
Sema &S, const MultiLevelTemplateArgumentList &TemplateArgs,
718+
const SYCLIntelFPGAInitiationIntervalAttr *A, Decl *New) {
719+
EnterExpressionEvaluationContext Unevaluated(
720+
S, Sema::ExpressionEvaluationContext::ConstantEvaluated);
721+
ExprResult Result = S.SubstExpr(A->getIntervalExpr(), TemplateArgs);
722+
if (!Result.isInvalid())
723+
S.AddSYCLIntelFPGAInitiationIntervalAttr(New, *A, Result.getAs<Expr>());
724+
}
725+
716726
/// Determine whether the attribute A might be relevent to the declaration D.
717727
/// If not, we can skip instantiating it. The attribute may or may not have
718728
/// been instantiated yet.
@@ -939,6 +949,12 @@ void Sema::InstantiateAttrs(const MultiLevelTemplateArgumentList &TemplateArgs,
939949
*this, TemplateArgs, SYCLIntelMaxWorkGroupSize, New);
940950
continue;
941951
}
952+
if (const auto *SYCLIntelFPGAInitiationInterval =
953+
dyn_cast<SYCLIntelFPGAInitiationIntervalAttr>(TmplAttr)) {
954+
instantiateSYCLIntelFPGAInitiationIntervalAttr(
955+
*this, TemplateArgs, SYCLIntelFPGAInitiationInterval, New);
956+
continue;
957+
}
942958
// Existing DLL attribute on the instantiation takes precedence.
943959
if (TmplAttr->getKind() == attr::DLLExport ||
944960
TmplAttr->getKind() == attr::DLLImport) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// 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
2+
3+
#include "sycl.hpp"
4+
5+
using namespace cl::sycl;
6+
queue q;
7+
8+
class Foo {
9+
public:
10+
[[intel::disable_loop_pipelining]] void operator()() const {}
11+
};
12+
13+
[[intel::disable_loop_pipelining]] void foo() {}
14+
15+
int main() {
16+
q.submit([&](handler &h) {
17+
// Test attribute is presented on function metadata.
18+
Foo f;
19+
h.single_task<class test_kernel1>(f);
20+
21+
// Test attribute is not propagated.
22+
h.single_task<class test_kernel2>(
23+
[]() { foo(); });
24+
25+
// Test attribute is applied on lambda.
26+
h.single_task<class test_kernel3>(
27+
[]() [[intel::disable_loop_pipelining]]{});
28+
});
29+
return 0;
30+
}
31+
32+
// CHECK: define dso_local spir_kernel void @"{{.*}}test_kernel1"() #0 !kernel_arg_buffer_location ![[NUM4:[0-9]+]] !disable_loop_pipelining ![[NUM5:[0-9]+]]
33+
// CHECK: define dso_local spir_kernel void @"{{.*}}test_kernel2"() #0 !kernel_arg_buffer_location ![[NUM4]]
34+
// CHECK: define dso_local spir_kernel void @"{{.*}}test_kernel3"() #0 !kernel_arg_buffer_location ![[NUM4]] !disable_loop_pipelining ![[NUM5]]
35+
// CHECK: ![[NUM4]] = !{}
36+
// CHECK: ![[NUM5]] = !{i32 1}

0 commit comments

Comments
 (0)