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 { 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 v2.0 `_ +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 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; Index: include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- include/clang/Basic/DiagnosticSemaKinds.td +++ include/clang/Basic/DiagnosticSemaKinds.td @@ -2117,6 +2117,10 @@ 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; Index: include/clang/Parse/Parser.h =================================================================== --- include/clang/Parse/Parser.h +++ include/clang/Parse/Parser.h @@ -2195,8 +2195,19 @@ 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); Index: lib/CodeGen/CGLoopInfo.cpp =================================================================== --- lib/CodeGen/CGLoopInfo.cpp +++ lib/CodeGen/CGLoopInfo.cpp @@ -115,20 +115,41 @@ // 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(); - } + // 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(); + } - 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/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(); @@ -3084,7 +3084,7 @@ // OpenCL single token adornments. case tok::kw___kernel: - ParseOpenCLAttributes(DS.getAttributes()); + ParseOpenCLKernelAttributes(DS.getAttributes()); continue; // Nullability type specifiers. 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); @@ -2208,3 +2210,19 @@ } 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; +} Index: lib/Sema/SemaStmtAttr.cpp =================================================================== --- lib/Sema/SemaStmtAttr.cpp +++ lib/Sema/SemaStmtAttr.cpp @@ -203,6 +203,52 @@ } } +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 @@ 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,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]]} 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/SemaOpenCL/unroll-hint.cl =================================================================== --- /dev/null +++ test/SemaOpenCL/unroll-hint.cl @@ -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