-
Notifications
You must be signed in to change notification settings - Fork 769
[SYCL] Add Clang support for FPGA loop fusion function attributes #2877
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
Changes from 3 commits
324390f
c94d591
c6cbe4d
1b9ce65
d761846
c7ba4b3
8c3c5b4
26b2e52
26bbc04
702bed5
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2608,6 +2608,34 @@ loop should not be fused with any adjacent loop. | |
}]; | ||
} | ||
|
||
def SYCLIntelLoopFuseDocs : Documentation { | ||
let Category = DocCatFunction; | ||
elizabethandrews marked this conversation as resolved.
Show resolved
Hide resolved
|
||
let Content = [{ | ||
``[[intel::loop_fuse(N)]]`` and ``[[intel::loop_fuse_independent(N)]]`` attributes apply | ||
to a function/lambda function. It is a strong request, to the extent possible, to fuse | ||
the loops within the function, that are contained in at most N-1 other loops within the | ||
function. If the optional parameter N is omitted, it is a strong request, to the extent | ||
possible, to fuse loops within the function that are not contained in any other loop | ||
within the function. ``[[intel::loop_fuse_independent(N)]]`` also guarantees that fusion | ||
safety analysis can ignore negative-distance dependences between these loops. The attributes | ||
elizabethandrews marked this conversation as resolved.
Show resolved
Hide resolved
|
||
are not propagated to their caller. | ||
|
||
.. code-block:: c++ | ||
|
||
[[intel::loop_fuse(N)]] | ||
int foo() {} | ||
|
||
[[intel::loop_fuse_independent(N)]] | ||
int foo() {} | ||
|
||
|
||
``[[intel::loop_fuse(N)]]`` and ``[[intel::loop_fuse_independent(N)]]`` takes one optional | ||
parameter, a constant integral expression N with value greater than or equal to 0. The | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It might be more clear to say this as "takes one optional argument that is a constant unsigned integer expression." |
||
parameter N may be a template parameter. | ||
|
||
}]; | ||
} | ||
|
||
def SYCLDeviceIndirectlyCallableDocs : Documentation { | ||
let Category = DocCatFunction; | ||
let Heading = "intel::device_indirectly_callable"; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -318,6 +318,19 @@ static bool checkAttrMutualExclusion(Sema &S, Decl *D, const Attr &AL) { | |
return false; | ||
} | ||
|
||
/// Give a warning for duplicate attributes, return true if duplicate. | ||
elizabethandrews marked this conversation as resolved.
Show resolved
Hide resolved
|
||
template <typename AttrType> | ||
static bool checkForDuplicateAttribute(Sema &S, Decl *D, | ||
const ParsedAttr &Attr) { | ||
// Give a warning for duplicates but not if it's one we've implicitly added. | ||
auto *A = D->getAttr<AttrType>(); | ||
if (A && !A->isImplicit()) { | ||
S.Diag(Attr.getLoc(), diag::warn_duplicate_attribute_exact) << A; | ||
return true; | ||
} | ||
return false; | ||
} | ||
|
||
static bool checkDeprecatedSYCLAttributeSpelling(Sema &S, | ||
const ParsedAttr &Attr) { | ||
if (Attr.getScopeName()->isStr("intelfpga")) | ||
|
@@ -3091,6 +3104,89 @@ static void handleMaxGlobalWorkDimAttr(Sema &S, Decl *D, | |
E); | ||
} | ||
|
||
SYCLIntelLoopFuseAttr * | ||
Sema::mergeSYCLIntelLoopFuseAttr(Decl *D, const AttributeCommonInfo &CI, | ||
Expr *E) { | ||
|
||
if (const auto ExistingAttr = D->getAttr<SYCLIntelLoopFuseAttr>()) { | ||
// [[intel::loop_fuse]] and [[intel::loop_fuse_independent]] are | ||
// incompatible | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. incompatible -> incompatible. (adds the full stop) |
||
if (ExistingAttr->getAttributeSpellingListIndex() != | ||
elizabethandrews marked this conversation as resolved.
Show resolved
Hide resolved
|
||
CI.getAttributeSpellingListIndex()) { | ||
Diag(CI.getLoc(), diag::err_attributes_are_not_compatible) | ||
<< CI.getAttrName() << ExistingAttr; | ||
elizabethandrews marked this conversation as resolved.
Show resolved
Hide resolved
|
||
Diag(ExistingAttr->getLocation(), diag::note_conflicting_attribute); | ||
return nullptr; | ||
} | ||
|
||
if (!E->isValueDependent()) { | ||
Optional<llvm::APSInt> ArgVal = E->getIntegerConstantExpr(Context); | ||
Optional<llvm::APSInt> ExistingArgVal = | ||
ExistingAttr->getValue()->getIntegerConstantExpr(Context); | ||
|
||
// Compare attribute argument value and warn if there is a mismatch | ||
if (ArgVal->getExtValue() != ExistingArgVal->getExtValue()) | ||
elizabethandrews marked this conversation as resolved.
Show resolved
Hide resolved
|
||
Diag(ExistingAttr->getLoc(), diag::warn_duplicate_attribute) | ||
<< ExistingAttr; | ||
} | ||
|
||
// If there is no mismatch, silently ignore duplicate attribute | ||
elizabethandrews marked this conversation as resolved.
Show resolved
Hide resolved
|
||
return nullptr; | ||
} | ||
return ::new (Context) SYCLIntelLoopFuseAttr(Context, CI, E); | ||
} | ||
|
||
static bool checkSYCLIntelLoopFuseArgument(Sema &S, | ||
const AttributeCommonInfo &CI, | ||
Expr *E) { | ||
// Dependent expressions are checked when instantiated. | ||
if (E->isValueDependent()) | ||
return false; | ||
|
||
Optional<llvm::APSInt> ArgVal = E->getIntegerConstantExpr(S.Context); | ||
if (!ArgVal) { | ||
S.Diag(E->getExprLoc(), diag::err_attribute_argument_type) | ||
<< CI.getAttrName() << AANT_ArgumentIntegerConstant | ||
elizabethandrews marked this conversation as resolved.
Show resolved
Hide resolved
|
||
<< E->getSourceRange(); | ||
return true; | ||
} | ||
|
||
SYCLIntelLoopFuseAttr TmpAttr(S.Context, CI, E); | ||
ExprResult ICE; | ||
if (S.checkRangedIntegralArgument<SYCLIntelLoopFuseAttr>(E, &TmpAttr, ICE)) | ||
elizabethandrews marked this conversation as resolved.
Show resolved
Hide resolved
|
||
return true; | ||
|
||
return false; | ||
} | ||
|
||
void Sema::addSYCLIntelLoopFuseAttr(Decl *D, const AttributeCommonInfo &CI, | ||
Expr *E) { | ||
assert(E && "argument has unexpected null value"); | ||
|
||
if (checkSYCLIntelLoopFuseArgument(*this, CI, E)) | ||
return; | ||
|
||
SYCLIntelLoopFuseAttr *NewAttr = mergeSYCLIntelLoopFuseAttr(D, CI, E); | ||
|
||
if (NewAttr) | ||
D->addAttr(NewAttr); | ||
} | ||
|
||
// Handles [[intel::loop_fuse]] and [[intel::loop_fuse_independent]]. | ||
static void handleLoopFuseAttr(Sema &S, Decl *D, const ParsedAttr &Attr) { | ||
// Attribute should not be processed during host compilation. | ||
if (S.LangOpts.SYCLIsHost) | ||
AaronBallman marked this conversation as resolved.
Show resolved
Hide resolved
|
||
return; | ||
|
||
// Default argument value is set to 1 | ||
Expr *E = Attr.isArgExpr(0) | ||
? Attr.getArgAsExpr(0) | ||
: IntegerLiteral::Create(S.Context, llvm::APInt(32, 1), | ||
S.Context.IntTy, Attr.getLoc()); | ||
|
||
S.addSYCLIntelLoopFuseAttr(D, Attr, E); | ||
} | ||
|
||
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; | ||
|
@@ -5256,19 +5352,6 @@ static void handleTypeTagForDatatypeAttr(Sema &S, Decl *D, | |
AL.getMustBeNull())); | ||
} | ||
|
||
/// Give a warning for duplicate attributes, return true if duplicate. | ||
template <typename AttrType> | ||
static bool checkForDuplicateAttribute(Sema &S, Decl *D, | ||
const ParsedAttr &Attr) { | ||
// Give a warning for duplicates but not if it's one we've implicitly added. | ||
auto *A = D->getAttr<AttrType>(); | ||
if (A && !A->isImplicit()) { | ||
S.Diag(Attr.getLoc(), diag::warn_duplicate_attribute_exact) << A; | ||
return true; | ||
} | ||
return false; | ||
} | ||
|
||
static void handleNoGlobalWorkOffsetAttr(Sema &S, Decl *D, | ||
const ParsedAttr &Attr) { | ||
if (S.LangOpts.SYCLIsHost) | ||
|
@@ -8388,6 +8471,9 @@ static void ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, | |
case ParsedAttr::AT_SYCLIntelUseStallEnableClusters: | ||
handleUseStallEnableClustersAttr(S, D, AL); | ||
break; | ||
case ParsedAttr::AT_SYCLIntelLoopFuse: | ||
handleLoopFuseAttr(S, D, AL); | ||
break; | ||
case ParsedAttr::AT_VecTypeHint: | ||
handleVecTypeHint(S, D, AL); | ||
break; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
// RUN: %clang_cc1 -fsycl -fsycl-is-device -internal-isystem %S/Inputs -triple spir64-unknown-unknown-sycldevice -emit-llvm -o - %s | FileCheck %s | ||
|
||
#include "sycl.hpp" | ||
|
||
using namespace cl::sycl; | ||
queue q; | ||
|
||
[[intel::loop_fuse(5)]] void foo() {} | ||
|
||
template <int SIZE> | ||
class KernelFunctor5 { | ||
public: | ||
[[intel::loop_fuse(SIZE)]] void operator()() const {} | ||
}; | ||
|
||
void bar() { | ||
|
||
q.submit([&](handler &h) { | ||
// Test template argument | ||
KernelFunctor5<5> f5; | ||
h.single_task<class kernel_name_1>(f5); | ||
|
||
// Test different argument sizes. | ||
// Emit 1 if there is no argument. | ||
h.single_task<class kernel_name_2>( | ||
[]() [[intel::loop_fuse]]{}); | ||
h.single_task<class kernel_name_3>( | ||
[]() [[intel::loop_fuse(0)]]{}); | ||
h.single_task<class kernel_name_4>( | ||
[]() [[intel::loop_fuse(1)]]{}); | ||
h.single_task<class kernel_name_5>( | ||
[]() [[intel::loop_fuse(10)]]{}); | ||
|
||
// Test attribute is not propagated. | ||
h.single_task<class kernel_name_6>( | ||
[]() { foo(); }); | ||
}); | ||
} | ||
|
||
// CHECK: define spir_kernel void @"{{.*}}kernel_name_1"() {{.*}} !loop_fuse ![[LF5:[0-9]+]] | ||
// CHECK: define spir_kernel void @"{{.*}}kernel_name_2"() {{.*}} !loop_fuse ![[LF1:[0-9]+]] | ||
// CHECK: define spir_kernel void @"{{.*}}kernel_name_3"() {{.*}} !loop_fuse ![[LF0:[0-9]+]] | ||
// CHECK: define spir_kernel void @"{{.*}}kernel_name_4"() {{.*}} !loop_fuse ![[LF1]] | ||
// CHECK: define spir_kernel void @"{{.*}}kernel_name_5"() {{.*}} !loop_fuse ![[LF10:[0-9]+]] | ||
// CHECK: define spir_kernel void @"{{.*}}kernel_name_6"() | ||
// CHECK-NOT: !loop_fuse | ||
// CHECK-SAME: { | ||
// CHECK: define spir_func void @{{.*}}foo{{.*}} !loop_fuse ![[LF5]] | ||
// CHECK: ![[LF5]] = !{i32 5, i32 0} | ||
// CHECK: ![[LF1]] = !{i32 1, i32 0} | ||
// CHECK: ![[LF0]] = !{i32 0, i32 0} | ||
// CHECK: ![[LF10]] = !{i32 10, i32 0} |
Uh oh!
There was an error while loading. Please reload this page.