yaxunl created this revision.
yaxunl added reviewers: Anastasia, pxli168, pekka.jaaskelainen.
yaxunl added subscribers: tstellarAMD, cfe-commits.
Add codegen and diagnostics for opencl_unroll_hint attribute.
The code is based on Khronos OpenCL compiler
(https://github.com/KhronosGroup/SPIR/tree/spirv-1.0).
http://reviews.llvm.org/D16686
Files:
include/clang/Basic/Attr.td
include/clang/Basic/DiagnosticParseKinds.td
include/clang/Basic/DiagnosticSemaKinds.td
include/clang/Parse/Parser.h
lib/CodeGen/CGLoopInfo.cpp
lib/Parse/ParseStmt.cpp
lib/Sema/SemaStmtAttr.cpp
test/CodeGenOpenCL/unroll-hint.cl
test/Parser/opencl-unroll-hint.cl
Index: test/Parser/opencl-unroll-hint.cl
===================================================================
--- /dev/null
+++ test/Parser/opencl-unroll-hint.cl
@@ -0,0 +1,31 @@
+//RUN: %clang_cc1 -O0 -cl-std=CL2.0 -fsyntax-only -verify %s
+
+extern int counter();
+
+kernel void B (global int *x) {
+ __attribute__((opencl_unroll_hint(42)))
+ if (x[0]) // expected-error {{OpenCL only supports opencl_unroll_hint on for, while, and do statements}}
+ x[0] = 15;
+}
+
+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--)
+ x[I] = I;
+}
+
+kernel void D (global int *x) {
+ int i;
+
+ __attribute__((opencl_unroll_hint))
+ do {
+ i = counter();
+ x[i] = i;
+ } while(i);
+}
+
+kernel void E() {
+ __attribute__((opencl_unroll_hint(-2))) // expected-error {{opencl_unroll_hint requires a positive integral compile time constant expression}}
+ for(int i=0; i<100; i++);
+}
Index: test/CodeGenOpenCL/unroll-hint.cl
===================================================================
--- /dev/null
+++ test/CodeGenOpenCL/unroll-hint.cl
@@ -0,0 +1,115 @@
+// RUN: %clang_cc1 -emit-llvm -O0 -cl-std=CL2.0 -o - %s | FileCheck %s
+// RUN: %clang_cc1 -emit-llvm -O0 -cl-std=CL1.2 -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,46 @@
}
}
+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).
+
+ 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 != NULL);
+ llvm::APSInt ArgVal(32);
+
+ if (E->isTypeDependent() || E->isValueDependent() ||
+ !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 +255,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
@@ -108,6 +108,22 @@
ParsedAttributesWithRange Attrs(AttrFactory);
MaybeParseCXX11Attributes(Attrs, nullptr, /*MightBeObjCMessageSend*/ true);
+ if (getLangOpts().OpenCL && getLangOpts().OpenCLVersion >= 120) {
+ bool wasAttribute = Tok.is(tok::kw___attribute);
+ MaybeParseGNUAttributes(Attrs);
+
+ if (!Tok.is(tok::kw___attribute) && wasAttribute) {
+ if (!(Tok.is(tok::kw_for) || Tok.is(tok::kw_while) || Tok.is(tok::kw_do))) {
+ AttributeList *attrList = Attrs.getList();
+ assert(attrList != NULL);
+ if (attrList->getName()->getName() == "opencl_unroll_hint") {
+ Diag(Tok, diag::err_opencl_unroll_hint_on_non_loop);
+ return StmtError();
+ }
+ }
+ }
+ }
+
StmtResult Res = ParseStatementOrDeclarationAfterAttributes(
Stmts, Allowed, TrailingElseLoc, Attrs);
Index: lib/CodeGen/CGLoopInfo.cpp
===================================================================
--- lib/CodeGen/CGLoopInfo.cpp
+++ lib/CodeGen/CGLoopInfo.cpp
@@ -115,20 +115,35 @@
// 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();
- }
+ 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
@@ -2232,6 +2232,13 @@
SourceLocation ScopeLoc,
AttributeList::Syntax Syntax);
+ void ParseOpenCLUnrollHintAttribute(IdentifierInfo *AttrName,
+ SourceLocation AttrNameLoc,
+ ParsedAttributes &Attrs,
+ SourceLocation *EndLoc,
+ IdentifierInfo *ScopeName,
+ SourceLocation ScopeLoc);
+
void ParseTypeofSpecifier(DeclSpec &DS);
SourceLocation ParseDecltypeSpecifier(DeclSpec &DS);
void AnnotateExistingDecltypeSpecifier(const DeclSpec &DS,
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 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 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/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 = [Undocumented];
+}
+
// 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