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 { 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; 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/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: 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(Attr); + const OpenCLUnrollHintAttr *OpenCLHint + = dyn_cast(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: 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/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: 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: 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++); +}