[OpenCL] Generate metadata for opencl_unroll_hint attribute

Add support for opencl_unroll_hint attribute from OpenCL v2.0 s6.11.5.

Reusing most of metadata generation from CGLoopInfo helper class.

The code is based on Khronos OpenCL compiler:
https://github.com/KhronosGroup/SPIR/tree/spirv-1.0

Patch by Liu Yaxun (Sam)!

Differential Revision: http://reviews.llvm.org/D16686

llvm-svn: 261350
This commit is contained in:
Anastasia Stulova 2016-02-19 18:30:11 +00:00
parent 7e4ba3dc02
commit 6bdbcbb3d9
12 changed files with 270 additions and 12 deletions

View File

@ -656,6 +656,12 @@ def OpenCLKernel : InheritableAttr {
let Documentation = [Undocumented];
}
def OpenCLUnrollHint : InheritableAttr {
let Spellings = [GNU<"opencl_unroll_hint">];
let Args = [UnsignedArgument<"UnrollHint">];
let Documentation = [OpenCLUnrollHintDocs];
}
// This attribute is both a type attribute, and a declaration attribute (for
// parameter variables).
def OpenCLImageAccess : Attr {

View File

@ -1567,6 +1567,19 @@ for further details including limitations of the unroll hints.
}];
}
def OpenCLUnrollHintDocs : Documentation {
let Category = DocCatStmt;
let Heading = "__attribute__((opencl_unroll_hint))";
let Content = [{
The opencl_unroll_hint attribute qualifier can be used to specify that a loop
(for, while and do loops) can be unrolled. This attribute qualifier can be
used to specify full unrolling or partial unrolling by a specified amount.
This is a compiler hint and the compiler may ignore this directive. See
`OpenCL v2.0 <https://www.khronos.org/registry/cl/specs/opencl-2.0.pdf>`_
s6.11.5 for details.
}];
}
def DocOpenCLAddressSpaces : DocumentationCategory<"OpenCL Address Spaces"> {
let Content = [{
The address space qualifier may be used to specify the region of memory that is

View File

@ -902,6 +902,9 @@ def err_pragma_optimize_invalid_argument : Error<
def err_pragma_optimize_extra_argument : Error<
"unexpected extra argument '%0' to '#pragma clang optimize'">;
def err_opencl_unroll_hint_on_non_loop : Error<
"OpenCL only supports 'opencl_unroll_hint' attribute on for, while, and do statements">;
// OpenCL EXTENSION pragma (OpenCL 1.1 [9.1])
def warn_pragma_expected_colon : Warning<
"missing ':' after %0 - ignoring">, InGroup<IgnoredPragmas>;

View File

@ -2123,6 +2123,10 @@ def err_attribute_too_few_arguments : Error<
def err_attribute_invalid_vector_type : Error<"invalid vector element type %0">;
def err_attribute_bad_neon_vector_size : Error<
"Neon vector size must be 64 or 128 bits">;
def err_attribute_requires_positive_integer : Error<
"%0 attribute requires a positive integral compile time constant expression">;
def err_attribute_requires_opencl_version : Error<
"%0 attribute requires OpenCL version %1 or above">;
def warn_unsupported_target_attribute
: Warning<"Ignoring unsupported '%0' in the target attribute string">,
InGroup<IgnoredAttributes>;

View File

@ -2198,8 +2198,19 @@ private:
SourceLocation SkipExtendedMicrosoftTypeAttributes();
void ParseMicrosoftInheritanceClassAttributes(ParsedAttributes &attrs);
void ParseBorlandTypeAttributes(ParsedAttributes &attrs);
void ParseOpenCLAttributes(ParsedAttributes &attrs);
void ParseOpenCLKernelAttributes(ParsedAttributes &attrs);
void ParseOpenCLQualifiers(ParsedAttributes &Attrs);
/// \brief Parses opencl_unroll_hint attribute if language is OpenCL v2.0
/// or higher.
/// \return false if error happens.
bool MaybeParseOpenCLUnrollHintAttribute(ParsedAttributes &Attrs) {
if (getLangOpts().OpenCL)
return ParseOpenCLUnrollHintAttribute(Attrs);
return true;
}
/// \brief Parses opencl_unroll_hint attribute.
/// \return false if error happens.
bool ParseOpenCLUnrollHintAttribute(ParsedAttributes &Attrs);
void ParseNullabilityTypeSpecifiers(ParsedAttributes &attrs);
VersionTuple ParseVersionTuple(SourceRange &Range);

View File

@ -115,20 +115,41 @@ void LoopInfoStack::push(BasicBlock *Header, clang::ASTContext &Ctx,
// Identify loop hint attributes from Attrs.
for (const auto *Attr : Attrs) {
const LoopHintAttr *LH = dyn_cast<LoopHintAttr>(Attr);
const OpenCLUnrollHintAttr *OpenCLHint =
dyn_cast<OpenCLUnrollHintAttr>(Attr);
// Skip non loop hint attributes
if (!LH)
if (!LH && !OpenCLHint) {
continue;
auto *ValueExpr = LH->getValue();
unsigned ValueInt = 1;
if (ValueExpr) {
llvm::APSInt ValueAPS = ValueExpr->EvaluateKnownConstInt(Ctx);
ValueInt = ValueAPS.getSExtValue();
}
LoopHintAttr::OptionType Option = LH->getOption();
LoopHintAttr::LoopHintState State = LH->getState();
LoopHintAttr::OptionType Option = LoopHintAttr::Unroll;
LoopHintAttr::LoopHintState State = LoopHintAttr::Disable;
unsigned ValueInt = 1;
// Translate opencl_unroll_hint attribute argument to
// equivalent LoopHintAttr enums.
// OpenCL v2.0 s6.11.5:
// 0 - full unroll (no argument).
// 1 - disable unroll.
// other positive integer n - unroll by n.
if (OpenCLHint) {
ValueInt = OpenCLHint->getUnrollHint();
if (ValueInt == 0) {
State = LoopHintAttr::Full;
} else if (ValueInt != 1) {
Option = LoopHintAttr::UnrollCount;
State = LoopHintAttr::Numeric;
}
} else if (LH) {
auto *ValueExpr = LH->getValue();
if (ValueExpr) {
llvm::APSInt ValueAPS = ValueExpr->EvaluateKnownConstInt(Ctx);
ValueInt = ValueAPS.getSExtValue();
}
Option = LH->getOption();
State = LH->getState();
}
switch (State) {
case LoopHintAttr::Disable:
switch (Option) {

View File

@ -670,7 +670,7 @@ void Parser::ParseBorlandTypeAttributes(ParsedAttributes &attrs) {
}
}
void Parser::ParseOpenCLAttributes(ParsedAttributes &attrs) {
void Parser::ParseOpenCLKernelAttributes(ParsedAttributes &attrs) {
// Treat these like attributes
while (Tok.is(tok::kw___kernel)) {
IdentifierInfo *AttrName = Tok.getIdentifierInfo();
@ -3098,7 +3098,7 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS,
// OpenCL single token adornments.
case tok::kw___kernel:
ParseOpenCLAttributes(DS.getAttributes());
ParseOpenCLKernelAttributes(DS.getAttributes());
continue;
// Nullability type specifiers.

View File

@ -107,6 +107,8 @@ Parser::ParseStatementOrDeclaration(StmtVector &Stmts,
ParsedAttributesWithRange Attrs(AttrFactory);
MaybeParseCXX11Attributes(Attrs, nullptr, /*MightBeObjCMessageSend*/ true);
if (!MaybeParseOpenCLUnrollHintAttribute(Attrs))
return StmtError();
StmtResult Res = ParseStatementOrDeclarationAfterAttributes(
Stmts, Allowed, TrailingElseLoc, Attrs);
@ -2208,3 +2210,19 @@ void Parser::ParseMicrosoftIfExistsStatement(StmtVector &Stmts) {
}
Braces.consumeClose();
}
bool Parser::ParseOpenCLUnrollHintAttribute(ParsedAttributes &Attrs) {
MaybeParseGNUAttributes(Attrs);
if (Attrs.empty())
return true;
if (Attrs.getList()->getKind() != AttributeList::AT_OpenCLUnrollHint)
return true;
if (!(Tok.is(tok::kw_for) || Tok.is(tok::kw_while) || Tok.is(tok::kw_do))) {
Diag(Tok, diag::err_opencl_unroll_hint_on_non_loop);
return false;
}
return true;
}

View File

@ -203,6 +203,52 @@ CheckForIncompatibleAttributes(Sema &S,
}
}
static Attr *handleOpenCLUnrollHint(Sema &S, Stmt *St, const AttributeList &A,
SourceRange Range) {
// OpenCL v2.0 s6.11.5 - opencl_unroll_hint can have 0 arguments (compiler
// determines unrolling factor) or 1 argument (the unroll factor provided
// by the user).
if (S.getLangOpts().OpenCLVersion < 200) {
S.Diag(A.getLoc(), diag::err_attribute_requires_opencl_version)
<< A.getName() << "2.0";
return nullptr;
}
unsigned NumArgs = A.getNumArgs();
if (NumArgs > 1) {
S.Diag(A.getLoc(), diag::err_attribute_too_many_arguments) << A.getName()
<< 1;
return nullptr;
}
unsigned UnrollFactor = 0;
if (NumArgs == 1) {
Expr *E = A.getArgAsExpr(0);
llvm::APSInt ArgVal(32);
if (!E->isIntegerConstantExpr(ArgVal, S.Context)) {
S.Diag(A.getLoc(), diag::err_attribute_argument_type)
<< A.getName() << AANT_ArgumentIntegerConstant << E->getSourceRange();
return nullptr;
}
int Val = ArgVal.getSExtValue();
if (Val <= 0) {
S.Diag(A.getRange().getBegin(),
diag::err_attribute_requires_positive_integer)
<< A.getName();
return nullptr;
}
UnrollFactor = Val;
}
return OpenCLUnrollHintAttr::CreateImplicit(S.Context, UnrollFactor);
}
static Attr *ProcessStmtAttribute(Sema &S, Stmt *St, const AttributeList &A,
SourceRange Range) {
switch (A.getKind()) {
@ -215,6 +261,8 @@ static Attr *ProcessStmtAttribute(Sema &S, Stmt *St, const AttributeList &A,
return handleFallThroughAttr(S, St, A, Range);
case AttributeList::AT_LoopHint:
return handleLoopHintAttr(S, St, A, Range);
case AttributeList::AT_OpenCLUnrollHint:
return handleOpenCLUnrollHint(S, St, A, Range);
default:
// if we're here, then we parsed a known attribute, but didn't recognize
// it as a statement attribute => it is declaration attribute

View File

@ -0,0 +1,96 @@
// RUN: %clang_cc1 -emit-llvm -O0 -cl-std=CL2.0 -o - %s | FileCheck %s
/*** for ***/
void for_count()
{
// CHECK-LABEL: for_count
__attribute__((opencl_unroll_hint(8)))
for( int i = 0; i < 1000; ++i);
// CHECK: br label %{{.*}}, !llvm.loop ![[FOR_COUNT:.*]]
}
void for_disable()
{
// CHECK-LABEL: for_disable
__attribute__((opencl_unroll_hint(1)))
for( int i = 0; i < 1000; ++i);
// CHECK: br label %{{.*}}, !llvm.loop ![[FOR_DISABLE:.*]]
}
void for_full()
{
// CHECK-LABEL: for_full
__attribute__((opencl_unroll_hint))
for( int i = 0; i < 1000; ++i);
// CHECK: br label %{{.*}}, !llvm.loop ![[FOR_FULL:.*]]
}
/*** while ***/
void while_count()
{
// CHECK-LABEL: while_count
int i = 1000;
__attribute__((opencl_unroll_hint(8)))
while(i-->0);
// CHECK: br label %{{.*}}, !llvm.loop ![[WHILE_COUNT:.*]]
}
void while_disable()
{
// CHECK-LABEL: while_disable
int i = 1000;
__attribute__((opencl_unroll_hint(1)))
while(i-->0);
// CHECK: br label %{{.*}}, !llvm.loop ![[WHILE_DISABLE:.*]]
}
void while_full()
{
// CHECK-LABEL: while_full
int i = 1000;
__attribute__((opencl_unroll_hint))
while(i-->0);
// CHECK: br label %{{.*}}, !llvm.loop ![[WHILE_FULL:.*]]
}
/*** do ***/
void do_count()
{
// CHECK-LABEL: do_count
int i = 1000;
__attribute__((opencl_unroll_hint(8)))
do {} while(i--> 0);
// CHECK: br i1 %{{.*}}, label %{{.*}}, label %{{.*}}, !llvm.loop ![[DO_COUNT:.*]]
}
void do_disable()
{
// CHECK-LABEL: do_disable
int i = 1000;
__attribute__((opencl_unroll_hint(1)))
do {} while(i--> 0);
// CHECK: br i1 %{{.*}}, label %{{.*}}, label %{{.*}}, !llvm.loop ![[DO_DISABLE:.*]]
}
void do_full()
{
// CHECK-LABEL: do_full
int i = 1000;
__attribute__((opencl_unroll_hint))
do {} while(i--> 0);
// CHECK: br i1 %{{.*}}, label %{{.*}}, label %{{.*}}, !llvm.loop ![[DO_FULL:.*]]
}
// CHECK: ![[FOR_COUNT]] = distinct !{![[FOR_COUNT]], ![[COUNT:.*]]}
// CHECK: ![[COUNT]] = !{!"llvm.loop.unroll.count", i32 8}
// CHECK: ![[FOR_DISABLE]] = distinct !{![[FOR_DISABLE]], ![[DISABLE:.*]]}
// CHECK: ![[DISABLE]] = !{!"llvm.loop.unroll.disable"}
// CHECK: ![[FOR_FULL]] = distinct !{![[FOR_FULL]], ![[FULL:.*]]}
// CHECK: ![[FULL]] = !{!"llvm.loop.unroll.full"}
// CHECK: ![[WHILE_COUNT]] = distinct !{![[WHILE_COUNT]], ![[COUNT]]}
// CHECK: ![[WHILE_DISABLE]] = distinct !{![[WHILE_DISABLE]], ![[DISABLE]]}
// CHECK: ![[WHILE_FULL]] = distinct !{![[WHILE_FULL]], ![[FULL]]}
// CHECK: ![[DO_COUNT]] = distinct !{![[DO_COUNT]], ![[COUNT]]}
// CHECK: ![[DO_DISABLE]] = distinct !{![[DO_DISABLE]], ![[DISABLE]]}
// CHECK: ![[DO_FULL]] = distinct !{![[DO_FULL]], ![[FULL]]}

View File

@ -0,0 +1,8 @@
//RUN: %clang_cc1 -O0 -cl-std=CL2.0 -fsyntax-only -verify %s
kernel void B (global int *x) {
__attribute__((opencl_unroll_hint(42)))
if (x[0]) // expected-error {{OpenCL only supports 'opencl_unroll_hint' attribute on for, while, and do statements}}
x[0] = 15;
}

View File

@ -0,0 +1,30 @@
//RUN: %clang_cc1 -O0 -fsyntax-only -verify %s
//RUN: %clang_cc1 -O0 -cl-std=CL2.0 -fsyntax-only -verify -DCL20 %s
kernel void D (global int *x) {
int i = 10;
#ifndef CL20
// expected-error@+2 {{'opencl_unroll_hint' attribute requires OpenCL version 2.0 or above}}
#endif
__attribute__((opencl_unroll_hint))
do {
} while(i--);
}
#ifdef CL20
kernel void C (global int *x) {
int I = 3;
__attribute__((opencl_unroll_hint(I))) // expected-error {{'opencl_unroll_hint' attribute requires an integer constant}}
while (I--);
}
kernel void E() {
__attribute__((opencl_unroll_hint(2,4))) // expected-error {{'opencl_unroll_hint' attribute takes no more than 1 argument}}
for(int i=0; i<100; i++);
}
kernel void F() {
__attribute__((opencl_unroll_hint(-1))) // expected-error {{'opencl_unroll_hint' attribute requires a positive integral compile time constant expression}}
for(int i=0; i<100; i++);
}
#endif