Index: clang/docs/OpenMPSupport.rst =================================================================== --- clang/docs/OpenMPSupport.rst +++ clang/docs/OpenMPSupport.rst @@ -268,7 +268,7 @@ +------------------------------+--------------------------------------------------------------+--------------------------+-----------------------------------------------------------------------+ | 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 | +------------------------------+--------------------------------------------------------------+--------------------------+-----------------------------------------------------------------------+ Index: clang/include/clang/Basic/DiagnosticGroups.td =================================================================== --- clang/include/clang/Basic/DiagnosticGroups.td +++ clang/include/clang/Basic/DiagnosticGroups.td @@ -1186,8 +1186,11 @@ 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. Index: clang/include/clang/Basic/DiagnosticParseKinds.td =================================================================== --- clang/include/clang/Basic/DiagnosticParseKinds.td +++ clang/include/clang/Basic/DiagnosticParseKinds.td @@ -1427,6 +1427,15 @@ def warn_omp_more_one_interop_type : Warning<"interop type '%0' cannot be specified more than once">, InGroup; +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; +def warn_omp51_compat_attributes : Warning< + "specifying OpenMP directives with [[]] is incompatible with OpenMP " + "standards before OpenMP 5.1">, + InGroup, DefaultIgnore; // Pragma loop support. def err_pragma_loop_missing_argument : Error< Index: clang/include/clang/Basic/TokenKinds.def =================================================================== --- clang/include/clang/Basic/TokenKinds.def +++ clang/include/clang/Basic/TokenKinds.def @@ -863,6 +863,13 @@ 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) Index: clang/include/clang/Parse/Parser.h =================================================================== --- clang/include/clang/Parse/Parser.h +++ clang/include/clang/Parse/Parser.h @@ -2772,6 +2772,16 @@ 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); @@ -2801,8 +2811,18 @@ 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 @@ -2811,7 +2831,8 @@ SourceLocation AttrNameLoc, ParsedAttributes &Attrs, SourceLocation *EndLoc, IdentifierInfo *ScopeName, - SourceLocation ScopeLoc); + SourceLocation ScopeLoc, + CachedTokens &OpenMPTokens); IdentifierInfo *TryParseCXX11AttributeIdentifier(SourceLocation &Loc); Index: clang/lib/Basic/Attributes.cpp =================================================================== --- clang/lib/Basic/Attributes.cpp +++ clang/lib/Basic/Attributes.cpp @@ -20,6 +20,14 @@ 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; Index: clang/lib/Parse/ParseCXXInlineMethods.cpp =================================================================== --- clang/lib/Parse/ParseCXXInlineMethods.cpp +++ clang/lib/Parse/ParseCXXInlineMethods.cpp @@ -778,6 +778,7 @@ 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); Index: clang/lib/Parse/ParseDecl.cpp =================================================================== --- clang/lib/Parse/ParseDecl.cpp +++ clang/lib/Parse/ParseDecl.cpp @@ -4301,7 +4301,7 @@ 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); Index: clang/lib/Parse/ParseDeclCXX.cpp =================================================================== --- clang/lib/Parse/ParseDeclCXX.cpp +++ clang/lib/Parse/ParseDeclCXX.cpp @@ -2667,6 +2667,13 @@ 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()); @@ -3261,6 +3268,7 @@ return nullptr; } + case tok::annot_attr_openmp: case tok::annot_pragma_openmp: return ParseOpenMPDeclarativeDirectiveWithExtDecl( AS, AccessAttrs, /*Delayed=*/true, TagType, TagDecl); @@ -4135,6 +4143,70 @@ } } +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 ( @@ -4175,7 +4247,8 @@ 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(); @@ -4200,6 +4273,18 @@ 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"))) @@ -4259,11 +4344,12 @@ /// /// [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; } @@ -4345,11 +4431,11 @@ // 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, @@ -4374,8 +4460,8 @@ 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); } Index: clang/lib/Parse/ParseOpenMP.cpp =================================================================== --- clang/lib/Parse/ParseOpenMP.cpp +++ clang/lib/Parse/ParseOpenMP.cpp @@ -1857,7 +1857,8 @@ 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); @@ -1875,7 +1876,7 @@ 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; @@ -2098,7 +2099,7 @@ 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()) { @@ -2275,7 +2276,8 @@ /// 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 Clauses; Index: clang/lib/Parse/ParseStmt.cpp =================================================================== --- clang/lib/Parse/ParseStmt.cpp +++ clang/lib/Parse/ParseStmt.cpp @@ -401,7 +401,12 @@ 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: Index: clang/lib/Parse/Parser.cpp =================================================================== --- clang/lib/Parse/Parser.cpp +++ clang/lib/Parse/Parser.cpp @@ -309,6 +309,7 @@ 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) @@ -798,6 +799,7 @@ 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); Index: clang/test/OpenMP/allocate_codegen_attr.cpp =================================================================== --- /dev/null +++ clang/test/OpenMP/allocate_codegen_attr.cpp @@ -0,0 +1,112 @@ +// 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 +struct ST { + static T m; + [[omp::directive(allocate(m) allocator(omp_low_lat_mem_alloc))]]; +}; + +template T foo() { + T v; + [[omp::directive(allocate(v) allocator(omp_cgroup_mem_alloc))]]; + v = ST::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()); +} + +// 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::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 + Index: clang/test/OpenMP/assumes_messages_attr.c =================================================================== --- /dev/null +++ clang/test/OpenMP/assumes_messages_attr.c @@ -0,0 +1,57 @@ +// 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)]]; + Index: clang/test/OpenMP/critical_codegen_attr.cpp =================================================================== --- /dev/null +++ clang/test/OpenMP/critical_codegen_attr.cpp @@ -0,0 +1,130 @@ +// 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 + Index: clang/test/OpenMP/masked_messages_attr.cpp =================================================================== --- /dev/null +++ clang/test/OpenMP/masked_messages_attr.cpp @@ -0,0 +1,86 @@ +// 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; +} + Index: clang/test/OpenMP/openmp_attribute.cpp =================================================================== --- /dev/null +++ clang/test/OpenMP/openmp_attribute.cpp @@ -0,0 +1,41 @@ +// 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 + Index: clang/test/OpenMP/openmp_attribute_compat.cpp =================================================================== --- /dev/null +++ clang/test/OpenMP/openmp_attribute_compat.cpp @@ -0,0 +1,19 @@ +// 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}} + Index: clang/test/OpenMP/openmp_attribute_parsing.cpp =================================================================== --- /dev/null +++ clang/test/OpenMP/openmp_attribute_parsing.cpp @@ -0,0 +1,77 @@ +// 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}} +} Index: clang/test/OpenMP/target_map_names_attr.cpp =================================================================== --- /dev/null +++ clang/test/OpenMP/target_map_names_attr.cpp @@ -0,0 +1,215 @@ +// 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 + Index: clang/test/OpenMP/taskloop_reduction_messages_attr.cpp =================================================================== --- /dev/null +++ clang/test/OpenMP/taskloop_reduction_messages_attr.cpp @@ -0,0 +1,311 @@ +// 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 // 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' requested here}} expected-note {{in instantiation of function template specialization 'tmain' requested here}} +} + Index: clang/test/OpenMP/teams_distribute_parallel_for_simd_num_teams_messages_attr.cpp =================================================================== --- /dev/null +++ clang/test/OpenMP/teams_distribute_parallel_for_simd_num_teams_messages_attr.cpp @@ -0,0 +1,73 @@ +// 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 // 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(argc); // expected-note {{in instantiation of function template specialization 'tmain' requested here}} +} Index: clang/test/OpenMP/unroll_codegen_unroll_for_attr.cpp =================================================================== --- /dev/null +++ clang/test/OpenMP/unroll_codegen_unroll_for_attr.cpp @@ -0,0 +1,237 @@ +// 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]]}