Skip to content
23 changes: 23 additions & 0 deletions clang/include/clang/Basic/Attr.td
Original file line number Diff line number Diff line change
Expand Up @@ -2308,6 +2308,23 @@ def : MutualExclusions<[SYCLIntelFPGAIVDep,
def : MutualExclusions<[SYCLIntelFPGAMaxConcurrency,
SYCLIntelFPGADisableLoopPipelining]>;

def SYCLIntelFPGAPipeline : InheritableAttr {
let Spellings = [CXX11<"intel","fpga_pipeline">];
let Args = [ExprArgument<"Value", /*optional*/1>];
let LangOpts = [SYCLIsDevice, SilentlyIgnoreSYCLIsHost];
let Subjects = SubjectList<[ForStmt, CXXForRangeStmt, WhileStmt, DoStmt],
ErrorDiag, "'for', 'while', and 'do' statements">;
let Documentation = [SYCLIntelFPGAPipelineAttrDocs];
let IsStmtDependent = 1;
}

def : MutualExclusions<[SYCLIntelFPGAInitiationInterval,
SYCLIntelFPGAPipeline]>;
def : MutualExclusions<[SYCLIntelFPGAIVDep,
SYCLIntelFPGAPipeline]>;
def : MutualExclusions<[SYCLIntelFPGAMaxConcurrency,
SYCLIntelFPGAPipeline]>;

def SYCLIntelFPGALoopCount : StmtAttr {
let Spellings = [CXX11<"intel", "loop_count_min">,
CXX11<"intel", "loop_count_max">,
Expand Down Expand Up @@ -2340,6 +2357,9 @@ def SYCLIntelFPGAMaxInterleaving : StmtAttr {
def : MutualExclusions<[SYCLIntelFPGADisableLoopPipelining,
SYCLIntelFPGAMaxInterleaving]>;

def : MutualExclusions<[SYCLIntelFPGAPipeline,
SYCLIntelFPGAMaxInterleaving]>;

def SYCLIntelFPGASpeculatedIterations : StmtAttr {
let Spellings = [CXX11<"intel", "speculated_iterations">];
let Subjects = SubjectList<[ForStmt, CXXForRangeStmt, WhileStmt, DoStmt],
Expand All @@ -2352,6 +2372,9 @@ def SYCLIntelFPGASpeculatedIterations : StmtAttr {
def : MutualExclusions<[SYCLIntelFPGADisableLoopPipelining,
SYCLIntelFPGASpeculatedIterations]>;

def : MutualExclusions<[SYCLIntelFPGAPipeline,
SYCLIntelFPGASpeculatedIterations]>;

def SYCLIntelFPGANofusion : StmtAttr {
let Spellings = [CXX11<"intel","nofusion">];
let Subjects = SubjectList<[ForStmt, CXXForRangeStmt, WhileStmt, DoStmt],
Expand Down
55 changes: 55 additions & 0 deletions clang/include/clang/Basic/AttrDocs.td
Original file line number Diff line number Diff line change
Expand Up @@ -3291,6 +3291,9 @@ function, or in conjunction with ``max_interleaving``,
``speculated_iterations``, ``max_concurrency``, ``initiation_interval``,
or ``ivdep``.

The ``[[intel::disable_loop_pipelining]]`` attribute spelling is a deprecated
synonym for ``[[[intel::fpga_pipeline]]`` and will be removed in the future.

.. code-block:: c++

void foo() {
Expand All @@ -3303,6 +3306,58 @@ or ``ivdep``.
}];
}

def SYCLIntelFPGAPipelineAttrDocs : Documentation {
let Category = DocCatVariable;
let Heading = "intel::fpga_pipeline";
let Content = [{
The ``intel::fpga_pipeline(N)`` attribute applies to a loop and it allows users
to disable or enable pipelining iterations of a loop. The attribute optionally
accepts an integer constant expression that is converted to `bool`. A `true`
value enables pipelining while a `false` value disables pipelining. The
optional argument defaults to `true`. This attribute cannot be applied to a
loop in conjunction with the ``max_interleaving``, ``speculated_iterations``,
``max_concurrency``, ``initiation_interval``, or ``ivdep`` attributes.

.. code-block:: c++

// Disable loop pipelining
void bar() {
int a[10];
[[[intel::fpga_pipeline(0)]] for (int i = 0; i != 10; ++i) a[i] = 0;
}

// Enable loop pipelining
void foo() {
int var = 0;
[[intel::fpga_pipeline(1)]] for (int i = 0; i < 10; ++i) var++;
}

void Array(int *array, size_t n) {
// identical to [[intel::fpga_pipeline(1)]]
[[intel::fpga_pipeline]] for (int i = 0; i < n; ++i) array[i] = 0;
}

void count () {
int a1[10], int i = 0;
[[intel::fpga_pipeline(1)]] while (i < 10) {
a1[i] += 3;
}

void check() {
int a = 10;
[[intel::fpga_pipeline(1)]] do {
a = a + 1;
} while (a < 20);
}

template<int A>
void func() {
[[intel::fpga_pipeline(A)]] for(;;) { }
}

}];
}

def SYCLIntelFPGALoopCountAttrDocs : Documentation {
let Category = DocCatVariable;
let Heading = "intel::loop_count_min, intel::loop_count_max, intel::loop_count_avg, intel::loop_count";
Expand Down
3 changes: 3 additions & 0 deletions clang/include/clang/Sema/Sema.h
Original file line number Diff line number Diff line change
Expand Up @@ -2277,6 +2277,9 @@ class Sema final {
SYCLIntelFPGALoopCoalesceAttr *
BuildSYCLIntelFPGALoopCoalesceAttr(const AttributeCommonInfo &CI, Expr *E);

SYCLIntelFPGAPipelineAttr *
BuildSYCLIntelFPGAPipelineAttr(const AttributeCommonInfo &CI, Expr *E);

bool CheckQualifiedFunctionForTypeId(QualType T, SourceLocation Loc);

bool CheckFunctionReturnType(QualType T, SourceLocation Loc);
Expand Down
23 changes: 22 additions & 1 deletion clang/lib/CodeGen/CGLoopInfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -611,6 +611,14 @@ MDNode *LoopInfo::createMetadata(
llvm::Type::getInt32Ty(Ctx), VC.second))};
LoopProperties.push_back(MDNode::get(Ctx, Vals));
}

for (auto &FP : Attrs.SYCLIntelFPGAPipeline) {
Metadata *Vals[] = {MDString::get(Ctx, FP.first),
ConstantAsMetadata::get(ConstantInt::get(
llvm::Type::getInt32Ty(Ctx), FP.second))};
LoopProperties.push_back(MDNode::get(Ctx, Vals));
}

LoopProperties.insert(LoopProperties.end(), AdditionalLoopProperties.begin(),
AdditionalLoopProperties.end());
return createFullUnrollMetadata(Attrs, LoopProperties, HasUserTransforms);
Expand Down Expand Up @@ -654,6 +662,7 @@ void LoopAttributes::clear() {
PipelineDisabled = false;
PipelineInitiationInterval = 0;
SYCLNofusionEnable = false;
SYCLIntelFPGAPipeline.clear();
MustProgress = false;
}

Expand Down Expand Up @@ -687,7 +696,8 @@ LoopInfo::LoopInfo(BasicBlock *Header, const LoopAttributes &Attrs,
Attrs.UnrollEnable == LoopAttributes::Unspecified &&
Attrs.UnrollAndJamEnable == LoopAttributes::Unspecified &&
Attrs.DistributeEnable == LoopAttributes::Unspecified && !StartLoc &&
Attrs.SYCLNofusionEnable == false && !EndLoc && !Attrs.MustProgress)
Attrs.SYCLNofusionEnable == false &&
Attrs.SYCLIntelFPGAPipeline.empty() && !EndLoc && !Attrs.MustProgress)
return;

TempLoopID = MDNode::getTemporary(Header->getContext(), None);
Expand Down Expand Up @@ -1011,6 +1021,8 @@ void LoopInfoStack::push(BasicBlock *Header, clang::ASTContext &Ctx,
// emitted
// For attribute nofusion:
// 'llvm.loop.fusion.disable' metadata will be emitted
// For attribute fpga_pipeline:
// n - 'llvm.loop.intel.pipelining.enable, i32 n' metadata will be emitted
for (const auto *A : Attrs) {
if (const auto *IntelFPGAIVDep = dyn_cast<SYCLIntelFPGAIVDepAttr>(A))
addSYCLIVDepInfo(Header->getContext(), IntelFPGAIVDep->getSafelenValue(),
Expand Down Expand Up @@ -1075,6 +1087,15 @@ void LoopInfoStack::push(BasicBlock *Header, clang::ASTContext &Ctx,

if (isa<SYCLIntelFPGANofusionAttr>(A))
setSYCLNofusionEnable();

if (const auto *IntelFPGAPipeline =
dyn_cast<SYCLIntelFPGAPipelineAttr>(A)) {
const auto *CE = cast<ConstantExpr>(IntelFPGAPipeline->getValue());
Optional<llvm::APSInt> ArgVal = CE->getResultAsAPSInt();
unsigned int Value = ArgVal->getBoolValue() ? 1 : 0;
const char *Var = "llvm.loop.intel.pipelining.enable";
setSYCLIntelFPGAPipeline(Var, Value);
}
}

setMustProgress(MustProgress);
Expand Down
9 changes: 9 additions & 0 deletions clang/lib/CodeGen/CGLoopInfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,10 @@ struct LoopAttributes {
/// Flag for llvm.loop.fusion.disable metatdata.
bool SYCLNofusionEnable;

/// Value for fpga_pipeline variant and metadata.
llvm::SmallVector<std::pair<const char *, unsigned int>, 2>
SYCLIntelFPGAPipeline;

/// Value for whether the loop is required to make progress.
bool MustProgress;
};
Expand Down Expand Up @@ -407,6 +411,11 @@ class LoopInfoStack {
/// Set flag of nofusion for the next loop pushed.
void setSYCLNofusionEnable() { StagedAttrs.SYCLNofusionEnable = true; }

/// Set variant and value of fpga_pipeline for the next loop pushed.
void setSYCLIntelFPGAPipeline(const char *Var, unsigned int Value) {
StagedAttrs.SYCLIntelFPGAPipeline.push_back({Var, Value});
}

/// Set no progress for the next loop pushed.
void setMustProgress(bool P) { StagedAttrs.MustProgress = P; }

Expand Down
8 changes: 8 additions & 0 deletions clang/lib/Sema/SemaDeclAttr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -364,6 +364,14 @@ void Sema::CheckDeprecatedSYCLAttributeSpelling(const ParsedAttr &A,
Diag(A.getLoc(), diag::ext_sycl_2020_attr_spelling) << A;
return;
}

// Deprecate [[intel::disable_loop_pipelining]] attribute spelling in favor
// of the SYCL FPGA attribute spelling [[intel::fpga_pipeline]].
if (A.hasScope() && A.getScopeName()->isStr("intel") &&
A.getAttrName()->isStr("disable_loop_pipelining")) {
DiagnoseDeprecatedAttribute(A, "intel", "fpga_pipeline");
return;
}
}

/// Check if IdxExpr is a valid parameter index for a function or
Expand Down
30 changes: 30 additions & 0 deletions clang/lib/Sema/SemaStmtAttr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,33 @@ static Attr *handleSYCLIntelFPGADisableLoopPipeliningAttr(Sema &S, Stmt *,
return new (S.Context) SYCLIntelFPGADisableLoopPipeliningAttr(S.Context, A);
}

// Handle [[intel:fpga_pipeline]] attribute.
static Attr *handleSYCLIntelFPGAPipelineAttr(Sema &S, Stmt *,
const ParsedAttr &A) {
// If no attribute argument is specified, set to default value '1'.
Expr *E = A.isArgExpr(0)
? A.getArgAsExpr(0)
: IntegerLiteral::Create(S.Context, llvm::APInt(32, 1),
S.Context.IntTy, A.getLoc());

return S.BuildSYCLIntelFPGAPipelineAttr(A, E);
}

SYCLIntelFPGAPipelineAttr *
Sema::BuildSYCLIntelFPGAPipelineAttr(const AttributeCommonInfo &A, Expr *E) {

if (!E->isValueDependent()) {
// Check if the expression is not value dependent.
llvm::APSInt ArgVal;
ExprResult Res = VerifyIntegerConstantExpression(E, &ArgVal);
if (Res.isInvalid())
return nullptr;
E = Res.get();
}

return new (Context) SYCLIntelFPGAPipelineAttr(Context, A, E);
}

static bool checkSYCLIntelFPGAIVDepSafeLen(Sema &S, llvm::APSInt &Value,
Expr *E) {
if (!Value.isStrictlyPositive())
Expand Down Expand Up @@ -821,6 +848,7 @@ static void CheckForIncompatibleSYCLLoopAttributes(
CheckForDuplicationSYCLLoopAttribute<LoopUnrollHintAttr>(S, Attrs, false);
CheckRedundantSYCLIntelFPGAIVDepAttrs(S, Attrs);
CheckForDuplicationSYCLLoopAttribute<SYCLIntelFPGANofusionAttr>(S, Attrs);
CheckForDuplicationSYCLLoopAttribute<SYCLIntelFPGAPipelineAttr>(S, Attrs);
}

void CheckForIncompatibleUnrollHintAttributes(
Expand Down Expand Up @@ -966,6 +994,8 @@ static Attr *ProcessStmtAttribute(Sema &S, Stmt *St, const ParsedAttr &A,
return handleUnlikely(S, St, A, Range);
case ParsedAttr::AT_SYCLIntelFPGANofusion:
return handleIntelFPGANofusionAttr(S, St, A);
case ParsedAttr::AT_SYCLIntelFPGAPipeline:
return handleSYCLIntelFPGAPipelineAttr(S, St, A);
default:
// N.B., ClangAttrEmitter.cpp emits a diagnostic helper that ensures a
// declaration attribute is not written on a statement, but this code is
Expand Down
9 changes: 9 additions & 0 deletions clang/lib/Sema/SemaTemplateInstantiate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1110,6 +1110,8 @@ namespace {
const SYCLIntelFPGASpeculatedIterationsAttr *SI);
const SYCLIntelFPGALoopCountAttr *
TransformSYCLIntelFPGALoopCountAttr(const SYCLIntelFPGALoopCountAttr *SI);
const SYCLIntelFPGAPipelineAttr *
TransformSYCLIntelFPGAPipelineAttr(const SYCLIntelFPGAPipelineAttr *SI);

ExprResult TransformPredefinedExpr(PredefinedExpr *E);
ExprResult TransformDeclRefExpr(DeclRefExpr *E);
Expand Down Expand Up @@ -1601,6 +1603,13 @@ const LoopUnrollHintAttr *TemplateInstantiator::TransformLoopUnrollHintAttr(
return getSema().BuildLoopUnrollHintAttr(*LU, TransformedExpr);
}

const SYCLIntelFPGAPipelineAttr *
TemplateInstantiator::TransformSYCLIntelFPGAPipelineAttr(
const SYCLIntelFPGAPipelineAttr *PA) {
Expr *TransformedExpr = getDerived().TransformExpr(PA->getValue()).get();
return getSema().BuildSYCLIntelFPGAPipelineAttr(*PA, TransformedExpr);
}

ExprResult TemplateInstantiator::transformNonTypeTemplateParmRef(
NonTypeTemplateParmDecl *parm,
SourceLocation loc,
Expand Down
52 changes: 52 additions & 0 deletions clang/test/CodeGenSYCL/intel-fpga-loops.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,14 @@
// CHECK: br label %for.cond2, !llvm.loop ![[MD_LCA_1:[0-9]+]]
// CHECK: br label %for.cond13, !llvm.loop ![[MD_LCA_2:[0-9]+]]
// CHECK: br label %for.cond24, !llvm.loop ![[MD_LCA_3:[0-9]+]]
// CHECK: br label %for.cond, !llvm.loop ![[MD_FP:[0-9]+]]
// CHECK: br label %for.cond2, !llvm.loop ![[MD_FP_1:[0-9]+]]
// CHECK: br label %for.cond13, !llvm.loop ![[MD_FP_2:[0-9]+]]
// CHECK: br label %for.cond24, !llvm.loop ![[MD_FP_3:[0-9]+]]
// CHECK: br label %while.cond, !llvm.loop ![[MD_FP_4:[0-9]+]]
// CHECK: br i1 %cmp38, label %do.body, label %do.end, !llvm.loop ![[MD_FP_5:[0-9]+]]
// CHECK: br label %for.cond40, !llvm.loop ![[MD_FP_6:[0-9]+]]
// CHECK: br label %while.cond47, !llvm.loop ![[MD_FP_7:[0-9]+]]

void disable_loop_pipelining() {
int a[10];
Expand Down Expand Up @@ -126,6 +134,7 @@ void speculated_iterations() {
a[i] = 0;
}

// Add CodeGen tests for FPGA loop attribute: [[intel::fpga_pipeline()]].
template <int A>
void loop_count_control() {
int a[10];
Expand All @@ -150,6 +159,48 @@ void loop_count_control() {
a[i] = 0;
}

// Add CodeGen tests for Loop attribute: [[intel::fpga_pipeline()]].
template <int A>
void fpga_pipeline() {
int a[10];
// CHECK: ![[MD_FP]] = distinct !{![[MD_FP]], ![[MP]], ![[MD_fpga_pipeline:[0-9]+]]}
// CHECK-NEXT: ![[MD_fpga_pipeline]] = !{!"llvm.loop.intel.pipelining.enable", i32 1}
[[intel::fpga_pipeline(A)]] for (int i = 0; i != 10; ++i)
a[i] = 0;

// CHECK: ![[MD_FP_1]] = distinct !{![[MD_FP_1]], ![[MP]], ![[MD_fpga_pipeline]]}
[[intel::fpga_pipeline(1)]] for (int i = 0; i != 10; ++i)
a[i] = 0;

// CHECK: ![[MD_FP_2]] = distinct !{![[MD_FP_2]], ![[MP]], ![[MD_fpga_pipeline]]}
[[intel::fpga_pipeline]] for (int i = 0; i != 10; ++i)
a[i] = 0;

// CHECK: ![[MD_FP_3]] = distinct !{![[MD_FP_3]], ![[MP]], ![[MD_dlp]]}
[[intel::fpga_pipeline(0)]] for (int i = 0; i != 10; ++i)
a[i] = 0;

// CHECK: ![[MD_FP_4]] = distinct !{![[MD_FP_4]], ![[MP]], ![[MD_fpga_pipeline]]}
int j = 0;
[[intel::fpga_pipeline]] while (j < 10) {
a[j] += 3;
}

// CHECK: ![[MD_FP_5]] = distinct !{![[MD_FP_5]], ![[MP]], ![[MD_fpga_pipeline]]}
int b = 10;
[[intel::fpga_pipeline(1)]] do {
b = b + 1;
} while (b < 20);

// CHECK: ![[MD_FP_6]] = distinct !{![[MD_FP_6]], ![[MD_fpga_pipeline]]}
int c[] = {0, 1, 2, 3, 4, 5};
[[intel::fpga_pipeline(A)]] for (int n : c) { n *= 2; }

// CHECK: ![[MD_FP_7]] = distinct !{![[MD_FP_7]], ![[MP]], ![[MD_fpga_pipeline]]}
int k = 0;
[[intel::fpga_pipeline(-1)]] while (k < 20) { a[k] += 2; }
}

template <typename name, typename Func>
__attribute__((sycl_kernel)) void kernel_single_task(const Func &kernelFunc) {
kernelFunc();
Expand All @@ -165,6 +216,7 @@ int main() {
max_interleaving<3, 0>();
speculated_iterations<4, 0>();
loop_count_control<12>();
fpga_pipeline<1>();
});
return 0;
}
Loading