+------------------------------+--------------------------------------------------------------+--------------------------+-----------------------------------------------------------------------+
| atomic extension | 'fail' clause on atomic construct | :none:`unclaimed` | |
+------------------------------+--------------------------------------------------------------+--------------------------+-----------------------------------------------------------------------+
-| base language | C++ attribute specifier syntax | :part:`worked on` | |
+| base language | C++ attribute specifier syntax | :good:`done` | D105648 |
+------------------------------+--------------------------------------------------------------+--------------------------+-----------------------------------------------------------------------+
| device extension | 'present' map type modifier | :good:`done` | D83061, D83062, D84422 |
+------------------------------+--------------------------------------------------------------+--------------------------+-----------------------------------------------------------------------+
def OpenMPLoopForm : DiagGroup<"openmp-loop-form">;
def OpenMPMapping : DiagGroup<"openmp-mapping">;
def OpenMPTarget : DiagGroup<"openmp-target", [OpenMPMapping]>;
+def OpenMPPre51Compat : DiagGroup<"pre-openmp-51-compat">;
+def OpenMP51Ext : DiagGroup<"openmp-51-extensions">;
def OpenMP : DiagGroup<"openmp", [
- SourceUsesOpenMP, OpenMPClauses, OpenMPLoopForm, OpenMPTarget, OpenMPMapping
+ SourceUsesOpenMP, OpenMPClauses, OpenMPLoopForm, OpenMPTarget,
+ OpenMPMapping, OpenMP51Ext
]>;
// Backend warnings.
def warn_omp_more_one_interop_type
: Warning<"interop type '%0' cannot be specified more than once">,
InGroup<OpenMPClauses>;
+def err_expected_sequence_or_directive : Error<
+ "expected an OpenMP 'directive' or 'sequence' attribute argument">;
+def ext_omp_attributes : ExtWarn<
+ "specifying OpenMP directives with [[]] is an OpenMP 5.1 extension">,
+ InGroup<OpenMP51Ext>;
+def warn_omp51_compat_attributes : Warning<
+ "specifying OpenMP directives with [[]] is incompatible with OpenMP "
+ "standards before OpenMP 5.1">,
+ InGroup<OpenMPPre51Compat>, DefaultIgnore;
// Pragma loop support.
def err_pragma_loop_missing_argument : Error<
PRAGMA_ANNOTATION(pragma_opencl_extension)
// Annotations for OpenMP pragma directives - #pragma omp ...
+// The parser produces this annotation token when it parses an [[omp::*]]
+// attribute. The tokens from the attribute argument list are replayed to the
+// token stream with this leading token (and a trailing pragma_openmp_end) so
+// that the parser can reuse the OpenMP parsing logic but still be able to
+// distinguish between a real pragma and a converted pragma. It is not marked
+// as a PRAGMA_ANNOTATION because it doesn't get generated from a #pragma.
+ANNOTATION(attr_openmp)
// The lexer produces these so that they only take effect when the parser
// handles #pragma omp ... directives.
PRAGMA_ANNOTATION(pragma_openmp)
IdentifierInfo *ScopeName, SourceLocation ScopeLoc,
ParsedAttr::Syntax Syntax);
+ void ReplayOpenMPAttributeTokens(CachedTokens &OpenMPTokens) {
+ // If parsing the attributes found an OpenMP directive, emit those tokens
+ // to the parse stream now.
+ if (!OpenMPTokens.empty()) {
+ PP.EnterToken(Tok, /*IsReinject*/ true);
+ PP.EnterTokenStream(OpenMPTokens, /*DisableMacroExpansion*/ true,
+ /*IsReinject*/ true);
+ ConsumeAnyToken(/*ConsumeCodeCompletionTok*/ true);
+ }
+ }
void MaybeParseCXX11Attributes(Declarator &D) {
if (standardAttributesAllowed() && isCXX11AttributeSpecifier()) {
ParsedAttributesWithRange attrs(AttrFactory);
return false;
}
- void ParseCXX11AttributeSpecifier(ParsedAttributes &attrs,
- SourceLocation *EndLoc = nullptr);
+ void ParseOpenMPAttributeArgs(IdentifierInfo *AttrName,
+ CachedTokens &OpenMPTokens);
+
+ void ParseCXX11AttributeSpecifierInternal(ParsedAttributes &Attrs,
+ CachedTokens &OpenMPTokens,
+ SourceLocation *EndLoc = nullptr);
+ void ParseCXX11AttributeSpecifier(ParsedAttributes &Attrs,
+ SourceLocation *EndLoc = nullptr) {
+ CachedTokens OpenMPTokens;
+ ParseCXX11AttributeSpecifierInternal(Attrs, OpenMPTokens, EndLoc);
+ ReplayOpenMPAttributeTokens(OpenMPTokens);
+ }
void ParseCXX11Attributes(ParsedAttributesWithRange &attrs,
SourceLocation *EndLoc = nullptr);
/// Parses a C++11 (or C2x)-style attribute argument list. Returns true
SourceLocation AttrNameLoc,
ParsedAttributes &Attrs, SourceLocation *EndLoc,
IdentifierInfo *ScopeName,
- SourceLocation ScopeLoc);
+ SourceLocation ScopeLoc,
+ CachedTokens &OpenMPTokens);
IdentifierInfo *TryParseCXX11AttributeIdentifier(SourceLocation &Loc);
else if (ScopeName == "_Clang")
ScopeName = "clang";
+ // As a special case, look for the omp::sequence and omp::directive
+ // attributes. We support those, but not through the typical attribute
+ // machinery that goes through TableGen. We support this in all OpenMP modes
+ // so long as double square brackets are enabled.
+ if (LangOpts.OpenMP && LangOpts.DoubleSquareBracketAttributes &&
+ ScopeName == "omp")
+ return (Name == "directive" || Name == "sequence") ? 1 : 0;
+
#include "clang/Basic/AttrHasAttributeImpl.inc"
return 0;
ConsumeAnyToken(/*ConsumeCodeCompletionTok=*/true);
assert(Tok.isAnnotation() && "Expected annotation token.");
switch (Tok.getKind()) {
+ case tok::annot_attr_openmp:
case tok::annot_pragma_openmp: {
AccessSpecifier AS = LP.getAccessSpecifier();
ParsedAttributesWithRange Attrs(AttrFactory);
continue;
}
- if (Tok.is(tok::annot_pragma_openmp)) {
+ if (Tok.isOneOf(tok::annot_pragma_openmp, tok::annot_attr_openmp)) {
// Result can be ignored, because it must be always empty.
AccessSpecifier AS = AS_none;
ParsedAttributesWithRange Attrs(AttrFactory);
ParsedAttributesViewWithRange FnAttrs;
// Optional C++11 attribute-specifier
MaybeParseCXX11Attributes(attrs);
+
+ // The next token may be an OpenMP pragma annotation token. That would
+ // normally be handled from ParseCXXClassMemberDeclarationWithPragmas, but in
+ // this case, it came from an *attribute* rather than a pragma. Handle it now.
+ if (Tok.is(tok::annot_attr_openmp))
+ return ParseOpenMPDeclarativeDirectiveWithExtDecl(AS, attrs);
+
// We need to keep these attributes for future diagnostic
// before they are taken over by declaration specifier.
FnAttrs.addAll(attrs.begin(), attrs.end());
return nullptr;
}
+ case tok::annot_attr_openmp:
case tok::annot_pragma_openmp:
return ParseOpenMPDeclarativeDirectiveWithExtDecl(
AS, AccessAttrs, /*Delayed=*/true, TagType, TagDecl);
}
}
+void Parser::ParseOpenMPAttributeArgs(IdentifierInfo *AttrName,
+ CachedTokens &OpenMPTokens) {
+ // Both 'sequence' and 'directive' attributes require arguments, so parse the
+ // open paren for the argument list.
+ BalancedDelimiterTracker T(*this, tok::l_paren);
+ if (T.consumeOpen()) {
+ Diag(Tok, diag::err_expected) << tok::l_paren;
+ return;
+ }
+
+ if (AttrName->isStr("directive")) {
+ // If the attribute is named `directive`, we can consume its argument list
+ // and push the tokens from it into the cached token stream for a new OpenMP
+ // pragma directive.
+ Token OMPBeginTok;
+ OMPBeginTok.startToken();
+ OMPBeginTok.setKind(tok::annot_attr_openmp);
+ OMPBeginTok.setLocation(Tok.getLocation());
+ OpenMPTokens.push_back(OMPBeginTok);
+
+ ConsumeAndStoreUntil(tok::r_paren, OpenMPTokens, /*StopAtSemi=*/false,
+ /*ConsumeFinalToken*/ false);
+ Token OMPEndTok;
+ OMPEndTok.startToken();
+ OMPEndTok.setKind(tok::annot_pragma_openmp_end);
+ OMPEndTok.setLocation(Tok.getLocation());
+ OpenMPTokens.push_back(OMPEndTok);
+ } else {
+ assert(AttrName->isStr("sequence") &&
+ "Expected either 'directive' or 'sequence'");
+ // If the attribute is named 'sequence', its argument is a list of one or
+ // more OpenMP attributes (either 'omp::directive' or 'omp::sequence',
+ // where the 'omp::' is optional).
+ do {
+ // We expect to see one of the following:
+ // * An identifier (omp) for the attribute namespace followed by ::
+ // * An identifier (directive) or an identifier (sequence).
+ SourceLocation IdentLoc;
+ IdentifierInfo *Ident = TryParseCXX11AttributeIdentifier(IdentLoc);
+
+ // If there is an identifier and it is 'omp', a double colon is required
+ // followed by the actual identifier we're after.
+ if (Ident && Ident->isStr("omp") && !ExpectAndConsume(tok::coloncolon))
+ Ident = TryParseCXX11AttributeIdentifier(IdentLoc);
+
+ // If we failed to find an identifier (scoped or otherwise), or we found
+ // an unexpected identifier, diagnose.
+ if (!Ident || (!Ident->isStr("directive") && !Ident->isStr("sequence"))) {
+ Diag(Tok.getLocation(), diag::err_expected_sequence_or_directive);
+ SkipUntil(tok::r_paren, StopBeforeMatch);
+ continue;
+ }
+ // We read an identifier. If the identifier is one of the ones we
+ // expected, we can recurse to parse the args.
+ ParseOpenMPAttributeArgs(Ident, OpenMPTokens);
+
+ // There may be a comma to signal that we expect another directive in the
+ // sequence.
+ } while (TryConsumeToken(tok::comma));
+ }
+ // Parse the closing paren for the argument list.
+ T.consumeClose();
+}
+
static bool IsBuiltInOrStandardCXX11Attribute(IdentifierInfo *AttrName,
IdentifierInfo *ScopeName) {
switch (
ParsedAttributes &Attrs,
SourceLocation *EndLoc,
IdentifierInfo *ScopeName,
- SourceLocation ScopeLoc) {
+ SourceLocation ScopeLoc,
+ CachedTokens &OpenMPTokens) {
assert(Tok.is(tok::l_paren) && "Not a C++11 attribute argument list");
SourceLocation LParenLoc = Tok.getLocation();
const LangOptions &LO = getLangOpts();
return true;
}
+ if (ScopeName && ScopeName->isStr("omp")) {
+ Diag(AttrNameLoc, getLangOpts().OpenMP >= 51
+ ? diag::warn_omp51_compat_attributes
+ : diag::ext_omp_attributes);
+
+ ParseOpenMPAttributeArgs(AttrName, OpenMPTokens);
+
+ // We claim that an attribute was parsed and added so that one is not
+ // created for us by the caller.
+ return true;
+ }
+
unsigned NumArgs;
// Some Clang-scoped attributes have some special parsing behavior.
if (ScopeName && (ScopeName->isStr("clang") || ScopeName->isStr("_Clang")))
///
/// [C++11] attribute-namespace:
/// identifier
-void Parser::ParseCXX11AttributeSpecifier(ParsedAttributes &attrs,
- SourceLocation *endLoc) {
+void Parser::ParseCXX11AttributeSpecifierInternal(ParsedAttributes &Attrs,
+ CachedTokens &OpenMPTokens,
+ SourceLocation *EndLoc) {
if (Tok.is(tok::kw_alignas)) {
Diag(Tok.getLocation(), diag::warn_cxx98_compat_alignas);
- ParseAlignmentSpecifier(attrs, endLoc);
+ ParseAlignmentSpecifier(Attrs, EndLoc);
return;
}
// Parse attribute arguments
if (Tok.is(tok::l_paren))
- AttrParsed = ParseCXX11AttributeArgs(AttrName, AttrLoc, attrs, endLoc,
- ScopeName, ScopeLoc);
+ AttrParsed = ParseCXX11AttributeArgs(AttrName, AttrLoc, Attrs, EndLoc,
+ ScopeName, ScopeLoc, OpenMPTokens);
if (!AttrParsed) {
- attrs.addNew(
+ Attrs.addNew(
AttrName,
SourceRange(ScopeLoc.isValid() ? ScopeLoc : AttrLoc, AttrLoc),
ScopeName, ScopeLoc, nullptr, 0,
SkipUntil(tok::r_square);
else if (Tok.is(tok::r_square))
checkCompoundToken(CloseLoc, tok::r_square, CompoundToken::AttrEnd);
- if (endLoc)
- *endLoc = Tok.getLocation();
+ if (EndLoc)
+ *EndLoc = Tok.getLocation();
if (ExpectAndConsume(tok::r_square))
SkipUntil(tok::r_square);
}
Parser::DeclGroupPtrTy Parser::ParseOpenMPDeclarativeDirectiveWithExtDecl(
AccessSpecifier &AS, ParsedAttributesWithRange &Attrs, bool Delayed,
DeclSpec::TST TagType, Decl *Tag) {
- assert(Tok.is(tok::annot_pragma_openmp) && "Not an OpenMP directive!");
+ assert(Tok.isOneOf(tok::annot_pragma_openmp, tok::annot_attr_openmp) &&
+ "Not an OpenMP directive!");
ParsingOpenMPDirectiveRAII DirScope(*this);
ParenBraceBracketBalancer BalancerRAIIObj(*this);
Toks.push_back(Tok);
while (Cnt && Tok.isNot(tok::eof)) {
(void)ConsumeAnyToken();
- if (Tok.is(tok::annot_pragma_openmp))
+ if (Tok.isOneOf(tok::annot_pragma_openmp, tok::annot_attr_openmp))
++Cnt;
else if (Tok.is(tok::annot_pragma_openmp_end))
--Cnt;
ConsumeAnyToken();
DeclGroupPtrTy Ptr;
- if (Tok.is(tok::annot_pragma_openmp)) {
+ if (Tok.isOneOf(tok::annot_pragma_openmp, tok::annot_attr_openmp)) {
Ptr = ParseOpenMPDeclarativeDirectiveWithExtDecl(AS, Attrs, Delayed,
TagType, Tag);
} else if (Tok.isNot(tok::r_brace) && !isEofOrEom()) {
///
StmtResult
Parser::ParseOpenMPDeclarativeOrExecutableDirective(ParsedStmtContext StmtCtx) {
- assert(Tok.is(tok::annot_pragma_openmp) && "Not an OpenMP directive!");
+ assert(Tok.isOneOf(tok::annot_pragma_openmp, tok::annot_attr_openmp) &&
+ "Not an OpenMP directive!");
ParsingOpenMPDirectiveRAII DirScope(*this);
ParenBraceBracketBalancer BalancerRAIIObj(*this);
SmallVector<OMPClause *, 5> Clauses;
return HandlePragmaCaptured();
case tok::annot_pragma_openmp:
+ // Prohibit attributes that are not OpenMP attributes, but only before
+ // processing a #pragma omp clause.
ProhibitAttributes(Attrs);
+ LLVM_FALLTHROUGH;
+ case tok::annot_attr_openmp:
+ // Do not prohibit attributes if they were OpenMP attributes.
return ParseOpenMPDeclarativeOrExecutableDirective(StmtCtx);
case tok::annot_pragma_ms_pointers_to_members:
return false;
case tok::annot_pragma_openmp:
+ case tok::annot_attr_openmp:
case tok::annot_pragma_openmp_end:
// Stop before an OpenMP pragma boundary.
if (OpenMPDirectiveParsing)
case tok::annot_pragma_opencl_extension:
HandlePragmaOpenCLExtension();
return nullptr;
+ case tok::annot_attr_openmp:
case tok::annot_pragma_openmp: {
AccessSpecifier AS = AS_none;
return ParseOpenMPDeclarativeDirectiveWithExtDecl(AS, attrs);
--- /dev/null
+// RUN: %clang_cc1 -verify -fopenmp -fopenmp-version=51 -triple x86_64-apple-darwin10.6.0 -emit-llvm -o - %s | FileCheck %s
+// RUN: %clang_cc1 -fopenmp -fopenmp-version=51 -triple x86_64-apple-darwin10.6.0 -x c++ -std=c++11 -emit-pch -o %t %s
+// RUN: %clang_cc1 -fopenmp -fopenmp-version=51 -triple x86_64-apple-darwin10.6.0 -std=c++11 -include-pch %t -verify %s -emit-llvm -o - | FileCheck %s
+// RUN: %clang_cc1 -verify -fopenmp -fopenmp-version=51 -triple x86_64-unknown-linux-gnu -emit-llvm -o - %s | FileCheck %s
+// RUN: %clang_cc1 -fopenmp -fopenmp-version=51 -fnoopenmp-use-tls -triple x86_64-unknown-linux-gnu -x c++ -std=c++11 -emit-pch -o %t %s
+// RUN: %clang_cc1 -fopenmp -fopenmp-version=51 -fnoopenmp-use-tls -triple x86_64-unknown-linux-gnu -std=c++11 -include-pch %t -verify %s -emit-llvm -o - | FileCheck %s
+
+// RUN: %clang_cc1 -verify -fopenmp-simd -fopenmp-version=51 -triple x86_64-apple-darwin10.6.0 -emit-llvm -o - %s | FileCheck --check-prefix SIMD-ONLY0 %s
+// RUN: %clang_cc1 -fopenmp-simd -fopenmp-version=51 -triple x86_64-apple-darwin10.6.0 -x c++ -std=c++11 -emit-pch -o %t %s
+// RUN: %clang_cc1 -fopenmp-simd -fopenmp-version=51 -triple x86_64-apple-darwin10.6.0 -std=c++11 -include-pch %t -verify %s -emit-llvm -o - | FileCheck --check-prefix SIMD-ONLY0 %s
+// RUN: %clang_cc1 -verify -fopenmp-simd -fopenmp-version=51 -triple x86_64-unknown-linux-gnu -emit-llvm -o - %s | FileCheck --check-prefix SIMD-ONLY0 %s
+// RUN: %clang_cc1 -fopenmp-simd -fopenmp-version=51 -fnoopenmp-use-tls -triple x86_64-unknown-linux-gnu -x c++ -std=c++11 -emit-pch -o %t %s
+// RUN: %clang_cc1 -fopenmp-simd -fopenmp-version=51 -fnoopenmp-use-tls -triple x86_64-unknown-linux-gnu -std=c++11 -include-pch %t -verify %s -emit-llvm -o - | FileCheck --check-prefix SIMD-ONLY0 %s
+// SIMD-ONLY0-NOT: {{__kmpc|__tgt}}
+// expected-no-diagnostics
+
+#ifndef HEADER
+#define HEADER
+
+enum omp_allocator_handle_t {
+ omp_null_allocator = 0,
+ omp_default_mem_alloc = 1,
+ omp_large_cap_mem_alloc = 2,
+ omp_const_mem_alloc = 3,
+ omp_high_bw_mem_alloc = 4,
+ omp_low_lat_mem_alloc = 5,
+ omp_cgroup_mem_alloc = 6,
+ omp_pteam_mem_alloc = 7,
+ omp_thread_mem_alloc = 8,
+ KMP_ALLOCATOR_MAX_HANDLE = __UINTPTR_MAX__
+};
+
+struct St{
+ int a;
+};
+
+struct St1{
+ int a;
+ static int b;
+ [[omp::directive(allocate(b) allocator(omp_default_mem_alloc))]];
+} d;
+
+int a, b, c;
+[[omp::directive(allocate(a) allocator(omp_large_cap_mem_alloc)),
+ directive(allocate(b) allocator(omp_const_mem_alloc)),
+ directive(allocate(d, c) allocator(omp_high_bw_mem_alloc))]];
+
+template <class T>
+struct ST {
+ static T m;
+ [[omp::directive(allocate(m) allocator(omp_low_lat_mem_alloc))]];
+};
+
+template <class T> T foo() {
+ T v;
+ [[omp::directive(allocate(v) allocator(omp_cgroup_mem_alloc))]];
+ v = ST<T>::m;
+ return v;
+}
+
+namespace ns{
+ int a;
+}
+[[omp::directive(allocate(ns::a) allocator(omp_pteam_mem_alloc))]];
+
+// CHECK-NOT: call {{.+}} {{__kmpc_alloc|__kmpc_free}}
+
+// CHECK-LABEL: @main
+int main () {
+ static int a;
+ [[omp::directive(allocate(a) allocator(omp_thread_mem_alloc))]];
+ a=2;
+ // CHECK-NOT: {{__kmpc_alloc|__kmpc_free}}
+ // CHECK: alloca double,
+ // CHECK-NOT: {{__kmpc_alloc|__kmpc_free}}
+ double b = 3;
+ [[omp::directive(allocate(b))]];
+ return (foo<int>());
+}
+
+// CHECK: define {{.*}}i32 @{{.+}}foo{{.+}}()
+// CHECK: [[GTID:%.+]] = call i32 @__kmpc_global_thread_num(%struct.ident_t* @{{.+}})
+// CHECK-NEXT: [[V_VOID_ADDR:%.+]] = call i8* @__kmpc_alloc(i32 [[GTID]], i64 4, i8* inttoptr (i64 6 to i8*))
+// CHECK-NEXT: [[V_ADDR:%.+]] = bitcast i8* [[V_VOID_ADDR]] to i32*
+// CHECK-NOT: {{__kmpc_alloc|__kmpc_free}}
+// CHECK: store i32 %{{.+}}, i32* [[V_ADDR]],
+// CHECK-NEXT: [[V_VAL:%.+]] = load i32, i32* [[V_ADDR]],
+// CHECK-NEXT: [[V_VOID_ADDR:%.+]] = bitcast i32* [[V_ADDR]] to i8*
+// CHECK-NEXT: call void @__kmpc_free(i32 [[GTID]], i8* [[V_VOID_ADDR]], i8* inttoptr (i64 6 to i8*))
+// CHECK-NOT: {{__kmpc_alloc|__kmpc_free}}
+// CHECK: ret i32 [[V_VAL]]
+
+// CHECK-NOT: call {{.+}} {{__kmpc_alloc|__kmpc_free}}
+extern template int ST<int>::m;
+
+// CHECK: define{{.*}} void @{{.+}}bar{{.+}}(i32 %{{.+}}, float* {{.+}})
+void bar(int a, float &z) {
+// CHECK: [[A_VOID_PTR:%.+]] = call i8* @__kmpc_alloc(i32 [[GTID:%.+]], i64 4, i8* inttoptr (i64 1 to i8*))
+// CHECK: [[A_ADDR:%.+]] = bitcast i8* [[A_VOID_PTR]] to i32*
+// CHECK: store i32 %{{.+}}, i32* [[A_ADDR]],
+// CHECK: [[Z_VOID_PTR:%.+]] = call i8* @__kmpc_alloc(i32 [[GTID]], i64 8, i8* inttoptr (i64 1 to i8*))
+// CHECK: [[Z_ADDR:%.+]] = bitcast i8* [[Z_VOID_PTR]] to float**
+// CHECK: store float* %{{.+}}, float** [[Z_ADDR]],
+[[omp::directive(allocate(a,z) allocator(omp_default_mem_alloc))]];
+// CHECK-NEXT: [[Z_VOID_PTR:%.+]] = bitcast float** [[Z_ADDR]] to i8*
+// CHECK: call void @__kmpc_free(i32 [[GTID]], i8* [[Z_VOID_PTR]], i8* inttoptr (i64 1 to i8*))
+// CHECK-NEXT: [[A_VOID_PTR:%.+]] = bitcast i32* [[A_ADDR]] to i8*
+// CHECK: call void @__kmpc_free(i32 [[GTID]], i8* [[A_VOID_PTR]], i8* inttoptr (i64 1 to i8*))
+// CHECK: ret void
+}
+#endif
+
--- /dev/null
+// RUN: %clang_cc1 -triple=x86_64-pc-win32 -verify -fopenmp -fopenmp-version=51 -std=c99 -fms-extensions -fdouble-square-bracket-attributes -Wno-pragma-pack %s
+// RUN: %clang_cc1 -triple=x86_64-pc-win32 -verify -fopenmp-simd -fopenmp-version=51 -std=c99 -fms-extensions -fdouble-square-bracket-attributes -Wno-pragma-pack %s
+
+[[omp::directive(assumes)]]; // expected-error {{expected at least one 'ext_', 'absent', 'contains', 'holds', 'no_openmp', 'no_openmp_routines', 'no_parallelism' clause for '#pragma omp assumes'}}
+[[omp::directive(begin)]]; // expected-error {{expected an OpenMP directive}}
+[[omp::directive(begin assumes)]]; // expected-error {{expected at least one 'ext_', 'absent', 'contains', 'holds', 'no_openmp', 'no_openmp_routines', 'no_parallelism' clause for '#pragma omp begin assumes'}}
+[[omp::directive(end assumes)]];
+
+[[omp::directive(assumes foobar)]]; // expected-warning {{valid assumes clauses start with 'ext_', 'absent', 'contains', 'holds', 'no_openmp', 'no_openmp_routines', 'no_parallelism'; token will be ignored}}
+[[omp::directive(begin assumes foobar)]]; // expected-warning {{valid begin assumes clauses start with 'ext_', 'absent', 'contains', 'holds', 'no_openmp', 'no_openmp_routines', 'no_parallelism'; token will be ignored}}
+[[omp::directive(end assumes)]];
+
+[[omp::directive(begin assumes foobar(foo 2 baz))]]; // expected-warning {{valid begin assumes clauses start with 'ext_', 'absent', 'contains', 'holds', 'no_openmp', 'no_openmp_routines', 'no_parallelism'; tokens will be ignored}} expected-note {{the ignored tokens spans until here}}
+[[omp::directive(assumes foobar(foo 2 baz))]]; // expected-warning {{valid assumes clauses start with 'ext_', 'absent', 'contains', 'holds', 'no_openmp', 'no_openmp_routines', 'no_parallelism'; tokens will be ignored}} expected-note {{the ignored tokens spans until here}}
+[[omp::directive(end assumes)]];
+
+[[omp::directive(assumes no_openmp(1))]]; // expected-warning {{'no_openmp' clause should not be followed by arguments; tokens will be ignored}} expected-note {{the ignored tokens spans until here}}
+[[omp::directive(begin assumes no_openmp(1 2 3))]]; // expected-warning {{'no_openmp' clause should not be followed by arguments; tokens will be ignored}} expected-note {{the ignored tokens spans until here}}
+[[omp::directive(end assumes no_openmp(1))]];
+
+[[omp::directive(assumes foobar no_openmp bazbaz)]]; // expected-warning {{valid assumes clauses start with 'ext_', 'absent', 'contains', 'holds', 'no_openmp', 'no_openmp_routines', 'no_parallelism'; token will be ignored}} expected-warning {{valid assumes clauses start with 'ext_', 'absent', 'contains', 'holds', 'no_openmp', 'no_openmp_routines', 'no_parallelism'; token will be ignored}}
+[[omp::directive(begin assumes foobar no_openmp bazbaz)]]; // expected-warning {{valid begin assumes clauses start with 'ext_', 'absent', 'contains', 'holds', 'no_openmp', 'no_openmp_routines', 'no_parallelism'; token will be ignored}} expected-warning {{valid begin assumes clauses start with 'ext_', 'absent', 'contains', 'holds', 'no_openmp', 'no_openmp_routines', 'no_parallelism'; token will be ignored}}
+[[omp::directive(end assumes)]];
+
+[[omp::directive(begin assumes foobar(foo 2 baz) no_openmp bazbaz(foo 2 baz))]]; // expected-warning {{valid begin assumes clauses start with 'ext_', 'absent', 'contains', 'holds', 'no_openmp', 'no_openmp_routines', 'no_parallelism'; tokens will be ignored}} expected-warning {{valid begin assumes clauses start with 'ext_', 'absent', 'contains', 'holds', 'no_openmp', 'no_openmp_routines', 'no_parallelism'; tokens will be ignored}} expected-note {{the ignored tokens spans until here}} expected-note {{the ignored tokens spans until here}}
+[[omp::directive(assumes foobar(foo 2 baz) no_openmp bazbaz(foo 2 baz))]]; // expected-warning {{valid assumes clauses start with 'ext_', 'absent', 'contains', 'holds', 'no_openmp', 'no_openmp_routines', 'no_parallelism'; tokens will be ignored}} expected-warning {{valid assumes clauses start with 'ext_', 'absent', 'contains', 'holds', 'no_openmp', 'no_openmp_routines', 'no_parallelism'; tokens will be ignored}} expected-note {{the ignored tokens spans until here}} expected-note {{the ignored tokens spans until here}}
+[[omp::directive(end assumes)]];
+
+[[omp::directive(assumes no_openmp foobar no_openmp)]]; // expected-warning {{valid assumes clauses start with 'ext_', 'absent', 'contains', 'holds', 'no_openmp', 'no_openmp_routines', 'no_parallelism'; token will be ignored}}
+[[omp::directive(begin assumes no_openmp foobar no_openmp)]]; // expected-warning {{valid begin assumes clauses start with 'ext_', 'absent', 'contains', 'holds', 'no_openmp', 'no_openmp_routines', 'no_parallelism'; token will be ignored}}
+[[omp::directive(end assumes)]];
+
+[[omp::directive(assumes holds(1, 2 3))]];
+[[omp::directive(begin assumes holds(1, 2 3))]];
+[[omp::directive(end assumes)]];
+
+[[omp::directive(assumes absent(1, 2 3))]];
+[[omp::directive(begin assumes absent(1, 2 3))]];
+[[omp::directive(end assumes)]];
+
+[[omp::directive(assumes contains(1, 2 3))]];
+[[omp::directive(begin assumes contains(1, 2 3))]];
+[[omp::directive(end assumes)]];
+
+[[omp::directive(assumes ext)]]; // expected-warning {{valid assumes clauses start with 'ext_', 'absent', 'contains', 'holds', 'no_openmp', 'no_openmp_routines', 'no_parallelism'; token will be ignored}}
+[[omp::directive(begin assumes ext)]]; // expected-warning {{valid begin assumes clauses start with 'ext_', 'absent', 'contains', 'holds', 'no_openmp', 'no_openmp_routines', 'no_parallelism'; token will be ignored}}
+[[omp::directive(end assumes)]];
+
+[[omp::directive(assumes ext_123(not allowed))]]; // expected-warning {{'ext_123' clause should not be followed by arguments; tokens will be ignored}} expected-note {{the ignored tokens spans until here}}
+[[omp::directive(begin assumes ext_123(not allowed))]]; // expected-warning {{'ext_123' clause should not be followed by arguments; tokens will be ignored}} expected-note {{the ignored tokens spans until here}}
+[[omp::directive(end assumes)]];
+
+[[omp::directive(end assumes)]]; // expected-error {{'#pragma omp end assumes' with no matching '#pragma omp begin assumes'}}
+
+// TODO: we should emit a warning at least.
+[[omp::directive(begin assumes ext_abc)]];
+
--- /dev/null
+// RUN: %clang_cc1 -verify -fopenmp -fopenmp-version=51 -emit-llvm %s -fexceptions -fcxx-exceptions -o - | FileCheck %s --check-prefixes=ALL,NORMAL
+// RUN: %clang_cc1 -fopenmp -fopenmp-version=51 -std=c++11 -triple x86_64-unknown-unknown -fexceptions -fcxx-exceptions -emit-pch -o %t %s
+// RUN: %clang_cc1 -fopenmp -fopenmp-version=51 -triple x86_64-unknown-unknown -fexceptions -fcxx-exceptions -std=c++11 -include-pch %t -verify %s -emit-llvm -o - | FileCheck %s --check-prefixes=ALL,NORMAL
+// RUN: %clang_cc1 -verify -triple x86_64-apple-darwin10 -fopenmp -fopenmp-version=51 -fexceptions -fcxx-exceptions -debug-info-kind=line-tables-only -emit-llvm %s -o - | FileCheck %s --check-prefix=TERM_DEBUG
+// RUN: %clang_cc1 -verify -fopenmp -fopenmp-version=51 -fopenmp-enable-irbuilder -emit-llvm %s -fexceptions -fcxx-exceptions -o - | FileCheck %s --check-prefixes=ALL,IRBUILDER
+// RUN: %clang_cc1 -fopenmp -fopenmp-version=51 -fopenmp-enable-irbuilder -std=c++11 -triple x86_64-unknown-unknown -fexceptions -fcxx-exceptions -emit-pch -o %t %s
+// RUN: %clang_cc1 -fopenmp -fopenmp-version=51 -fopenmp-enable-irbuilder -triple x86_64-unknown-unknown -fexceptions -fcxx-exceptions -std=c++11 -include-pch %t -verify %s -emit-llvm -o - | FileCheck %s --check-prefixes=ALL,IRBUILDER
+
+// RUN: %clang_cc1 -verify -fopenmp-simd -fopenmp-version=51 -emit-llvm %s -fexceptions -fcxx-exceptions -o - | FileCheck --check-prefix SIMD-ONLY0 %s
+// RUN: %clang_cc1 -fopenmp-simd -fopenmp-version=51 -std=c++11 -triple x86_64-unknown-unknown -fexceptions -fcxx-exceptions -emit-pch -o %t %s
+// RUN: %clang_cc1 -fopenmp-simd -fopenmp-version=51 -triple x86_64-unknown-unknown -fexceptions -fcxx-exceptions -std=c++11 -include-pch %t -verify %s -emit-llvm -o - | FileCheck --check-prefix SIMD-ONLY0 %s
+// RUN: %clang_cc1 -verify -triple x86_64-apple-darwin10 -fopenmp-simd -fopenmp-version=51 -fexceptions -fcxx-exceptions -debug-info-kind=line-tables-only -emit-llvm %s -o - | FileCheck --check-prefix SIMD-ONLY0 %s
+// SIMD-ONLY0-NOT: {{__kmpc|__tgt}}
+// expected-no-diagnostics
+#ifndef HEADER
+#define HEADER
+
+// ALL: [[IDENT_T_TY:%.+]] = type { i32, i32, i32, i32, i8* }
+// ALL: [[UNNAMED_LOCK:@.+]] = common global [8 x i32] zeroinitializer
+// ALL: [[THE_NAME_LOCK:@.+]] = common global [8 x i32] zeroinitializer
+// ALL: [[THE_NAME_LOCK1:@.+]] = common global [8 x i32] zeroinitializer
+
+// ALL: define {{.*}}void [[FOO:@.+]]()
+
+void foo() { extern void mayThrow(); mayThrow(); }
+
+// ALL-LABEL: @main
+// TERM_DEBUG-LABEL: @main
+int main() {
+ // ALL: [[A_ADDR:%.+]] = alloca i8
+ char a;
+
+// ALL: [[GTID:%.+]] = call {{.*}}i32 @__kmpc_global_thread_num([[IDENT_T_TY]]* [[DEFAULT_LOC:@.+]])
+// ALL: call {{.*}}void @__kmpc_critical([[IDENT_T_TY]]* [[DEFAULT_LOC]], i32 [[GTID]], [8 x i32]* [[UNNAMED_LOCK]])
+// ALL-NEXT: store i8 2, i8* [[A_ADDR]]
+// ALL-NEXT: call {{.*}}void @__kmpc_end_critical([[IDENT_T_TY]]* [[DEFAULT_LOC]], i32 [[GTID]], [8 x i32]* [[UNNAMED_LOCK]])
+ [[omp::directive(critical)]]
+ a = 2;
+// IRBUILDER: [[GTID:%.+]] = call {{.*}}i32 @__kmpc_global_thread_num([[IDENT_T_TY]]* [[DEFAULT_LOC:@.+]])
+// ALL: call {{.*}}void @__kmpc_critical([[IDENT_T_TY]]* [[DEFAULT_LOC]], i32 [[GTID]], [8 x i32]* [[THE_NAME_LOCK]])
+// IRBUILDER-NEXT: call {{.*}}void [[FOO]]()
+// NORMAL-NEXT: invoke {{.*}}void [[FOO]]()
+// ALL: call {{.*}}void @__kmpc_end_critical([[IDENT_T_TY]]* [[DEFAULT_LOC]], i32 [[GTID]], [8 x i32]* [[THE_NAME_LOCK]])
+ [[omp::directive(critical(the_name))]]
+ foo();
+// IRBUILDER: [[GTID:%.+]] = call {{.*}}i32 @__kmpc_global_thread_num([[IDENT_T_TY]]* [[DEFAULT_LOC:@.+]])
+// ALL: call {{.*}}void @__kmpc_critical_with_hint([[IDENT_T_TY]]* [[DEFAULT_LOC]], i32 [[GTID]], [8 x i32]* [[THE_NAME_LOCK1]], i{{64|32}} 23)
+// IRBUILDER-NEXT: call {{.*}}void [[FOO]]()
+// NORMAL-NEXT: invoke {{.*}}void [[FOO]]()
+// ALL: call {{.*}}void @__kmpc_end_critical([[IDENT_T_TY]]* [[DEFAULT_LOC]], i32 [[GTID]], [8 x i32]* [[THE_NAME_LOCK1]])
+ [[omp::directive(critical(the_name1) hint(23))]]
+ foo();
+ // IRBUILDER: [[GTID:%.+]] = call {{.*}}i32 @__kmpc_global_thread_num([[IDENT_T_TY]]* [[DEFAULT_LOC:@.+]])
+ // ALL: call {{.*}}void @__kmpc_critical([[IDENT_T_TY]]* [[DEFAULT_LOC]], i32 [[GTID]], [8 x i32]* [[THE_NAME_LOCK]])
+ // ALL: br label
+ // ALL-NOT: call {{.*}}void @__kmpc_end_critical(
+ // ALL: br label
+ // ALL-NOT: call {{.*}}void @__kmpc_end_critical(
+ // NORMAL: br label
+ if (a)
+ [[omp::directive(critical(the_name))]]
+ while (1)
+ ;
+ // ALL: call {{.*}}void [[FOO]]()
+ foo();
+ // ALL-NOT: call void @__kmpc_critical
+ // ALL-NOT: call void @__kmpc_end_critical
+ return a;
+}
+
+// ALL-LABEL: lambda_critical
+// TERM_DEBUG-LABEL: lambda_critical
+void lambda_critical(int a, int b) {
+ auto l = [=]() {
+ [[omp::directive(critical)]]
+ {
+ // ALL: call void @__kmpc_critical(
+ int c = a + b;
+ }
+ };
+
+ l();
+
+ auto l1 = [=]() {
+ [[omp::sequence(directive(parallel), directive(critical))]]
+ {
+ // ALL: call void @__kmpc_critical(
+ int c = a + b;
+ }
+ };
+
+ l1();
+}
+
+struct S {
+ int a;
+};
+// ALL-LABEL: critical_ref
+void critical_ref(S &s) {
+ // ALL: [[S_ADDR:%.+]] = alloca %struct.S*,
+ // ALL: [[S_REF:%.+]] = load %struct.S*, %struct.S** [[S_ADDR]],
+ // ALL: [[S_A_REF:%.+]] = getelementptr inbounds %struct.S, %struct.S* [[S_REF]], i32 0, i32 0
+ ++s.a;
+ // ALL: call void @__kmpc_critical(
+ [[omp::directive(critical)]]
+ // ALL: [[S_REF:%.+]] = load %struct.S*, %struct.S** [[S_ADDR]],
+ // ALL: [[S_A_REF:%.+]] = getelementptr inbounds %struct.S, %struct.S* [[S_REF]], i32 0, i32 0
+ ++s.a;
+ // ALL: call void @__kmpc_end_critical(
+}
+
+// ALL-LABEL: parallel_critical
+// TERM_DEBUG-LABEL: parallel_critical
+void parallel_critical() {
+ [[omp::sequence(directive(parallel), directive(critical))]]
+ // TERM_DEBUG-NOT: __kmpc_global_thread_num
+ // TERM_DEBUG: call void @__kmpc_critical({{.+}}), !dbg [[DBG_LOC_START:![0-9]+]]
+ // TERM_DEBUG: invoke void {{.*}}foo{{.*}}()
+ // TERM_DEBUG: unwind label %[[TERM_LPAD:.+]],
+ // TERM_DEBUG-NOT: __kmpc_global_thread_num
+ // TERM_DEBUG: call void @__kmpc_end_critical({{.+}}), !dbg [[DBG_LOC_END:![0-9]+]]
+ // TERM_DEBUG: [[TERM_LPAD]]
+ // TERM_DEBUG: call void @__clang_call_terminate
+ // TERM_DEBUG: unreachable
+ foo();
+}
+// TERM_DEBUG-DAG: [[DBG_LOC_START]] = !DILocation(line: [[@LINE-12]],
+// TERM_DEBUG-DAG: [[DBG_LOC_END]] = !DILocation(line: [[@LINE-3]],
+#endif
+
--- /dev/null
+// RUN: %clang_cc1 -verify -fopenmp -fopenmp-version=51 %s -Wuninitialized
+// RUN: %clang_cc1 -verify -fopenmp-simd -fopenmp-version=51 %s -Wuninitialized
+
+void xxx(int argc) {
+ int x; // expected-note {{initialize the variable 'x' to silence this warning}}
+ [[omp::directive(masked)]]
+ argc = x; // expected-warning {{variable 'x' is uninitialized when used here}}
+}
+
+void yyy(int argc) {
+ int x; // expected-note {{initialize the variable 'x' to silence this warning}}
+ [[omp::directive(masked filter(1))]]
+ argc = x; // expected-warning {{variable 'x' is uninitialized when used here}}
+}
+
+int foo();
+
+int main() {
+ [[omp::directive(masked)]]
+ ;
+ [[omp::directive(masked filter(1) filter(2))]] // expected-error {{directive '#pragma omp masked' cannot contain more than one 'filter' clause}}
+ ;
+ int x,y,z;
+ [[omp::directive(masked filter(x) filter(y) filter(z))]] // expected-error 2 {{directive '#pragma omp masked' cannot contain more than one 'filter' clause}}
+ ;
+ [[omp::directive(masked nowait)]] // expected-error {{unexpected OpenMP clause 'nowait' in directive '#pragma omp masked'}}
+ [[omp::directive(masked unknown)]] // expected-warning {{extra tokens at the end of '#pragma omp masked' are ignored}}
+ foo();
+ {
+ [[omp::directive(masked)]]
+ } // expected-error {{expected statement}}
+ {
+ [[omp::directive(masked filter(2))]]
+ } // expected-error {{expected statement}}
+ [[omp::directive(for)]]
+ for (int i = 0; i < 10; ++i) {
+ foo();
+ [[omp::directive(masked filter(1))]] // expected-error {{region cannot be closely nested inside 'for' region}}
+ foo();
+ }
+ [[omp::directive(sections)]]
+ {
+ foo();
+ [[omp::directive(masked)]] // expected-error {{region cannot be closely nested inside 'sections' region}}
+ foo();
+ }
+ [[omp::directive(single)]]
+ for (int i = 0; i < 10; ++i) {
+ foo();
+ [[omp::directive(masked allocate(i))]] // expected-error {{region cannot be closely nested inside 'single' region}} expected-error {{unexpected OpenMP clause 'allocate' in directive '#pragma omp masked'}}
+ foo();
+ }
+ [[omp::directive(masked)]]
+ for (int i = 0; i < 10; ++i) {
+ foo();
+ [[omp::directive(masked)]]
+ foo();
+ }
+ [[omp::directive(for ordered)]]
+ for (int i = 0; i < 10; ++i)
+ [[omp::directive(masked)]] // expected-error {{region cannot be closely nested inside 'for' region}}
+ {
+ foo();
+ }
+
+ return 0;
+}
+
+int foo() {
+ L1: // expected-note {{jump exits scope of OpenMP structured block}}
+ foo();
+ [[omp::directive(masked filter(0))]]
+ {
+ foo();
+ goto L1; // expected-error {{cannot jump from this goto statement to its label}}
+ }
+ goto L2; // expected-error {{cannot jump from this goto statement to its label}}
+ [[omp::directive(masked filter(-2))]]
+ { // expected-note {{jump bypasses OpenMP structured block}}
+ L2:
+ foo();
+ }
+
+ return 0;
+}
+
--- /dev/null
+// RUN: %clang_cc1 -fopenmp -fopenmp-version=51 -fsyntax-only -verify -DSUPPORTED=1 %s
+// RUN: %clang_cc1 -fopenmp -fopenmp-version=51 -fsyntax-only -verify -DSUPPORTED=1 -x c -std=c2x %s
+// RUN: %clang_cc1 -fsyntax-only -verify -DSUPPORTED=0 %s
+// RUN: %clang_cc1 -fsyntax-only -verify -DSUPPORTED=0 -x c -std=c2x %s
+// RUN: %clang_cc1 -fopenmp -fopenmp-version=50 -fsyntax-only -verify -DSUPPORTED=1 %s
+// RUN: %clang_cc1 -fopenmp -fopenmp-version=50 -fsyntax-only -verify -DSUPPORTED=1 -x c -std=c2x %s
+// expected-no-diagnostics
+
+#ifndef SUPPORTED
+#error "Someone messed up a RUN line"
+#endif
+
+#ifdef __cplusplus
+#if __has_cpp_attribute(omp::sequence) != SUPPORTED
+#error "No idea what you're talking about"
+#endif
+
+#if __has_cpp_attribute(omp::directive) != SUPPORTED
+#error "No idea what you're talking about"
+#endif
+
+#if __has_cpp_attribute(omp::totally_bogus)
+#error "No idea what you're talking about"
+#endif
+
+#else // __cplusplus
+
+#if __has_c_attribute(omp::sequence) != SUPPORTED
+#error "No idea what you're talking about"
+#endif
+
+#if __has_c_attribute(omp::directive) != SUPPORTED
+#error "No idea what you're talking about"
+#endif
+
+#if __has_c_attribute(omp::totally_bogus)
+#error "No idea what you're talking about"
+#endif
+
+#endif
+
--- /dev/null
+// RUN: %clang_cc1 -fopenmp -fopenmp-version=51 -fsyntax-only -verify=pre -Wpre-openmp-51-compat %s
+// RUN: %clang_cc1 -fopenmp -fopenmp-version=51 -fsyntax-only -verify=off %s
+// RUN: %clang_cc1 -fopenmp -fopenmp-version=50 -fsyntax-only -verify=ext -Wopenmp %s
+// RUN: %clang_cc1 -fopenmp -fopenmp-version=50 -fsyntax-only -verify=ext -Wopenmp-51-extensions %s
+// RUN: %clang_cc1 -fopenmp -fopenmp-version=50 -fsyntax-only -verify=ext -Wall %s
+// RUN: %clang_cc1 -fopenmp -fopenmp-version=50 -fsyntax-only -verify=ext %s
+// RUN: %clang_cc1 -fopenmp -fopenmp-version=50 -fsyntax-only -verify=off -Wno-openmp %s
+// RUN: %clang_cc1 -fopenmp -fopenmp-version=50 -fsyntax-only -verify=off -Wno-openmp-51-extensions %s
+
+// RUN: %clang_cc1 -fopenmp -fopenmp-version=51 -fsyntax-only -verify=pre -Wpre-openmp-51-compat -x c -fdouble-square-bracket-attributes %s
+// RUN: %clang_cc1 -fopenmp -fopenmp-version=51 -fsyntax-only -verify=off -x c -std=c2x %s
+// RUN: %clang_cc1 -fopenmp -fopenmp-version=50 -fsyntax-only -verify=ext -Wopenmp -x c -std=c2x %s
+
+// off-no-diagnostics
+
+int x;
+[[omp::directive(threadprivate(x))]]; // pre-warning {{specifying OpenMP directives with [[]] is incompatible with OpenMP standards before OpenMP 5.1}} \
+ // ext-warning {{specifying OpenMP directives with [[]] is an OpenMP 5.1 extension}}
+
--- /dev/null
+// RUN: %clang_cc1 -std=c++17 -fopenmp -fopenmp-version=51 -fsyntax-only -verify %s
+
+// This file tests the custom parsing logic for the OpenMP 5.1 attribute
+// syntax. It does not test actual OpenMP directive syntax, just the attribute
+// parsing bits.
+
+// FIXME: the diagnostic here is a bit unsatisfying. We handle the custom omp
+// attribute parsing logic when parsing the attribute argument list, and we
+// only process an attribute argument list when we see an open paren after the
+// attribute name. So this means we never hit the omp-specific parsing and
+// instead handle this through the usual Sema attribute handling in
+// SemaDeclAttr.cpp, which diagnoses this as an unknown attribute.
+[[omp::directive]]; // expected-warning {{unknown attribute 'directive' ignored}}
+[[omp::sequence]]; // expected-warning {{unknown attribute 'sequence' ignored}}
+[[omp::unknown]]; // expected-warning {{unknown attribute 'unknown' ignored}}
+
+[[omp::directive()]]; // expected-error {{expected an OpenMP directive}}
+[[omp::sequence()]]; // expected-error {{expected an OpenMP 'directive' or 'sequence' attribute argument}}
+
+// Both sequence and directive require an argument list, test that we diagnose
+// when the inner directive or sequence is missing its argument list.
+[[omp::sequence(directive)]]; // expected-error {{expected '('}}
+[[omp::sequence(sequence)]]; // expected-error {{expected '('}}
+[[omp::sequence(omp::directive)]]; // expected-error {{expected '('}}
+[[omp::sequence(omp::sequence)]]; // expected-error {{expected '('}}
+
+// All of the diagnostics here come from the inner sequence and directive not
+// being given an argument, but this tests that we can parse either with or
+// without the 'omp::'.
+[[omp::sequence(directive(), sequence())]]; // expected-error {{expected an OpenMP directive}} expected-error {{expected an OpenMP 'directive' or 'sequence' attribute argument}}
+[[omp::sequence(omp::directive(), sequence())]]; // expected-error {{expected an OpenMP directive}} expected-error {{expected an OpenMP 'directive' or 'sequence' attribute argument}}
+[[omp::sequence(directive(), omp::sequence())]]; // expected-error {{expected an OpenMP directive}} expected-error {{expected an OpenMP 'directive' or 'sequence' attribute argument}}
+[[omp::sequence(omp::directive(), omp::sequence())]]; // expected-error {{expected an OpenMP directive}} expected-error {{expected an OpenMP 'directive' or 'sequence' attribute argument}}
+
+// Test that we properly diagnose missing parens within the inner arguments of
+// a sequence attribute.
+[[omp::sequence( // expected-note {{to match this '('}}
+ directive(
+)]]; // expected-error {{expected ')'}} expected-error {{expected an OpenMP directive}}
+[[omp::sequence( // expected-note {{to match this '('}}
+ sequence(
+)]]; // expected-error {{expected ')'}} expected-error {{expected an OpenMP 'directive' or 'sequence' attribute argument}}
+
+// Test that we properly handle the using attribute syntax.
+[[using omp: directive()]]; // expected-error {{expected an OpenMP directive}}
+[[using omp: sequence()]]; // expected-error {{expected an OpenMP 'directive' or 'sequence' attribute argument}}
+[[using omp: sequence(omp::directive())]]; // expected-error {{expected an OpenMP directive}}
+[[using omp: sequence(directive())]]; // expected-error {{expected an OpenMP directive}}
+
+// Test that we give a sensible error on an unknown attribute in the omp
+// namespace that has an argument list.
+[[omp::unknown()]]; // expected-warning {{unknown attribute 'unknown' ignored}}
+[[using omp: unknown()]]; // expected-warning {{unknown attribute 'unknown' ignored}}
+
+// Test that unknown arguments to the omp::sequence are rejected, regardless of
+// what level they're at.
+[[omp::sequence(unknown)]]; // expected-error {{expected an OpenMP 'directive' or 'sequence' attribute argument}}
+[[omp::sequence(sequence(unknown))]]; // expected-error {{expected an OpenMP 'directive' or 'sequence' attribute argument}}
+[[omp::sequence(omp::unknown)]]; // expected-error {{expected an OpenMP 'directive' or 'sequence' attribute argument}}
+[[omp::sequence(sequence(omp::unknown))]]; // expected-error {{expected an OpenMP 'directive' or 'sequence' attribute argument}}
+
+// FIXME: combining non-openmp attributes with openmp attributes has surprising
+// results due to the replay of tokens. We properly parse the non-openmp
+// attributes, but we also replay the OpenMP tokens. The attributes then get
+// passed to the OpenMP parsing functions and it does not attach the attribute
+// to the declaration statement AST node as you might expect. This means that
+// the expected diagnostics are not issued. Thankfully, due to the positioning
+// of OpenMP attributes and what they appertain to, this should not be a
+// frequent issue (hopefully).
+int x;
+[[deprecated, omp::directive(threadprivate(x))]] int y; // FIXME-expected-note {{'y' has been explicitly marked deprecated here}}
+[[omp::directive(threadprivate(x)), deprecated]] int z; // FIXME-expected-note {{'z' has been explicitly marked deprecated here}}
+void test() {
+ x = 1;
+ y = 1; // FIXME-expected-warning {{warning: 'y' is deprecated}}
+ z = 1; // FIXME-expected-warning {{warning: 'z' is deprecated}}
+}
--- /dev/null
+// RUN: %clang_cc1 -fopenmp -fopenmp-version=51 -triple powerpc64le-unknown-unknown -fopenmp-targets=powerpc64le-ibm-linux-gnu -debug-info-kind=limited -emit-llvm %s -o - | FileCheck %s --check-prefix DEBUG
+// RUN: %clang_cc1 -fopenmp -fopenmp-version=51 -triple powerpc64le-unknown-unknown -fopenmp-targets=powerpc64le-ibm-linux-gnu -emit-llvm %s -o - | FileCheck %s --check-prefix CHECK
+#ifndef HEADER
+#define HEADER
+
+// DEBUG: @{{[0-9]+}} = private unnamed_addr constant [{{[0-9]+}} x i8] c";d;{{.*}}.cpp;{{[0-9]+}};{{[0-9]+}};;\00"
+// DEBUG: @{{[0-9]+}} = private unnamed_addr constant [{{[0-9]+}} x i8] c";i;{{.*}}.cpp;{{[0-9]+}};{{[0-9]+}};;\00"
+// DEBUG: @{{[0-9]+}} = private unnamed_addr constant [{{[0-9]+}} x i8] c";i[1:23];{{.*}}.cpp;{{[0-9]+}};{{[0-9]+}};;\00"
+// DEBUG: @{{[0-9]+}} = private unnamed_addr constant [{{[0-9]+}} x i8] c";p;{{.*}}.cpp;{{[0-9]+}};{{[0-9]+}};;\00"
+// DEBUG: @{{[0-9]+}} = private unnamed_addr constant [{{[0-9]+}} x i8] c";p[1:24];{{.*}}.cpp;{{[0-9]+}};{{[0-9]+}};;\00"
+// DEBUG: @{{[0-9]+}} = private unnamed_addr constant [{{[0-9]+}} x i8] c";s;{{.*}}.cpp;{{[0-9]+}};{{[0-9]+}};;\00"
+// DEBUG: @{{[0-9]+}} = private unnamed_addr constant [{{[0-9]+}} x i8] c";s.i;{{.*}}.cpp;{{[0-9]+}};{{[0-9]+}};;\00"
+// DEBUG: @{{[0-9]+}} = private unnamed_addr constant [{{[0-9]+}} x i8] c";s.s.f;{{.*}}.cpp;{{[0-9]+}};{{[0-9]+}};;\00"
+// DEBUG: @{{[0-9]+}} = private unnamed_addr constant [{{[0-9]+}} x i8] c";s.p[:22];{{.*}}.cpp;{{[0-9]+}};{{[0-9]+}};;\00"
+// DEBUG: @{{[0-9]+}} = private unnamed_addr constant [{{[0-9]+}} x i8] c";s.ps->s.i;{{.*}}.cpp;{{[0-9]+}};{{[0-9]+}};;\00"
+// DEBUG: @{{[0-9]+}} = private unnamed_addr constant [{{[0-9]+}} x i8] c";s.ps->ps;{{.*}}.cpp;{{[0-9]+}};{{[0-9]+}};;\00"
+// DEBUG: @{{[0-9]+}} = private unnamed_addr constant [{{[0-9]+}} x i8] c";s.ps->ps->ps;{{.*}}.cpp;{{[0-9]+}};{{[0-9]+}};;\00"
+// DEBUG: @{{[0-9]+}} = private unnamed_addr constant [{{[0-9]+}} x i8] c";s.ps->ps->s.f[:22];{{.*}}.cpp;{{[0-9]+}};{{[0-9]+}};;\00"
+// DEBUG: @{{[0-9]+}} = private unnamed_addr constant [{{[0-9]+}} x i8] c";ps;{{.*}}.cpp;{{[0-9]+}};{{[0-9]+}};;\00"
+// DEBUG: @{{[0-9]+}} = private unnamed_addr constant [{{[0-9]+}} x i8] c";ps->i;{{.*}}.cpp;{{[0-9]+}};{{[0-9]+}};;\00"
+// DEBUG: @{{[0-9]+}} = private unnamed_addr constant [{{[0-9]+}} x i8] c";ps->s.f;{{.*}}.cpp;{{[0-9]+}};{{[0-9]+}};;\00"
+// DEBUG: @{{[0-9]+}} = private unnamed_addr constant [{{[0-9]+}} x i8] c";ps->p[:22];{{.*}}.cpp;{{[0-9]+}};{{[0-9]+}};;\00"
+// DEBUG: @{{[0-9]+}} = private unnamed_addr constant [{{[0-9]+}} x i8] c";ps->ps->s.i;{{.*}}.cpp;{{[0-9]+}};{{[0-9]+}};;\00"
+// DEBUG: @{{[0-9]+}} = private unnamed_addr constant [{{[0-9]+}} x i8] c";ps->ps->ps;{{.*}}.cpp;{{[0-9]+}};{{[0-9]+}};;\00"
+// DEBUG: @{{[0-9]+}} = private unnamed_addr constant [{{[0-9]+}} x i8] c";ps->ps->ps->ps;{{.*}}.cpp;{{[0-9]+}};{{[0-9]+}};;\00"
+// DEBUG: @{{[0-9]+}} = private unnamed_addr constant [{{[0-9]+}} x i8] c";ps->ps->ps->s.f[:22];{{.*}}.cpp;{{[0-9]+}};{{[0-9]+}};;\00"
+// DEBUG: @{{[0-9]+}} = private unnamed_addr constant [{{[0-9]+}} x i8] c";s.f[:22];{{.*}}.cpp;{{[0-9]+}};{{[0-9]+}};;\00"
+// DEBUG: @{{[0-9]+}} = private unnamed_addr constant [{{[0-9]+}} x i8] c";s.p[:33];{{.*}}.cpp;{{[0-9]+}};{{[0-9]+}};;\00"
+// DEBUG: @{{[0-9]+}} = private unnamed_addr constant [{{[0-9]+}} x i8] c";ps->p[:33];{{.*}}.cpp;{{[0-9]+}};{{[0-9]+}};;\00"
+// DEBUG: @{{[0-9]+}} = private unnamed_addr constant [{{[0-9]+}} x i8] c";s.s;{{.*}}.cpp;{{[0-9]+}};{{[0-9]+}};;\00"
+
+struct S1 {
+ int i;
+ float f[50];
+};
+
+struct S2 {
+ int i;
+ float f[50];
+ S1 s;
+ double *p;
+ struct S2 *ps;
+};
+
+void foo() {
+ double d;
+ int i[100];
+ float *p;
+
+ S2 s;
+ S2 *ps;
+
+ [[omp::directive(target map(d))]]
+ { }
+ [[omp::directive(target map(i))]]
+ { }
+ [[omp::directive(target map(i[1:23]))]]
+ { }
+ [[omp::directive(target map(p))]]
+ { }
+ [[omp::directive(target map(p[1:24]))]]
+ { }
+ [[omp::directive(target map(s))]]
+ { }
+ [[omp::directive(target map(s.i))]]
+ { }
+ [[omp::directive(target map(s.s.f))]]
+ { }
+ [[omp::directive(target map(s.p))]]
+ { }
+ [[omp::directive(target map(to: s.p[:22]))]]
+ { }
+ [[omp::directive(target map(s.ps))]]
+ { }
+ [[omp::directive(target map(from: s.ps->s.i))]]
+ { }
+ [[omp::directive(target map(to: s.ps->ps))]]
+ { }
+ [[omp::directive(target map(s.ps->ps->ps))]]
+ { }
+ [[omp::directive(target map(to: s.ps->ps->s.f[:22]))]]
+ { }
+ [[omp::directive(target map(ps))]]
+ { }
+ [[omp::directive(target map(ps->i))]]
+ { }
+ [[omp::directive(target map(ps->s.f))]]
+ { }
+ [[omp::directive(target map(from: ps->p))]]
+ { }
+ [[omp::directive(target map(to: ps->p[:22]))]]
+ { }
+ [[omp::directive(target map(ps->ps))]]
+ { }
+ [[omp::directive(target map(from: ps->ps->s.i))]]
+ { }
+ [[omp::directive(target map(from: ps->ps->ps))]]
+ { }
+ [[omp::directive(target map(ps->ps->ps->ps))]]
+ { }
+ [[omp::directive(target map(to: ps->ps->ps->s.f[:22]))]]
+ { }
+ [[omp::directive(target map(to: s.f[:22]) map(from: s.p[:33]))]]
+ { }
+ [[omp::directive(target map(from: s.f[:22]) map(to: ps->p[:33]))]]
+ { }
+ [[omp::directive(target map(from: s.f[:22], s.s) map(to: ps->p[:33]))]]
+ { }
+}
+
+// DEBUG: @{{[0-9]+}} = private unnamed_addr constant [{{[0-9]+}} x i8] c";B;{{.*}}.cpp;{{[0-9]+}};{{[0-9]+}};;\00"
+// DEBUG: @{{[0-9]+}} = private unnamed_addr constant [{{[0-9]+}} x i8] c";unknown;unknown;0;0;;\00"
+// DEBUG: @{{[0-9]+}} = private unnamed_addr constant [{{[0-9]+}} x i8] c";A;{{.*}}.cpp;{{[0-9]+}};{{[0-9]+}};;\00"
+// DEBUG: @{{[0-9]+}} = private unnamed_addr constant [{{[0-9]+}} x i8] c";x;{{.*}}.cpp;{{[0-9]+}};{{[0-9]+}};;\00"
+// DEBUG: @{{[0-9]+}} = private unnamed_addr constant [{{[0-9]+}} x i8] c";fn;{{.*}}.cpp;{{[0-9]+}};{{[0-9]+}};;\00"
+// DEBUG: @{{[0-9]+}} = private unnamed_addr constant [{{[0-9]+}} x i8] c";s;{{.*}}.cpp;{{[0-9]+}};{{[0-9]+}};;\00"
+// DEBUG: @{{.+}} = private constant [7 x i8*] [i8* getelementptr inbounds ([{{[0-9]+}} x i8], [{{[0-9]+}} x i8]* @{{[0-9]+}}, i32 0, i32 0), i8* getelementptr inbounds ([{{[0-9]+}} x i8], [{{[0-9]+}} x i8]* @{{[0-9]+}}, i32 0, i32 0), i8* getelementptr inbounds ([{{[0-9]+}} x i8], [{{[0-9]+}} x i8]* @{{[0-9]+}}, i32 0, i32 0), i8* getelementptr inbounds ([{{[0-9]+}} x i8], [{{[0-9]+}} x i8]* @{{[0-9]+}}, i32 0, i32 0), i8* getelementptr inbounds ([{{[0-9]+}} x i8], [{{[0-9]+}} x i8]* @{{[0-9]+}}, i32 0, i32 0), i8* getelementptr inbounds ([{{[0-9]+}} x i8], [{{[0-9]+}} x i8]* @{{[0-9]+}}, i32 0, i32 0), i8* getelementptr inbounds ([{{[0-9]+}} x i8], [{{[0-9]+}} x i8]* @{{[0-9]+}}, i32 0, i32 0)]
+
+void bar(int N) {
+ double B[10];
+ double A[N];
+ double x;
+ S1 s;
+ auto fn = [&x]() { return x; };
+ [[omp::directive(target)]]
+ {
+ (void)B;
+ (void)A;
+ (void)fn();
+ (void)s.f;
+ }
+}
+
+// DEBUG: @{{[0-9]+}} = private unnamed_addr constant [{{[0-9]+}} x i8] c";t;{{.*}}.cpp;{{[0-9]+}};{{[0-9]+}};;\00"
+
+[[omp::directive(declare target)]];
+double t;
+[[omp::directive(end declare target)]];
+
+void baz() {
+ [[omp::directive(target map(to:t))]]
+ { }
+ [[omp::directive(target map(to:t) nowait)]]
+ { }
+ [[omp::directive(target teams map(to:t))]]
+ { }
+ [[omp::directive(target teams map(to:t) nowait)]]
+ { }
+ [[omp::directive(target data map(to:t))]]
+ { }
+ [[omp::sequence(directive(target enter data map(to:t)),
+ directive(target enter data map(to:t) nowait),
+ directive(target exit data map(from:t)),
+ directive(target exit data map(from:t) nowait),
+ directive(target update from(t)),
+ directive(target update to(t)),
+ directive(target update from(t) nowait),
+ directive(target update to(t) nowait))]];
+}
+
+struct S3 {
+ double Z[64];
+};
+
+[[omp::directive(declare mapper(id: S3 s) map(s.Z[0:64]))]]
+void qux() {
+ S3 s;
+ [[omp::directive(target map(mapper(id), to:s))]]
+ { }
+}
+
+// DEBUG: @{{[0-9]+}} = private unnamed_addr constant [{{[0-9]+}} x i8] c";s.Z[0:64];{{.*}}.cpp;{{[0-9]+}};{{[0-9]+}};;\00"
+
+// Clang used to mistakenly generate the map name "x" for both x and y on this
+// directive. Conditions to reproduce the bug: a single map clause has two
+// variables, and at least the second is used in the associated statement.
+//
+// DEBUG: @{{[0-9]+}} = private unnamed_addr constant [{{[0-9]+}} x i8] c";x;{{.*}}.cpp;[[@LINE+3]];7;;\00"
+// DEBUG: @{{[0-9]+}} = private unnamed_addr constant [{{[0-9]+}} x i8] c";y;{{.*}}.cpp;[[@LINE+2]];10;;\00"
+void secondMapNameInClause() {
+ int x, y;
+ [[omp::directive(target map(to: x, y))]];
+ x = y = 1;
+}
+
+// DEBUG: %{{.+}} = call i32 @__tgt_target_mapper(%struct.ident_t* @{{.+}}, i64 -1, i8* @{{.+}}, i32 1, i8** %{{.+}}, i8** %{{.+}}, i64* {{.+}}, i64* {{.+}}, i8** getelementptr inbounds ([{{[0-9]+}} x i8*], [{{[0-9]+}} x i8*]* @.offload_mapnames{{.*}}, i32 0, i32 0), i8** {{.+}})
+// DEBUG: %{{.+}} = call i32 @__tgt_target_teams_mapper(%struct.ident_t* @{{.+}}, i64 -1, i8* @{{.+}}, i32 1, i8** %{{.+}}, i8** %{{.+}}, i64* {{.+}}, i64* {{.+}}, i8** getelementptr inbounds ([{{[0-9]+}} x i8*], [{{[0-9]+}} x i8*]* @.offload_mapnames{{.*}}, i32 0, i32 0), i8** {{.+}}, i32 {{.+}}, i32 {{.+}})
+// DEBUG: call void @__tgt_target_data_begin_mapper(%struct.ident_t* @{{.+}}, i64 -1, i32 1, i8** %{{.+}}, i8** %{{.+}}, i64* {{.+}}, i64* {{.+}}, i8** getelementptr inbounds ([{{[0-9]+}} x i8*], [{{[0-9]+}} x i8*]* @.offload_mapnames{{.*}}, i32 0, i32 0), i8** {{.+}})
+// DEBUG: call void @__tgt_target_data_end_mapper(%struct.ident_t* @{{.+}}, i64 -1, i32 1, i8** %{{.+}}, i8** %{{.+}}, i64* {{.+}}, i64* {{.+}}, i8** getelementptr inbounds ([{{[0-9]+}} x i8*], [{{[0-9]+}} x i8*]* @.offload_mapnames{{.*}}, i32 0, i32 0), i8** {{.+}})
+// DEBUG: call void @__tgt_target_data_update_mapper(%struct.ident_t* @{{.+}}, i64 -1, i32 1, i8** %{{.+}}, i8** %{{.+}}, i64* {{.+}}, i64* {{.+}}, i8** getelementptr inbounds ([{{[0-9]+}} x i8*], [{{[0-9]+}} x i8*]* @.offload_mapnames{{.*}}, i32 0, i32 0), i8** {{.+}})
+// DEBUG: %{{.+}} = call i32 @__tgt_target_nowait_mapper(%struct.ident_t* @{{.+}}, i64 -1, i8* @{{.+}}, i32 1, i8** %{{.+}}, i8** %{{.+}}, i64* %{{.+}}, i64* {{.+}}, i8** getelementptr inbounds ([{{[0-9]+}} x i8*], [{{[0-9]+}} x i8*]* @.offload_mapnames{{.*}}, i32 0, i32 0), i8** {{.+}})
+// DEBUG: %{{.+}} = call i32 @__tgt_target_teams_nowait_mapper(%struct.ident_t* @{{.+}}, i64 -1, i8* @{{.+}}, i32 1, i8** %{{.+}}, i8** %{{.+}}, i64* {{.+}}, i64* {{.+}}, i8** getelementptr inbounds ([{{[0-9]+}} x i8*], [{{[0-9]+}} x i8*]* @.offload_mapnames{{.*}}, i32 0, i32 0), i8** {{.+}}, i32 {{.+}}, i32 {{.+}})
+// DEBUG: call void @__tgt_target_data_begin_nowait_mapper(%struct.ident_t* @{{.+}}, i64 -1, i32 1, i8** %{{.+}}, i8** %{{.+}}, i64* {{.+}}, i64* {{.+}}, i8** getelementptr inbounds ([{{[0-9]+}} x i8*], [{{[0-9]+}} x i8*]* @.offload_mapnames{{.*}}, i32 0, i32 0), i8** {{.+}})
+// DEBUG: call void @__tgt_target_data_end_nowait_mapper(%struct.ident_t* @{{.+}}, i64 -1, i32 1, i8** %{{.+}}, i8** %{{.+}}, i64* {{.+}}, i64* {{.+}}, i8** getelementptr inbounds ([{{[0-9]+}} x i8*], [{{[0-9]+}} x i8*]* @.offload_mapnames{{.*}}, i32 0, i32 0), i8** {{.+}})
+// DEBUG: call void @__tgt_target_data_update_nowait_mapper(%struct.ident_t* @{{.+}}, i64 -1, i32 1, i8** %{{.+}}, i8** %{{.+}}, i64* {{.+}}, i64* {{.+}}, i8** getelementptr inbounds ([{{[0-9]+}} x i8*], [{{[0-9]+}} x i8*]* @.offload_mapnames{{.*}}, i32 0, i32 0), i8** {{.+}})
+
+// CHECK: %{{.+}} = call i32 @__tgt_target_mapper(%struct.ident_t* @{{.+}}, i64 -1, i8* @{{.+}}, i32 1, i8** %{{.+}}, i8** %{{.+}}, i64* {{.+}}, i64* {{.+}}, i8** null, i8** {{.+}})
+// CHECK: %{{.+}} = call i32 @__tgt_target_teams_mapper(%struct.ident_t* @{{.+}}, i64 -1, i8* @{{.+}}, i32 1, i8** %{{.+}}, i8** %{{.+}}, i64* {{.+}}, i64* {{.+}}, i8** null, i8** {{.+}}, i32 {{.+}}, i32 {{.+}})
+// CHECK: call void @__tgt_target_data_begin_mapper(%struct.ident_t* @{{.+}}, i64 -1, i32 1, i8** %{{.+}}, i8** %{{.+}}, i64* {{.+}}, i64* {{.+}}, i8** null, i8** {{.+}})
+// CHECK: call void @__tgt_target_data_end_mapper(%struct.ident_t* @{{.+}}, i64 -1, i32 1, i8** %{{.+}}, i8** %{{.+}}, i64* {{.+}}, i64* {{.+}}, i8** null, i8** {{.+}})
+// CHECK: call void @__tgt_target_data_update_mapper(%struct.ident_t* @{{.+}}, i64 -1, i32 1, i8** %{{.+}}, i8** %{{.+}}, i64* {{.+}}, i64* {{.+}}, i8** null, i8** {{.+}})
+// CHECK: %{{.+}} = call i32 @__tgt_target_nowait_mapper(%struct.ident_t* @{{.+}}, i64 -1, i8* @{{.+}}, i32 1, i8** %{{.+}}, i8** %{{.+}}, i64* %{{.+}}, i64* {{.+}}, i8** null, i8** {{.+}})
+// CHECK: %{{.+}} = call i32 @__tgt_target_teams_nowait_mapper(%struct.ident_t* @{{.+}}, i64 -1, i8* @{{.+}}, i32 1, i8** %{{.+}}, i8** %{{.+}}, i64* {{.+}}, i64* {{.+}}, i8** null, i8** {{.+}}, i32 {{.+}}, i32 {{.+}})
+// CHECK: call void @__tgt_target_data_begin_nowait_mapper(%struct.ident_t* @{{.+}}, i64 -1, i32 1, i8** %{{.+}}, i8** %{{.+}}, i64* {{.+}}, i64* {{.+}}, i8** null, i8** {{.+}})
+// CHECK: call void @__tgt_target_data_end_nowait_mapper(%struct.ident_t* @{{.+}}, i64 -1, i32 1, i8** %{{.+}}, i8** %{{.+}}, i64* {{.+}}, i64* {{.+}}, i8** null, i8** {{.+}})
+// CHECK: call void @__tgt_target_data_update_nowait_mapper(%struct.ident_t* @{{.+}}, i64 -1, i32 1, i8** %{{.+}}, i8** %{{.+}}, i64* {{.+}}, i64* {{.+}}, i8** null, i8** {{.+}})
+
+
+// DEBUG: void @.omp_mapper._ZTS2S3.id(i8* {{.*}}, i8* {{.*}}, i8* {{.*}}, i64 {{.*}}, i64 {{.*}}, i8* [[NAME_ARG:%.+]])
+// DEBUG: store i8* [[NAME_ARG]], i8** [[NAME_STACK:%.+]]
+// DEBUG: [[MAPPER_NAME:%.+]] = load i8*, i8** [[NAME_STACK]]
+// DEBUG: call void @__tgt_push_mapper_component(i8* %{{.*}}, i8* %{{.*}}, i8* %{{.*}}, i64 %{{.*}}, i64 %{{.*}}, i8* [[MAPPER_NAME]])
+
+#endif
+
--- /dev/null
+// RUN: %clang_cc1 -verify -fopenmp -fopenmp-version=51 -fsyntax-only %s -Wuninitialized
+// RUN: %clang_cc1 -verify -fopenmp -fopenmp-version=51 -std=c++11 -fsyntax-only %s -Wuninitialized
+// RUN: %clang_cc1 -verify -fopenmp-simd -fopenmp-version=51 -fsyntax-only %s -Wuninitialized
+// RUN: %clang_cc1 -verify -fopenmp-simd -fopenmp-version=51 -std=c++11 -fsyntax-only %s -Wuninitialized
+
+typedef void **omp_allocator_handle_t;
+extern const omp_allocator_handle_t omp_null_allocator;
+extern const omp_allocator_handle_t omp_default_mem_alloc;
+extern const omp_allocator_handle_t omp_large_cap_mem_alloc;
+extern const omp_allocator_handle_t omp_const_mem_alloc;
+extern const omp_allocator_handle_t omp_high_bw_mem_alloc;
+extern const omp_allocator_handle_t omp_low_lat_mem_alloc;
+extern const omp_allocator_handle_t omp_cgroup_mem_alloc;
+extern const omp_allocator_handle_t omp_pteam_mem_alloc;
+extern const omp_allocator_handle_t omp_thread_mem_alloc;
+
+void xxx(int argc) {
+ int fp; // expected-note {{initialize the variable 'fp' to silence this warning}}
+ [[omp::directive(taskloop reduction(+:fp))]] // expected-warning {{variable 'fp' is uninitialized when used here}}
+ for (int i = 0; i < 10; ++i)
+ ;
+}
+
+void foo() {
+}
+
+bool foobool(int argc) {
+ return argc;
+}
+
+void foobar(int &ref) {
+ [[omp::directive(taskloop reduction(+:ref))]]
+ for (int i = 0; i < 10; ++i)
+ foo();
+}
+
+struct S1; // expected-note {{declared here}} expected-note 4 {{forward declaration of 'S1'}}
+extern S1 a;
+class S2 {
+ mutable int a;
+ S2 &operator+(const S2 &arg) { return (*this); } // expected-note 3 {{implicitly declared private here}}
+
+public:
+ S2() : a(0) {}
+ S2(S2 &s2) : a(s2.a) {}
+ static float S2s; // expected-note 2 {{static data member is predetermined as shared}}
+ static const float S2sc; // expected-note 2 {{'S2sc' declared here}}
+};
+const float S2::S2sc = 0;
+S2 b; // expected-note 3 {{'b' defined here}}
+const S2 ba[5]; // expected-note 2 {{'ba' defined here}}
+class S3 {
+ int a;
+
+public:
+ int b;
+ S3() : a(0) {}
+ S3(const S3 &s3) : a(s3.a) {}
+ S3 operator+(const S3 &arg1) { return arg1; }
+};
+int operator+(const S3 &arg1, const S3 &arg2) { return 5; }
+S3 c; // expected-note 3 {{'c' defined here}}
+const S3 ca[5]; // expected-note 2 {{'ca' defined here}}
+extern const int f; // expected-note 4 {{'f' declared here}}
+class S4 {
+ int a;
+ S4(); // expected-note {{implicitly declared private here}}
+ S4(const S4 &s4);
+ S4 &operator+(const S4 &arg) { return (*this); }
+
+public:
+ S4(int v) : a(v) {}
+};
+S4 &operator&=(S4 &arg1, S4 &arg2) { return arg1; }
+class S5 {
+ int a:32;
+ S5() : a(0) {} // expected-note {{implicitly declared private here}}
+ S5(const S5 &s5) : a(s5.a) {}
+ S5 &operator+(const S5 &arg);
+
+public:
+ S5(int v) : a(v) {}
+};
+class S6 { // expected-note 3 {{candidate function (the implicit copy assignment operator) not viable: no known conversion from 'int' to 'const S6' for 1st argument}}
+#if __cplusplus >= 201103L // C++11 or later
+// expected-note@-2 3 {{candidate function (the implicit move assignment operator) not viable}}
+#endif
+ int a;
+
+public:
+ S6() : a(6) {}
+ operator int() { return 6; }
+} o;
+
+struct S7 {
+ int a: 32;
+ S7() {
+ [[omp::directive(taskloop reduction(+:a))]] // expected-error {{expected addressable reduction item for the task-based directives}}
+ for (int i = 0; i < 10; ++i)
+ ++a;
+ }
+};
+
+S3 h, k;
+[[omp::directive(threadprivate(h))]]; // expected-note 2 {{defined as threadprivate or thread local}}
+
+template <class T> // expected-note {{declared here}}
+T tmain(T argc) {
+ const T d = T(); // expected-note 4 {{'d' defined here}}
+ const T da[5] = {T()}; // expected-note 2 {{'da' defined here}}
+ T qa[5] = {T()};
+ T i, z;
+ T &j = i; // expected-note 4 {{'j' defined here}}
+ S3 &p = k; // expected-note 2 {{'p' defined here}}
+ const T &r = da[(int)i]; // expected-note 2 {{'r' defined here}}
+ T &q = qa[(int)i]; // expected-note 2 {{'q' defined here}}
+ T fl;
+ [[omp::directive(taskloop reduction)]] // expected-error {{expected '(' after 'reduction'}}
+ for (int i = 0; i < 10; ++i)
+ foo();
+ [[omp::directive(taskloop reduction +)]] // expected-error {{expected '(' after 'reduction'}} expected-warning {{extra tokens at the end of '#pragma omp taskloop' are ignored}}
+ for (int i = 0; i < 10; ++i)
+ foo();
+ [[omp::directive(taskloop reduction())]] // expected-error {{expected unqualified-id}} expected-warning {{missing ':' after reduction identifier - ignoring}}
+ for (int i = 0; i < 10; ++i)
+ foo();
+ [[omp::directive(taskloop reduction(*))]] // expected-warning {{missing ':' after reduction identifier - ignoring}} expected-error {{expected expression}}
+ for (int i = 0; i < 10; ++i)
+ foo();
+ [[omp::directive(taskloop reduction(\))]] // expected-error {{expected unqualified-id}} expected-warning {{missing ':' after reduction identifier - ignoring}}
+ for (int i = 0; i < 10; ++i)
+ foo();
+ [[omp::directive(taskloop reduction(foo : argc))]] //expected-error {{incorrect reduction identifier, expected one of '+', '-', '*', '&', '|', '^', '&&', '||', 'min' or 'max' or declare reduction for type 'float'}} expected-error {{incorrect reduction identifier, expected one of '+', '-', '*', '&', '|', '^', '&&', '||', 'min' or 'max' or declare reduction for type 'int'}}
+ for (int i = 0; i < 10; ++i)
+ foo();
+ [[omp::directive(taskloop reduction(^ : T))]] // expected-error {{'T' does not refer to a value}}
+ for (int i = 0; i < 10; ++i)
+ foo();
+ [[omp::directive(taskloop reduction(+ : z, a, b, c, d, f))]] // expected-error {{a reduction list item with incomplete type 'S1'}} expected-error 3 {{const-qualified variable cannot be reduction}} expected-error 2 {{'operator+' is a private member of 'S2'}}
+ for (int i = 0; i < 10; ++i)
+ foo();
+ [[omp::directive(taskloop reduction(min : a, b, c, d, f))]] // expected-error {{a reduction list item with incomplete type 'S1'}} expected-error 4 {{arguments of OpenMP clause 'reduction' for 'min' or 'max' must be of arithmetic type}} expected-error 3 {{const-qualified variable cannot be reduction}}
+ for (int i = 0; i < 10; ++i)
+ foo();
+ [[omp::directive(taskloop reduction(max : h.b))]] // expected-error {{expected variable name, array element or array section}}
+ for (int i = 0; i < 10; ++i)
+ foo();
+ [[omp::directive(taskloop reduction(+ : ba))]] // expected-error {{const-qualified variable cannot be reduction}}
+ for (int i = 0; i < 10; ++i)
+ foo();
+ [[omp::directive(taskloop reduction(* : ca))]] // expected-error {{const-qualified variable cannot be reduction}}
+ for (int i = 0; i < 10; ++i)
+ foo();
+ [[omp::directive(taskloop reduction(- : da))]] // expected-error {{const-qualified variable cannot be reduction}} expected-error {{const-qualified variable cannot be reduction}}
+ for (int i = 0; i < 10; ++i)
+ foo();
+ [[omp::directive(taskloop reduction(^ : fl))]] // expected-error {{invalid operands to binary expression ('float' and 'float')}}
+ for (int i = 0; i < 10; ++i)
+ foo();
+ [[omp::directive(taskloop reduction(&& : S2::S2s))]] // expected-error {{shared variable cannot be reduction}}
+ for (int i = 0; i < 10; ++i)
+ foo();
+ [[omp::directive(taskloop reduction(&& : S2::S2sc))]] // expected-error {{const-qualified variable cannot be reduction}}
+ for (int i = 0; i < 10; ++i)
+ foo();
+ [[omp::directive(taskloop reduction(+ : h, k))]] // expected-error {{threadprivate or thread local variable cannot be reduction}}
+ for (int i = 0; i < 10; ++i)
+ foo();
+ [[omp::directive(taskloop reduction(+ : o))]] // expected-error 2 {{no viable overloaded '='}}
+ for (int i = 0; i < 10; ++i)
+ foo();
+ [[omp::directive(taskloop private(i), reduction(+ : j), reduction(+ : q))]] // expected-error 4 {{argument of OpenMP clause 'reduction' must reference the same object in all threads}}
+ for (int i = 0; i < 10; ++i)
+ foo();
+ [[omp::sequence(directive(parallel private(k)), directive(taskloop reduction(+ : p), reduction(+ : p)))]] // expected-error 2 {{argument of OpenMP clause 'reduction' must reference the same object in all threads}}
+ for (int i = 0; i < 10; ++i)
+ foo();
+ [[omp::directive(taskloop reduction(+ : p), reduction(+ : p))]] // expected-error 2 {{variable can appear only once in OpenMP 'reduction' clause}} expected-note 2 {{previously referenced here}}
+ for (int i = 0; i < 10; ++i)
+ foo();
+ [[omp::directive(taskloop reduction(+ : r))]] // expected-error 2 {{const-qualified variable cannot be reduction}}
+ for (int i = 0; i < 10; ++i)
+ foo();
+ [[omp::sequence(directive(parallel shared(i)), directive(parallel reduction(min : i)), directive(taskloop reduction(max : j)))]] // expected-error 2 {{argument of OpenMP clause 'reduction' must reference the same object in all threads}}
+ for (int i = 0; i < 10; ++i)
+ foo();
+ [[omp::sequence(directive(parallel private(fl)), directive(taskloop reduction(+ : fl) allocate(omp_thread_mem_alloc: fl)))]] // expected-warning 2 {{allocator with the 'thread' trait access has unspecified behavior on 'taskloop' directive}}
+ for (int i = 0; i < 10; ++i)
+ foo();
+ [[omp::sequence(directive(parallel reduction(* : fl)), directive(taskloop reduction(+ : fl)))]]
+ for (int i = 0; i < 10; ++i)
+ foo();
+
+ return T();
+}
+
+namespace A {
+double x;
+[[omp::directive(threadprivate(x))]]; // expected-note {{defined as threadprivate or thread local}}
+}
+namespace B {
+using A::x;
+}
+
+int main(int argc, char **argv) {
+ const int d = 5; // expected-note 2 {{'d' defined here}}
+ const int da[5] = {0}; // expected-note {{'da' defined here}}
+ int qa[5] = {0};
+ S4 e(4);
+ S5 g(5);
+ int i, z;
+ int &j = i; // expected-note 2 {{'j' defined here}}
+ S3 &p = k; // expected-note 2 {{'p' defined here}}
+ const int &r = da[i]; // expected-note {{'r' defined here}}
+ int &q = qa[i]; // expected-note {{'q' defined here}}
+ float fl;
+ [[omp::directive(taskloop reduction)]] // expected-error {{expected '(' after 'reduction'}}
+ for (int i = 0; i < 10; ++i)
+ foo();
+ [[omp::directive(taskloop reduction +)]] // expected-error {{expected '(' after 'reduction'}} expected-warning {{extra tokens at the end of '#pragma omp taskloop' are ignored}}
+ for (int i = 0; i < 10; ++i)
+ foo();
+ [[omp::directive(taskloop reduction())]] // expected-error {{expected unqualified-id}} expected-warning {{missing ':' after reduction identifier - ignoring}}
+ for (int i = 0; i < 10; ++i)
+ foo();
+ [[omp::directive(taskloop reduction(*))]] // expected-warning {{missing ':' after reduction identifier - ignoring}} expected-error {{expected expression}}
+ for (int i = 0; i < 10; ++i)
+ foo();
+ [[omp::directive(taskloop reduction(\))]] // expected-error {{expected unqualified-id}} expected-warning {{missing ':' after reduction identifier - ignoring}}
+ for (int i = 0; i < 10; ++i)
+ foo();
+ [[omp::directive(taskloop reduction(~ : argc))]] // expected-error {{expected unqualified-id}}
+ for (int i = 0; i < 10; ++i)
+ foo();
+ [[omp::directive(taskloop reduction(&& : argc, z))]]
+ for (int i = 0; i < 10; ++i)
+ foo();
+ [[omp::directive(taskloop reduction(^ : S1))]] // expected-error {{'S1' does not refer to a value}}
+ for (int i = 0; i < 10; ++i)
+ foo();
+ [[omp::directive(taskloop reduction(+ : a, b, c, d, f))]] // expected-error {{a reduction list item with incomplete type 'S1'}} expected-error 2 {{const-qualified variable cannot be reduction}} expected-error {{'operator+' is a private member of 'S2'}}
+ for (int i = 0; i < 10; ++i)
+ foo();
+ [[omp::directive(taskloop reduction(min : a, b, c, d, f))]] // expected-error {{a reduction list item with incomplete type 'S1'}} expected-error 2 {{arguments of OpenMP clause 'reduction' for 'min' or 'max' must be of arithmetic type}} expected-error 2 {{const-qualified variable cannot be reduction}}
+ for (int i = 0; i < 10; ++i)
+ foo();
+ [[omp::directive(taskloop reduction(max : h.b))]] // expected-error {{expected variable name, array element or array section}}
+ for (int i = 0; i < 10; ++i)
+ foo();
+ [[omp::directive(taskloop reduction(+ : ba))]] // expected-error {{const-qualified variable cannot be reduction}}
+ for (int i = 0; i < 10; ++i)
+ foo();
+ [[omp::directive(taskloop reduction(* : ca))]] // expected-error {{const-qualified variable cannot be reduction}}
+ for (int i = 0; i < 10; ++i)
+ foo();
+ [[omp::directive(taskloop reduction(- : da))]] // expected-error {{const-qualified variable cannot be reduction}}
+ for (int i = 0; i < 10; ++i)
+ foo();
+ [[omp::directive(taskloop reduction(^ : fl))]] // expected-error {{invalid operands to binary expression ('float' and 'float')}}
+ for (int i = 0; i < 10; ++i)
+ foo();
+ [[omp::directive(taskloop reduction(&& : S2::S2s))]] // expected-error {{shared variable cannot be reduction}}
+ for (int i = 0; i < 10; ++i)
+ foo();
+ [[omp::directive(taskloop reduction(&& : S2::S2sc))]] // expected-error {{const-qualified variable cannot be reduction}}
+ for (int i = 0; i < 10; ++i)
+ foo();
+ [[omp::directive(taskloop reduction(& : e, g))]] // expected-error {{calling a private constructor of class 'S4'}} expected-error {{calling a private constructor of class 'S5'}} expected-error {{invalid operands to binary expression ('S5' and 'S5')}}
+ for (int i = 0; i < 10; ++i)
+ foo();
+ [[omp::directive(taskloop reduction(+ : h, k, B::x))]] // expected-error 2 {{threadprivate or thread local variable cannot be reduction}}
+ for (int i = 0; i < 10; ++i)
+ foo();
+ [[omp::directive(taskloop reduction(+ : o))]] // expected-error {{no viable overloaded '='}}
+ for (int i = 0; i < 10; ++i)
+ foo();
+ [[omp::directive(taskloop private(i), reduction(+ : j), reduction(+ : q))]] // expected-error 2 {{argument of OpenMP clause 'reduction' must reference the same object in all threads}}
+ for (int i = 0; i < 10; ++i)
+ foo();
+ [[omp::sequence(directive(parallel private(k)), directive(taskloop reduction(+ : p), reduction(+ : p)))]] // expected-error 2 {{argument of OpenMP clause 'reduction' must reference the same object in all threads}}
+ for (int i = 0; i < 10; ++i)
+ foo();
+ [[omp::directive(taskloop reduction(+ : p), reduction(+ : p))]] // expected-error {{variable can appear only once in OpenMP 'reduction' clause}} expected-note {{previously referenced here}}
+ for (int i = 0; i < 10; ++i)
+ foo();
+ [[omp::directive(taskloop reduction(+ : r))]] // expected-error {{const-qualified variable cannot be reduction}}
+ for (int i = 0; i < 10; ++i)
+ foo();
+ [[omp::sequence(directive(parallel shared(i)), directive(parallel reduction(min : i)), directive(taskloop reduction(max : j)))]] // expected-error {{argument of OpenMP clause 'reduction' must reference the same object in all threads}}
+ for (int i = 0; i < 10; ++i)
+ foo();
+ [[omp::sequence(directive(parallel private(fl)), directive(taskloop reduction(+ : fl)))]]
+ for (int i = 0; i < 10; ++i)
+ foo();
+ [[omp::sequence(directive(parallel reduction(* : fl)), directive(taskloop reduction(+ : fl)))]]
+ for (int i = 0; i < 10; ++i)
+ foo();
+ static int m;
+ [[omp::directive(taskloop reduction(+ : m))]] // OK
+ for (int i = 0; i < 10; ++i)
+ m++;
+ [[omp::directive(taskloop reduction(task, + : m))]] // expected-error {{'reduction' clause with 'task' modifier allowed only on non-simd parallel or worksharing constructs}}
+ for (int i = 0; i < 10; ++i)
+ m++;
+ [[omp::directive(taskloop nogroup reduction(+ : m))]] // expected-error {{'reduction' clause cannot be used with 'nogroup' clause}}
+ for (int i = 0; i < 10; ++i)
+ m++;
+
+ return tmain(argc) + tmain(fl); // expected-note {{in instantiation of function template specialization 'tmain<int>' requested here}} expected-note {{in instantiation of function template specialization 'tmain<float>' requested here}}
+}
+
--- /dev/null
+// RUN: %clang_cc1 -verify -fsyntax-only -fopenmp -fopenmp-version=51 -std=c++11 %s -Wuninitialized
+// RUN: %clang_cc1 -verify -fsyntax-only -fopenmp-simd -fopenmp-version=51 -std=c++11 %s -Wuninitialized
+
+void foo() {
+}
+
+bool foobool(int argc) {
+ return argc;
+}
+
+struct S1; // expected-note 2 {{declared here}}
+
+template <typename T, int C> // expected-note {{declared here}}
+T tmain(T argc) {
+ char **a;
+ T k;
+ [[omp::sequence(directive(target), directive(teams distribute parallel for simd num_teams(C)))]]
+ for (int i=0; i<100; i++) foo();
+ [[omp::sequence(directive(target), directive(teams distribute parallel for simd num_teams(T)))]] // expected-error {{'T' does not refer to a value}}
+ for (int i=0; i<100; i++) foo();
+ [[omp::sequence(directive(target), directive(teams distribute parallel for simd num_teams))]] // expected-error {{expected '(' after 'num_teams'}}
+ for (int i=0; i<100; i++) foo();
+ [[omp::sequence(directive(target), directive(teams distribute parallel for simd num_teams()))]] // expected-error {{expected expression}}
+ for (int i=0; i<100; i++) foo();
+ [[omp::sequence(directive(target), directive(teams distribute parallel for simd num_teams(argc > 0 ? a[1] : a[2])))]] // expected-error {{expression must have integral or unscoped enumeration type, not 'char *'}}
+ for (int i=0; i<100; i++) foo();
+ [[omp::sequence(directive(target), directive(teams distribute parallel for simd num_teams(argc + k)))]]
+ for (int i=0; i<100; i++) foo();
+ [[omp::sequence(directive(target), directive(teams distribute parallel for simd num_teams(argc), num_teams (argc+1)))]] // expected-error {{directive '#pragma omp teams distribute parallel for simd' cannot contain more than one 'num_teams' clause}}
+ for (int i=0; i<100; i++) foo();
+ [[omp::sequence(directive(target), directive(teams distribute parallel for simd num_teams(S1)))]] // expected-error {{'S1' does not refer to a value}}
+ for (int i=0; i<100; i++) foo();
+ [[omp::sequence(directive(target), directive(teams distribute parallel for simd num_teams(-2)))]] // expected-error {{argument to 'num_teams' clause must be a strictly positive integer value}}
+ for (int i=0; i<100; i++) foo();
+ [[omp::sequence(directive(target), directive(teams distribute parallel for simd num_teams(-10u)))]]
+ for (int i=0; i<100; i++) foo();
+ [[omp::sequence(directive(target), directive(teams distribute parallel for simd num_teams(3.14)))]] // expected-error 2 {{expression must have integral or unscoped enumeration type, not 'double'}}
+ for (int i=0; i<100; i++) foo();
+
+ return 0;
+}
+
+int main(int argc, char **argv) {
+ int k;
+ [[omp::sequence(directive(target), directive(teams distribute parallel for simd num_teams))]] // expected-error {{expected '(' after 'num_teams'}}
+ for (int i=0; i<100; i++) foo();
+
+ [[omp::sequence(directive(target), directive(teams distribute parallel for simd num_teams ()))]] // expected-error {{expected expression}}
+ for (int i=0; i<100; i++) foo();
+
+ [[omp::sequence(directive(target), directive(teams distribute parallel for simd num_teams (argc > 0 ? argv[1] : argv[2])))]] // expected-error {{expression must have integral or unscoped enumeration type, not 'char *'}}
+ for (int i=0; i<100; i++) foo();
+
+ [[omp::sequence(directive(target), directive(teams distribute parallel for simd num_teams (argc + k)))]]
+ for (int i=0; i<100; i++) foo();
+
+ [[omp::sequence(directive(target), directive(teams distribute parallel for simd num_teams (argc), num_teams (argc+1)))]] // expected-error {{directive '#pragma omp teams distribute parallel for simd' cannot contain more than one 'num_teams' clause}}
+ for (int i=0; i<100; i++) foo();
+
+ [[omp::sequence(directive(target), directive(teams distribute parallel for simd num_teams (S1)))]] // expected-error {{'S1' does not refer to a value}}
+ for (int i=0; i<100; i++) foo();
+
+ [[omp::sequence(directive(target), directive(teams distribute parallel for simd num_teams (-2)))]] // expected-error {{argument to 'num_teams' clause must be a strictly positive integer value}}
+ for (int i=0; i<100; i++) foo();
+
+ [[omp::sequence(directive(target), directive(teams distribute parallel for simd num_teams (-10u)))]]
+ for (int i=0; i<100; i++) foo();
+
+ [[omp::sequence(directive(target), directive(teams distribute parallel for simd num_teams (3.14)))]] // expected-error {{expression must have integral or unscoped enumeration type, not 'double'}}
+ for (int i=0; i<100; i++) foo();
+
+ return tmain<int, 10>(argc); // expected-note {{in instantiation of function template specialization 'tmain<int, 10>' requested here}}
+}
--- /dev/null
+// Check code generation
+// RUN: %clang_cc1 -verify -triple x86_64-pc-linux-gnu -fopenmp -fopenmp-version=51 -emit-llvm %s -o - | FileCheck %s --check-prefix=IR
+
+// Check same results after serialization round-trip
+// RUN: %clang_cc1 -verify -triple x86_64-pc-linux-gnu -fopenmp -fopenmp-version=51 -emit-pch -o %t %s
+// RUN: %clang_cc1 -verify -triple x86_64-pc-linux-gnu -fopenmp -fopenmp-version=51 -include-pch %t -emit-llvm %s -o - | FileCheck %s --check-prefix=IR
+// expected-no-diagnostics
+
+#ifndef HEADER
+#define HEADER
+
+// placeholder for loop body code.
+extern "C" void body(...) {}
+
+
+// IR-LABEL: @func(
+// IR-NEXT: [[ENTRY:.*]]:
+// IR-NEXT: %[[START_ADDR:.+]] = alloca i32, align 4
+// IR-NEXT: %[[END_ADDR:.+]] = alloca i32, align 4
+// IR-NEXT: %[[STEP_ADDR:.+]] = alloca i32, align 4
+// IR-NEXT: %[[DOTOMP_IV:.+]] = alloca i32, align 4
+// IR-NEXT: %[[TMP:.+]] = alloca i32, align 4
+// IR-NEXT: %[[I:.+]] = alloca i32, align 4
+// IR-NEXT: %[[DOTCAPTURE_EXPR_:.+]] = alloca i32, align 4
+// IR-NEXT: %[[DOTCAPTURE_EXPR_1:.+]] = alloca i32, align 4
+// IR-NEXT: %[[DOTCAPTURE_EXPR_2:.+]] = alloca i32, align 4
+// IR-NEXT: %[[DOTCAPTURE_EXPR_3:.+]] = alloca i32, align 4
+// IR-NEXT: %[[DOTUNROLLED_IV_I:.+]] = alloca i32, align 4
+// IR-NEXT: %[[DOTCAPTURE_EXPR_6:.+]] = alloca i32, align 4
+// IR-NEXT: %[[DOTCAPTURE_EXPR_8:.+]] = alloca i32, align 4
+// IR-NEXT: %[[DOTCAPTURE_EXPR_12:.+]] = alloca i32, align 4
+// IR-NEXT: %[[DOTCAPTURE_EXPR_14:.+]] = alloca i32, align 4
+// IR-NEXT: %[[DOTUNROLLED_IV__UNROLLED_IV_I:.+]] = alloca i32, align 4
+// IR-NEXT: %[[DOTOMP_LB:.+]] = alloca i32, align 4
+// IR-NEXT: %[[DOTOMP_UB:.+]] = alloca i32, align 4
+// IR-NEXT: %[[DOTOMP_STRIDE:.+]] = alloca i32, align 4
+// IR-NEXT: %[[DOTOMP_IS_LAST:.+]] = alloca i32, align 4
+// IR-NEXT: %[[DOTUNROLLED_IV__UNROLLED_IV_I18:.+]] = alloca i32, align 4
+// IR-NEXT: %[[DOTUNROLL_INNER_IV__UNROLLED_IV_I:.+]] = alloca i32, align 4
+// IR-NEXT: %[[DOTUNROLL_INNER_IV_I:.+]] = alloca i32, align 4
+// IR-NEXT: %[[TMP0:.+]] = call i32 @__kmpc_global_thread_num(%struct.ident_t* @2)
+// IR-NEXT: store i32 %[[START:.+]], i32* %[[START_ADDR]], align 4
+// IR-NEXT: store i32 %[[END:.+]], i32* %[[END_ADDR]], align 4
+// IR-NEXT: store i32 %[[STEP:.+]], i32* %[[STEP_ADDR]], align 4
+// IR-NEXT: %[[TMP1:.+]] = load i32, i32* %[[START_ADDR]], align 4
+// IR-NEXT: store i32 %[[TMP1]], i32* %[[I]], align 4
+// IR-NEXT: %[[TMP2:.+]] = load i32, i32* %[[START_ADDR]], align 4
+// IR-NEXT: store i32 %[[TMP2]], i32* %[[DOTCAPTURE_EXPR_]], align 4
+// IR-NEXT: %[[TMP3:.+]] = load i32, i32* %[[END_ADDR]], align 4
+// IR-NEXT: store i32 %[[TMP3]], i32* %[[DOTCAPTURE_EXPR_1]], align 4
+// IR-NEXT: %[[TMP4:.+]] = load i32, i32* %[[STEP_ADDR]], align 4
+// IR-NEXT: store i32 %[[TMP4]], i32* %[[DOTCAPTURE_EXPR_2]], align 4
+// IR-NEXT: %[[TMP5:.+]] = load i32, i32* %[[DOTCAPTURE_EXPR_1]], align 4
+// IR-NEXT: %[[TMP6:.+]] = load i32, i32* %[[DOTCAPTURE_EXPR_]], align 4
+// IR-NEXT: %[[SUB:.+]] = sub i32 %[[TMP5]], %[[TMP6]]
+// IR-NEXT: %[[SUB4:.+]] = sub i32 %[[SUB]], 1
+// IR-NEXT: %[[TMP7:.+]] = load i32, i32* %[[DOTCAPTURE_EXPR_2]], align 4
+// IR-NEXT: %[[ADD:.+]] = add i32 %[[SUB4]], %[[TMP7]]
+// IR-NEXT: %[[TMP8:.+]] = load i32, i32* %[[DOTCAPTURE_EXPR_2]], align 4
+// IR-NEXT: %[[DIV:.+]] = udiv i32 %[[ADD]], %[[TMP8]]
+// IR-NEXT: %[[SUB5:.+]] = sub i32 %[[DIV]], 1
+// IR-NEXT: store i32 %[[SUB5]], i32* %[[DOTCAPTURE_EXPR_3]], align 4
+// IR-NEXT: store i32 0, i32* %[[DOTUNROLLED_IV_I]], align 4
+// IR-NEXT: %[[TMP9:.+]] = load i32, i32* %[[DOTCAPTURE_EXPR_3]], align 4
+// IR-NEXT: %[[ADD7:.+]] = add i32 %[[TMP9]], 1
+// IR-NEXT: store i32 %[[ADD7]], i32* %[[DOTCAPTURE_EXPR_6]], align 4
+// IR-NEXT: %[[TMP10:.+]] = load i32, i32* %[[DOTCAPTURE_EXPR_6]], align 4
+// IR-NEXT: %[[SUB9:.+]] = sub i32 %[[TMP10]], -1
+// IR-NEXT: %[[DIV10:.+]] = udiv i32 %[[SUB9]], 2
+// IR-NEXT: %[[SUB11:.+]] = sub i32 %[[DIV10]], 1
+// IR-NEXT: store i32 %[[SUB11]], i32* %[[DOTCAPTURE_EXPR_8]], align 4
+// IR-NEXT: %[[TMP11:.+]] = load i32, i32* %[[DOTCAPTURE_EXPR_8]], align 4
+// IR-NEXT: %[[ADD13:.+]] = add i32 %[[TMP11]], 1
+// IR-NEXT: store i32 %[[ADD13]], i32* %[[DOTCAPTURE_EXPR_12]], align 4
+// IR-NEXT: %[[TMP12:.+]] = load i32, i32* %[[DOTCAPTURE_EXPR_12]], align 4
+// IR-NEXT: %[[SUB15:.+]] = sub i32 %[[TMP12]], -1
+// IR-NEXT: %[[DIV16:.+]] = udiv i32 %[[SUB15]], 2
+// IR-NEXT: %[[SUB17:.+]] = sub i32 %[[DIV16]], 1
+// IR-NEXT: store i32 %[[SUB17]], i32* %[[DOTCAPTURE_EXPR_14]], align 4
+// IR-NEXT: store i32 0, i32* %[[DOTUNROLLED_IV__UNROLLED_IV_I]], align 4
+// IR-NEXT: %[[TMP13:.+]] = load i32, i32* %[[DOTCAPTURE_EXPR_12]], align 4
+// IR-NEXT: %[[CMP:.+]] = icmp ult i32 0, %[[TMP13]]
+// IR-NEXT: br i1 %[[CMP]], label %[[OMP_PRECOND_THEN:.+]], label %[[OMP_PRECOND_END:.+]]
+// IR-EMPTY:
+// IR-NEXT: [[OMP_PRECOND_THEN]]:
+// IR-NEXT: store i32 0, i32* %[[DOTOMP_LB]], align 4
+// IR-NEXT: %[[TMP14:.+]] = load i32, i32* %[[DOTCAPTURE_EXPR_14]], align 4
+// IR-NEXT: store i32 %[[TMP14]], i32* %[[DOTOMP_UB]], align 4
+// IR-NEXT: store i32 1, i32* %[[DOTOMP_STRIDE]], align 4
+// IR-NEXT: store i32 0, i32* %[[DOTOMP_IS_LAST]], align 4
+// IR-NEXT: call void @__kmpc_for_static_init_4u(%struct.ident_t* @1, i32 %[[TMP0]], i32 34, i32* %[[DOTOMP_IS_LAST]], i32* %[[DOTOMP_LB]], i32* %[[DOTOMP_UB]], i32* %[[DOTOMP_STRIDE]], i32 1, i32 1)
+// IR-NEXT: %[[TMP15:.+]] = load i32, i32* %[[DOTOMP_UB]], align 4
+// IR-NEXT: %[[TMP16:.+]] = load i32, i32* %[[DOTCAPTURE_EXPR_14]], align 4
+// IR-NEXT: %[[CMP19:.+]] = icmp ugt i32 %[[TMP15]], %[[TMP16]]
+// IR-NEXT: br i1 %[[CMP19]], label %[[COND_TRUE:.+]], label %[[COND_FALSE:.+]]
+// IR-EMPTY:
+// IR-NEXT: [[COND_TRUE]]:
+// IR-NEXT: %[[TMP17:.+]] = load i32, i32* %[[DOTCAPTURE_EXPR_14]], align 4
+// IR-NEXT: br label %[[COND_END:.+]]
+// IR-EMPTY:
+// IR-NEXT: [[COND_FALSE]]:
+// IR-NEXT: %[[TMP18:.+]] = load i32, i32* %[[DOTOMP_UB]], align 4
+// IR-NEXT: br label %[[COND_END]]
+// IR-EMPTY:
+// IR-NEXT: [[COND_END]]:
+// IR-NEXT: %[[COND:.+]] = phi i32 [ %[[TMP17]], %[[COND_TRUE]] ], [ %[[TMP18]], %[[COND_FALSE]] ]
+// IR-NEXT: store i32 %[[COND]], i32* %[[DOTOMP_UB]], align 4
+// IR-NEXT: %[[TMP19:.+]] = load i32, i32* %[[DOTOMP_LB]], align 4
+// IR-NEXT: store i32 %[[TMP19]], i32* %[[DOTOMP_IV]], align 4
+// IR-NEXT: br label %[[OMP_INNER_FOR_COND:.+]]
+// IR-EMPTY:
+// IR-NEXT: [[OMP_INNER_FOR_COND]]:
+// IR-NEXT: %[[TMP20:.+]] = load i32, i32* %[[DOTOMP_IV]], align 4
+// IR-NEXT: %[[TMP21:.+]] = load i32, i32* %[[DOTOMP_UB]], align 4
+// IR-NEXT: %[[ADD20:.+]] = add i32 %[[TMP21]], 1
+// IR-NEXT: %[[CMP21:.+]] = icmp ult i32 %[[TMP20]], %[[ADD20]]
+// IR-NEXT: br i1 %[[CMP21]], label %[[OMP_INNER_FOR_BODY:.+]], label %[[OMP_INNER_FOR_END:.+]]
+// IR-EMPTY:
+// IR-NEXT: [[OMP_INNER_FOR_BODY]]:
+// IR-NEXT: %[[TMP22:.+]] = load i32, i32* %[[DOTOMP_IV]], align 4
+// IR-NEXT: %[[MUL:.+]] = mul i32 %[[TMP22]], 2
+// IR-NEXT: %[[ADD22:.+]] = add i32 0, %[[MUL]]
+// IR-NEXT: store i32 %[[ADD22]], i32* %[[DOTUNROLLED_IV__UNROLLED_IV_I18]], align 4
+// IR-NEXT: %[[TMP23:.+]] = load i32, i32* %[[DOTUNROLLED_IV__UNROLLED_IV_I18]], align 4
+// IR-NEXT: store i32 %[[TMP23]], i32* %[[DOTUNROLL_INNER_IV__UNROLLED_IV_I]], align 4
+// IR-NEXT: br label %[[FOR_COND:.+]]
+// IR-EMPTY:
+// IR-NEXT: [[FOR_COND]]:
+// IR-NEXT: %[[TMP24:.+]] = load i32, i32* %[[DOTUNROLL_INNER_IV__UNROLLED_IV_I]], align 4
+// IR-NEXT: %[[TMP25:.+]] = load i32, i32* %[[DOTUNROLLED_IV__UNROLLED_IV_I18]], align 4
+// IR-NEXT: %[[ADD23:.+]] = add i32 %[[TMP25]], 2
+// IR-NEXT: %[[CMP24:.+]] = icmp ule i32 %[[TMP24]], %[[ADD23]]
+// IR-NEXT: br i1 %[[CMP24]], label %[[LAND_RHS:.+]], label %[[LAND_END:.+]]
+// IR-EMPTY:
+// IR-NEXT: [[LAND_RHS]]:
+// IR-NEXT: %[[TMP26:.+]] = load i32, i32* %[[DOTUNROLL_INNER_IV__UNROLLED_IV_I]], align 4
+// IR-NEXT: %[[TMP27:.+]] = load i32, i32* %[[DOTCAPTURE_EXPR_8]], align 4
+// IR-NEXT: %[[ADD25:.+]] = add i32 %[[TMP27]], 1
+// IR-NEXT: %[[CMP26:.+]] = icmp ule i32 %[[TMP26]], %[[ADD25]]
+// IR-NEXT: br label %[[LAND_END]]
+// IR-EMPTY:
+// IR-NEXT: [[LAND_END]]:
+// IR-NEXT: %[[TMP28:.+]] = phi i1 [ false, %[[FOR_COND]] ], [ %[[CMP26]], %[[LAND_RHS]] ]
+// IR-NEXT: br i1 %[[TMP28]], label %[[FOR_BODY:.+]], label %[[FOR_END41:.+]]
+// IR-EMPTY:
+// IR-NEXT: [[FOR_BODY]]:
+// IR-NEXT: %[[TMP29:.+]] = load i32, i32* %[[DOTUNROLL_INNER_IV__UNROLLED_IV_I]], align 4
+// IR-NEXT: %[[MUL27:.+]] = mul i32 %[[TMP29]], 2
+// IR-NEXT: %[[ADD28:.+]] = add i32 0, %[[MUL27]]
+// IR-NEXT: store i32 %[[ADD28]], i32* %[[DOTUNROLLED_IV_I]], align 4
+// IR-NEXT: %[[TMP30:.+]] = load i32, i32* %[[DOTUNROLLED_IV_I]], align 4
+// IR-NEXT: store i32 %[[TMP30]], i32* %[[DOTUNROLL_INNER_IV_I]], align 4
+// IR-NEXT: br label %[[FOR_COND29:.+]]
+// IR-EMPTY:
+// IR-NEXT: [[FOR_COND29]]:
+// IR-NEXT: %[[TMP31:.+]] = load i32, i32* %[[DOTUNROLL_INNER_IV_I]], align 4
+// IR-NEXT: %[[TMP32:.+]] = load i32, i32* %[[DOTUNROLLED_IV_I]], align 4
+// IR-NEXT: %[[ADD30:.+]] = add i32 %[[TMP32]], 2
+// IR-NEXT: %[[CMP31:.+]] = icmp ule i32 %[[TMP31]], %[[ADD30]]
+// IR-NEXT: br i1 %[[CMP31]], label %[[LAND_RHS32:.+]], label %[[LAND_END35:.+]]
+// IR-EMPTY:
+// IR-NEXT: [[LAND_RHS32]]:
+// IR-NEXT: %[[TMP33:.+]] = load i32, i32* %[[DOTUNROLL_INNER_IV_I]], align 4
+// IR-NEXT: %[[TMP34:.+]] = load i32, i32* %[[DOTCAPTURE_EXPR_3]], align 4
+// IR-NEXT: %[[ADD33:.+]] = add i32 %[[TMP34]], 1
+// IR-NEXT: %[[CMP34:.+]] = icmp ule i32 %[[TMP33]], %[[ADD33]]
+// IR-NEXT: br label %[[LAND_END35]]
+// IR-EMPTY:
+// IR-NEXT: [[LAND_END35]]:
+// IR-NEXT: %[[TMP35:.+]] = phi i1 [ false, %[[FOR_COND29]] ], [ %[[CMP34]], %[[LAND_RHS32]] ]
+// IR-NEXT: br i1 %[[TMP35]], label %[[FOR_BODY36:.+]], label %[[FOR_END:.+]]
+// IR-EMPTY:
+// IR-NEXT: [[FOR_BODY36]]:
+// IR-NEXT: %[[TMP36:.+]] = load i32, i32* %[[DOTCAPTURE_EXPR_]], align 4
+// IR-NEXT: %[[TMP37:.+]] = load i32, i32* %[[DOTUNROLL_INNER_IV_I]], align 4
+// IR-NEXT: %[[TMP38:.+]] = load i32, i32* %[[DOTCAPTURE_EXPR_2]], align 4
+// IR-NEXT: %[[MUL37:.+]] = mul i32 %[[TMP37]], %[[TMP38]]
+// IR-NEXT: %[[ADD38:.+]] = add i32 %[[TMP36]], %[[MUL37]]
+// IR-NEXT: store i32 %[[ADD38]], i32* %[[I]], align 4
+// IR-NEXT: %[[TMP39:.+]] = load i32, i32* %[[START_ADDR]], align 4
+// IR-NEXT: %[[TMP40:.+]] = load i32, i32* %[[END_ADDR]], align 4
+// IR-NEXT: %[[TMP41:.+]] = load i32, i32* %[[STEP_ADDR]], align 4
+// IR-NEXT: %[[TMP42:.+]] = load i32, i32* %[[I]], align 4
+// IR-NEXT: call void (...) @body(i32 %[[TMP39]], i32 %[[TMP40]], i32 %[[TMP41]], i32 %[[TMP42]])
+// IR-NEXT: br label %[[FOR_INC:.+]]
+// IR-EMPTY:
+// IR-NEXT: [[FOR_INC]]:
+// IR-NEXT: %[[TMP43:.+]] = load i32, i32* %[[DOTUNROLL_INNER_IV_I]], align 4
+// IR-NEXT: %[[INC:.+]] = add i32 %[[TMP43]], 1
+// IR-NEXT: store i32 %[[INC]], i32* %[[DOTUNROLL_INNER_IV_I]], align 4
+// IR-NEXT: br label %[[FOR_COND29]], !llvm.loop ![[LOOP2:[0-9]+]]
+// IR-EMPTY:
+// IR-NEXT: [[FOR_END]]:
+// IR-NEXT: br label %[[FOR_INC39:.+]]
+// IR-EMPTY:
+// IR-NEXT: [[FOR_INC39]]:
+// IR-NEXT: %[[TMP44:.+]] = load i32, i32* %[[DOTUNROLL_INNER_IV__UNROLLED_IV_I]], align 4
+// IR-NEXT: %[[INC40:.+]] = add i32 %[[TMP44]], 1
+// IR-NEXT: store i32 %[[INC40]], i32* %[[DOTUNROLL_INNER_IV__UNROLLED_IV_I]], align 4
+// IR-NEXT: br label %[[FOR_COND]], !llvm.loop ![[LOOP5:[0-9]+]]
+// IR-EMPTY:
+// IR-NEXT: [[FOR_END41]]:
+// IR-NEXT: br label %[[OMP_BODY_CONTINUE:.+]]
+// IR-EMPTY:
+// IR-NEXT: [[OMP_BODY_CONTINUE]]:
+// IR-NEXT: br label %[[OMP_INNER_FOR_INC:.+]]
+// IR-EMPTY:
+// IR-NEXT: [[OMP_INNER_FOR_INC]]:
+// IR-NEXT: %[[TMP45:.+]] = load i32, i32* %[[DOTOMP_IV]], align 4
+// IR-NEXT: %[[ADD42:.+]] = add i32 %[[TMP45]], 1
+// IR-NEXT: store i32 %[[ADD42]], i32* %[[DOTOMP_IV]], align 4
+// IR-NEXT: br label %[[OMP_INNER_FOR_COND]]
+// IR-EMPTY:
+// IR-NEXT: [[OMP_INNER_FOR_END]]:
+// IR-NEXT: br label %[[OMP_LOOP_EXIT:.+]]
+// IR-EMPTY:
+// IR-NEXT: [[OMP_LOOP_EXIT]]:
+// IR-NEXT: call void @__kmpc_for_static_fini(%struct.ident_t* @1, i32 %[[TMP0]])
+// IR-NEXT: br label %[[OMP_PRECOND_END]]
+// IR-EMPTY:
+// IR-NEXT: [[OMP_PRECOND_END]]:
+// IR-NEXT: call void @__kmpc_barrier(%struct.ident_t* @3, i32 %[[TMP0]])
+// IR-NEXT: ret void
+// IR-NEXT: }
+extern "C" void func(int start, int end, int step) {
+ [[omp::sequence(directive(for), directive(unroll partial), directive(unroll partial))]]
+ for (int i = start; i < end; i+=step)
+ body(start, end, step, i);
+}
+
+#endif /* HEADER */
+
+
+// IR: ![[LOOP2]] = distinct !{![[LOOP2]], ![[LOOPPROP3:[0-9]+]], ![[LOOPPROP4:[0-9]+]]}
+// IR: ![[LOOPPROP3]] = !{!"llvm.loop.mustprogress"}
+// IR: ![[LOOPPROP4]] = !{!"llvm.loop.unroll.count", i32 2}
+// IR: ![[LOOP5]] = distinct !{![[LOOP5]], ![[LOOPPROP3]], ![[LOOPPROP4]]}