yaxunl updated this revision to Diff 46526.
yaxunl added a comment.
update the tests.
http://reviews.llvm.org/D16686
Files:
include/clang/Basic/Attr.td
include/clang/Basic/AttrDocs.td
include/clang/Basic/DiagnosticParseKinds.td
include/clang/Basic/DiagnosticSemaKinds.td
include/clang/Parse/Parser.h
lib/CodeGen/CGLoopInfo.cpp
lib/Parse/ParseDecl.cpp
lib/Parse/ParseStmt.cpp
lib/Sema/SemaStmtAttr.cpp
test/CodeGenOpenCL/unroll-hint.cl
test/Parser/opencl-unroll-hint.cl
test/SemaOpenCL/unroll-hint.cl
Index: test/SemaOpenCL/unroll-hint.cl
===================================================================
--- /dev/null
+++ test/SemaOpenCL/unroll-hint.cl
@@ -0,0 +1,19 @@
+//RUN: %clang_cc1 -O0 -cl-std=CL2.0 -fsyntax-only -verify %s
+
+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 D (global int *x) {
+ int i = 10;
+ __attribute__((opencl_unroll_hint))
+ do {
+ } while(i--);
+}
+
+kernel void E() {
+ __attribute__((opencl_unroll_hint(2,4))) // expected-error {{1 attribute takes no more than 1 argument}}
+ for(int i=0; i<100; i++);
+}
Index: test/Parser/opencl-unroll-hint.cl
===================================================================
--- /dev/null
+++ test/Parser/opencl-unroll-hint.cl
@@ -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;
+}
+
Index: test/CodeGenOpenCL/unroll-hint.cl
===================================================================
--- /dev/null
+++ test/CodeGenOpenCL/unroll-hint.cl
@@ -0,0 +1,114 @@
+// RUN: %clang_cc1 -emit-llvm -O0 -cl-std=CL2.0 -o - %s | FileCheck %s
+
+/*** for ***/
+void for_count(int* sum)
+{
+// CHECK-LABEL: for_count
+ __attribute__((opencl_unroll_hint(8)))
+ for( int i = 0; i < 1000; ++i) {
+ *sum += i;
+ }
+// CHECK: br label %{{.*}}, !llvm.loop ![[FOR_COUNT:.*]]
+}
+
+void for_disable(int* sum)
+{
+// CHECK-LABEL: for_disable
+ __attribute__((opencl_unroll_hint(1)))
+ for( int i = 0; i < 1000; ++i) {
+ *sum += i;
+ }
+// CHECK: br label %{{.*}}, !llvm.loop ![[FOR_DISABLE:.*]]
+}
+
+void for_full(int* sum)
+{
+// CHECK-LABEL: for_full
+ __attribute__((opencl_unroll_hint))
+ for( int i = 0; i < 1000; ++i) {
+ *sum += i;
+ }
+// CHECK: br label %{{.*}}, !llvm.loop ![[FOR_FULL:.*]]
+}
+
+/*** while ***/
+void while_count(int* sum)
+{
+// CHECK-LABEL: while_count
+ int i = 1000;
+ __attribute__((opencl_unroll_hint(8)))
+ while(i-->0) {
+ *sum += i;
+ }
+// CHECK: br label %{{.*}}, !llvm.loop ![[WHILE_COUNT:.*]]
+}
+
+void while_disable(int* sum)
+{
+// CHECK-LABEL: while_disable
+ int i = 1000;
+ __attribute__((opencl_unroll_hint(1)))
+ while(i-->0) {
+ *sum += i;
+ }
+// CHECK: br label %{{.*}}, !llvm.loop ![[WHILE_DISABLE:.*]]
+}
+
+void while_full(int* sum)
+{
+// CHECK-LABEL: while_full
+ int i = 1000;
+ __attribute__((opencl_unroll_hint))
+ while(i-->0) {
+ *sum += i;
+ }
+// CHECK: br label %{{.*}}, !llvm.loop ![[WHILE_FULL:.*]]
+}
+
+/*** do ***/
+void do_count(int* sum)
+{
+// CHECK-LABEL: do_count
+ int i = 1000;
+ __attribute__((opencl_unroll_hint(8)))
+ do {
+ *sum += i;
+ } while(i--> 0);
+// CHECK: br i1 %{{.*}}, label %{{.*}}, label %{{.*}}, !llvm.loop ![[DO_COUNT:.*]]
+}
+
+void do_disable(int* sum)
+{
+// CHECK-LABEL: do_disable
+ int i = 1000;
+ __attribute__((opencl_unroll_hint(1)))
+ do {
+ *sum += i;
+ } while(i--> 0);
+// CHECK: br i1 %{{.*}}, label %{{.*}}, label %{{.*}}, !llvm.loop ![[DO_DISABLE:.*]]
+}
+
+void do_full(int* sum)
+{
+// CHECK-LABEL: do_full
+ int i = 1000;
+ __attribute__((opencl_unroll_hint))
+ do {
+ *sum += i;
+ } 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]]}
Index: lib/Sema/SemaStmtAttr.cpp
===================================================================
--- lib/Sema/SemaStmtAttr.cpp
+++ lib/Sema/SemaStmtAttr.cpp
@@ -203,6 +203,47 @@
}
}
+static Attr *handleOpenCLUnrollHint(Sema &S, Stmt *St, const AttributeList &A,
+ SourceRange Range) {
+ assert(A.getKind() == AttributeList::AT_OpenCLUnrollHint);
+
+ // opencl_unroll_hint can have 0 arguments (compiler determines unrolling
+ // factor) or 1 argument (the unroll factor provided by the user).
+ // See OpenCL 2.0 spec section 6.11.5 for details.
+
+ unsigned NumArgs = A.getNumArgs();
+
+ if (NumArgs > 1) {
+ S.Diag(A.getLoc(), diag::err_attribute_too_many_arguments) << 1;
+ return 0;
+ }
+
+ unsigned unrollingFactor = 0;
+
+ if (NumArgs == 1) {
+ Expr *E = A.getArgAsExpr(0);
+ assert(E != nullptr && "Invalid opencl_unroll_hint argument");
+ llvm::APSInt ArgVal(32);
+
+ if (!E->isIntegerConstantExpr(ArgVal, S.Context)) {
+ S.Diag(A.getLoc(), diag::err_attribute_argument_type)
+ << A.getName()->getName() << AANT_ArgumentIntegerConstant
+ << E->getSourceRange();
+ return 0;
+ }
+
+ int64_t val = ArgVal.getSExtValue();
+
+ if (val <= 0) {
+ S.Diag(A.getRange().getBegin(), diag::err_opencl_unroll_hint_factor);
+ return 0;
+ }
+ unrollingFactor = val;
+ }
+
+ return OpenCLUnrollHintAttr::CreateImplicit(S.Context, unrollingFactor);
+}
+
static Attr *ProcessStmtAttribute(Sema &S, Stmt *St, const AttributeList &A,
SourceRange Range) {
switch (A.getKind()) {
@@ -215,6 +256,8 @@
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
Index: lib/Parse/ParseStmt.cpp
===================================================================
--- lib/Parse/ParseStmt.cpp
+++ lib/Parse/ParseStmt.cpp
@@ -107,6 +107,8 @@
ParsedAttributesWithRange Attrs(AttrFactory);
MaybeParseCXX11Attributes(Attrs, nullptr, /*MightBeObjCMessageSend*/ true);
+ if (!MaybeParseOpenCLUnrollHintAttribute(Attrs))
+ return StmtError();
StmtResult Res = ParseStatementOrDeclarationAfterAttributes(
Stmts, Allowed, TrailingElseLoc, Attrs);
@@ -2206,3 +2208,19 @@
}
Braces.consumeClose();
}
+
+bool Parser::ParseOpenCLUnrollHintAttribute(ParsedAttributes &Attrs) {
+ MaybeParseGNUAttributes(Attrs);
+
+ if (Attrs.empty())
+ return true;
+
+ if (Attrs.getList()->getName()->getName() != "opencl_unroll_hint")
+ 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;
+}
Index: lib/Parse/ParseDecl.cpp
===================================================================
--- lib/Parse/ParseDecl.cpp
+++ lib/Parse/ParseDecl.cpp
@@ -670,7 +670,7 @@
}
}
-void Parser::ParseOpenCLAttributes(ParsedAttributes &attrs) {
+void Parser::ParseOpenCLKernelAttributes(ParsedAttributes &attrs) {
// Treat these like attributes
while (Tok.is(tok::kw___kernel)) {
IdentifierInfo *AttrName = Tok.getIdentifierInfo();
@@ -3079,7 +3079,7 @@
// OpenCL single token adornments.
case tok::kw___kernel:
- ParseOpenCLAttributes(DS.getAttributes());
+ ParseOpenCLKernelAttributes(DS.getAttributes());
continue;
// Nullability type specifiers.
Index: lib/CodeGen/CGLoopInfo.cpp
===================================================================
--- lib/CodeGen/CGLoopInfo.cpp
+++ lib/CodeGen/CGLoopInfo.cpp
@@ -115,20 +115,43 @@
// 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();
+ LoopHintAttr::OptionType Option = LoopHintAttr::Unroll;
+ LoopHintAttr::LoopHintState State = LoopHintAttr::Disable;
unsigned ValueInt = 1;
- if (ValueExpr) {
- llvm::APSInt ValueAPS = ValueExpr->EvaluateKnownConstInt(Ctx);
- ValueInt = ValueAPS.getSExtValue();
- }
+ // Translate opencl_unroll_hint attribute argument to
+ // equivalent LoopHintAttr enums.
+ // See OpenCL 2.0 spec section 6.11.5 for details.
+ // 0 corresponds to opencl_unroll_hint attribute without
+ // argument.
+ // 0 - full unroll
+ // 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();
+ }
- LoopHintAttr::OptionType Option = LH->getOption();
- LoopHintAttr::LoopHintState State = LH->getState();
+ Option = LH->getOption();
+ State = LH->getState();
+ }
switch (State) {
case LoopHintAttr::Disable:
switch (Option) {
Index: include/clang/Parse/Parser.h
===================================================================
--- include/clang/Parse/Parser.h
+++ include/clang/Parse/Parser.h
@@ -2195,8 +2195,18 @@
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 2.0+.
+ /// \return false if error happens.
+ bool MaybeParseOpenCLUnrollHintAttribute(ParsedAttributes &Attrs) {
+ if (getLangOpts().OpenCL && getLangOpts().OpenCLVersion >= 200)
+ 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);
Index: include/clang/Basic/DiagnosticSemaKinds.td
===================================================================
--- include/clang/Basic/DiagnosticSemaKinds.td
+++ include/clang/Basic/DiagnosticSemaKinds.td
@@ -7657,6 +7657,8 @@
"the event_t type can only be used with __private address space qualifier">;
def err_expected_kernel_void_return_type : Error<
"kernel must have void return type">;
+def err_opencl_unroll_hint_factor : Error<
+ "opencl_unroll_hint attribute requires a positive integral compile time constant expression">;
def err_sampler_argument_required : Error<
"sampler_t variable required - got %0">;
def err_wrong_sampler_addressspace: Error<
Index: include/clang/Basic/DiagnosticParseKinds.td
===================================================================
--- include/clang/Basic/DiagnosticParseKinds.td
+++ include/clang/Basic/DiagnosticParseKinds.td
@@ -902,6 +902,9 @@
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>;
Index: include/clang/Basic/AttrDocs.td
===================================================================
--- include/clang/Basic/AttrDocs.td
+++ include/clang/Basic/AttrDocs.td
@@ -1562,6 +1562,19 @@
}];
}
+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 2.0 spec <https://www.khronos.org/registry/cl/specs/opencl-2.0.pdf>`_
+section 6.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
Index: include/clang/Basic/Attr.td
===================================================================
--- include/clang/Basic/Attr.td
+++ include/clang/Basic/Attr.td
@@ -655,6 +655,12 @@
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 {
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits