From de59f564400de1b0fe30ae07f3c800562a025e27 Mon Sep 17 00:00:00 2001 From: Aaron Ballman Date: Mon, 12 Jul 2021 06:51:19 -0400 Subject: [PATCH] [OpenMP] Support OpenMP 5.1 attributes OpenMP 5.1 added support for writing OpenMP directives using [[]] syntax in addition to using #pragma and this introduces support for the new syntax. In OpenMP, the attributes take one of two forms: [[omp::directive(...)]] or [[omp::sequence(...)]]. A directive attribute contains an OpenMP directive clause that is identical to the analogous #pragma syntax. A sequence attribute can contain either sequence or directive arguments and is used to ensure that the attributes are processed sequentially for situations where the order of the attributes matter (remember: https://eel.is/c++draft/dcl.attr.grammar#4.sentence-4). The approach taken here is somewhat novel and deserves mention. We could refactor much of the OpenMP parsing logic to work for either pragma annotation tokens or for attribute clauses. It would be a fair amount of effort to share the logic for both, but it's certainly doable. However, the semantic attribute system is not designed to handle the arbitrarily complex arguments that OpenMP directives contain. Adding support to thread the novel parsed information until we can produce a semantic attribute would be considerably more effort. What's more, existing OpenMP constructs are not (often) represented as semantic attributes. So doing this through Attr.td would be a massive undertaking that would likely only benefit OpenMP and comes with additional risks. Rather than walk down that path, I am taking advantage of the fact that the syntax of the directives within the directive clause is identical to that of the #pragma form. Once the parser recognizes that we're processing an OpenMP attribute, it caches all of the directive argument tokens and then replays them as though the user wrote a pragma. This reuses the same OpenMP parsing and semantic logic directly, but does come with a risk if the OpenMP committee decides to purposefully diverge their pragma and attribute syntaxes. So, despite this being a novel approach that does token replay, I think it's actually a better approach than trying to do this through the declarative syntax in Attr.td. --- clang/docs/OpenMPSupport.rst | 2 +- clang/include/clang/Basic/DiagnosticGroups.td | 5 +- clang/include/clang/Basic/DiagnosticParseKinds.td | 9 + clang/include/clang/Basic/TokenKinds.def | 7 + clang/include/clang/Parse/Parser.h | 27 +- clang/lib/Basic/Attributes.cpp | 8 + clang/lib/Parse/ParseCXXInlineMethods.cpp | 1 + clang/lib/Parse/ParseDecl.cpp | 2 +- clang/lib/Parse/ParseDeclCXX.cpp | 104 ++++++- clang/lib/Parse/ParseOpenMP.cpp | 10 +- clang/lib/Parse/ParseStmt.cpp | 5 + clang/lib/Parse/Parser.cpp | 2 + clang/test/OpenMP/allocate_codegen_attr.cpp | 112 ++++++++ clang/test/OpenMP/assumes_messages_attr.c | 57 ++++ clang/test/OpenMP/critical_codegen_attr.cpp | 130 +++++++++ clang/test/OpenMP/masked_messages_attr.cpp | 86 ++++++ clang/test/OpenMP/openmp_attribute.cpp | 41 +++ clang/test/OpenMP/openmp_attribute_compat.cpp | 19 ++ clang/test/OpenMP/openmp_attribute_parsing.cpp | 77 +++++ clang/test/OpenMP/target_map_names_attr.cpp | 215 ++++++++++++++ .../OpenMP/taskloop_reduction_messages_attr.cpp | 311 +++++++++++++++++++++ ...e_parallel_for_simd_num_teams_messages_attr.cpp | 73 +++++ .../test/OpenMP/unroll_codegen_unroll_for_attr.cpp | 237 ++++++++++++++++ 23 files changed, 1521 insertions(+), 19 deletions(-) create mode 100644 clang/test/OpenMP/allocate_codegen_attr.cpp create mode 100644 clang/test/OpenMP/assumes_messages_attr.c create mode 100644 clang/test/OpenMP/critical_codegen_attr.cpp create mode 100644 clang/test/OpenMP/masked_messages_attr.cpp create mode 100644 clang/test/OpenMP/openmp_attribute.cpp create mode 100644 clang/test/OpenMP/openmp_attribute_compat.cpp create mode 100644 clang/test/OpenMP/openmp_attribute_parsing.cpp create mode 100644 clang/test/OpenMP/target_map_names_attr.cpp create mode 100644 clang/test/OpenMP/taskloop_reduction_messages_attr.cpp create mode 100644 clang/test/OpenMP/teams_distribute_parallel_for_simd_num_teams_messages_attr.cpp create mode 100644 clang/test/OpenMP/unroll_codegen_unroll_for_attr.cpp diff --git a/clang/docs/OpenMPSupport.rst b/clang/docs/OpenMPSupport.rst index 3bd1a0c..88d3107 100644 --- a/clang/docs/OpenMPSupport.rst +++ b/clang/docs/OpenMPSupport.rst @@ -268,7 +268,7 @@ want to help with the implementation. +------------------------------+--------------------------------------------------------------+--------------------------+-----------------------------------------------------------------------+ | 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 | +------------------------------+--------------------------------------------------------------+--------------------------+-----------------------------------------------------------------------+ diff --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td index f35c105..18097cf 100644 --- a/clang/include/clang/Basic/DiagnosticGroups.td +++ b/clang/include/clang/Basic/DiagnosticGroups.td @@ -1186,8 +1186,11 @@ def OpenMPClauses : DiagGroup<"openmp-clauses">; 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. diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td index aecc585..7e4b084 100644 --- a/clang/include/clang/Basic/DiagnosticParseKinds.td +++ b/clang/include/clang/Basic/DiagnosticParseKinds.td @@ -1427,6 +1427,15 @@ def err_omp_expected_interop_type : Error< 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< diff --git a/clang/include/clang/Basic/TokenKinds.def b/clang/include/clang/Basic/TokenKinds.def index fd99096..48a664e 100644 --- a/clang/include/clang/Basic/TokenKinds.def +++ b/clang/include/clang/Basic/TokenKinds.def @@ -863,6 +863,13 @@ PRAGMA_ANNOTATION(pragma_ms_pragma) 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) diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h index 605292f..8eb3f90 100644 --- a/clang/include/clang/Parse/Parser.h +++ b/clang/include/clang/Parse/Parser.h @@ -2772,6 +2772,16 @@ private: 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 @@ private: 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 @@ private: SourceLocation AttrNameLoc, ParsedAttributes &Attrs, SourceLocation *EndLoc, IdentifierInfo *ScopeName, - SourceLocation ScopeLoc); + SourceLocation ScopeLoc, + CachedTokens &OpenMPTokens); IdentifierInfo *TryParseCXX11AttributeIdentifier(SourceLocation &Loc); diff --git a/clang/lib/Basic/Attributes.cpp b/clang/lib/Basic/Attributes.cpp index ff6dbf8..62eea9c 100644 --- a/clang/lib/Basic/Attributes.cpp +++ b/clang/lib/Basic/Attributes.cpp @@ -20,6 +20,14 @@ int clang::hasAttribute(AttrSyntax Syntax, const IdentifierInfo *Scope, 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; diff --git a/clang/lib/Parse/ParseCXXInlineMethods.cpp b/clang/lib/Parse/ParseCXXInlineMethods.cpp index b033590..116724a 100644 --- a/clang/lib/Parse/ParseCXXInlineMethods.cpp +++ b/clang/lib/Parse/ParseCXXInlineMethods.cpp @@ -778,6 +778,7 @@ void Parser::ParseLexedPragma(LateParsedPragma &LP) { 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); diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp index 76d031b..c1f20b2 100644 --- a/clang/lib/Parse/ParseDecl.cpp +++ b/clang/lib/Parse/ParseDecl.cpp @@ -4301,7 +4301,7 @@ void Parser::ParseStructUnionBody(SourceLocation RecordLoc, 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); diff --git a/clang/lib/Parse/ParseDeclCXX.cpp b/clang/lib/Parse/ParseDeclCXX.cpp index 37a5ae1..ca5c013 100644 --- a/clang/lib/Parse/ParseDeclCXX.cpp +++ b/clang/lib/Parse/ParseDeclCXX.cpp @@ -2667,6 +2667,13 @@ Parser::ParseCXXClassMemberDeclaration(AccessSpecifier AS, 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 @@ Parser::DeclGroupPtrTy Parser::ParseCXXClassMemberDeclarationWithPragmas( return nullptr; } + case tok::annot_attr_openmp: case tok::annot_pragma_openmp: return ParseOpenMPDeclarativeDirectiveWithExtDecl( AS, AccessAttrs, /*Delayed=*/true, TagType, TagDecl); @@ -4135,6 +4143,70 @@ IdentifierInfo *Parser::TryParseCXX11AttributeIdentifier(SourceLocation &Loc) { } } +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 @@ bool Parser::ParseCXX11AttributeArgs(IdentifierInfo *AttrName, 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 @@ bool Parser::ParseCXX11AttributeArgs(IdentifierInfo *AttrName, 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 @@ bool Parser::ParseCXX11AttributeArgs(IdentifierInfo *AttrName, /// /// [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 @@ void Parser::ParseCXX11AttributeSpecifier(ParsedAttributes &attrs, // 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 @@ void Parser::ParseCXX11AttributeSpecifier(ParsedAttributes &attrs, 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); } diff --git a/clang/lib/Parse/ParseOpenMP.cpp b/clang/lib/Parse/ParseOpenMP.cpp index d2152fd..18e43c3 100644 --- a/clang/lib/Parse/ParseOpenMP.cpp +++ b/clang/lib/Parse/ParseOpenMP.cpp @@ -1857,7 +1857,8 @@ void Parser::ParseOMPEndDeclareTargetDirective(OpenMPDirectiveKind BeginDKind, 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 @@ Parser::DeclGroupPtrTy Parser::ParseOpenMPDeclarativeDirectiveWithExtDecl( 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 @@ Parser::DeclGroupPtrTy Parser::ParseOpenMPDeclarativeDirectiveWithExtDecl( 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 @@ Parser::DeclGroupPtrTy Parser::ParseOpenMPDeclarativeDirectiveWithExtDecl( /// 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; diff --git a/clang/lib/Parse/ParseStmt.cpp b/clang/lib/Parse/ParseStmt.cpp index 7dd8dd1..ebfe048 100644 --- a/clang/lib/Parse/ParseStmt.cpp +++ b/clang/lib/Parse/ParseStmt.cpp @@ -401,7 +401,12 @@ Retry: 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: diff --git a/clang/lib/Parse/Parser.cpp b/clang/lib/Parse/Parser.cpp index 55b25d2..c81dd03 100644 --- a/clang/lib/Parse/Parser.cpp +++ b/clang/lib/Parse/Parser.cpp @@ -309,6 +309,7 @@ bool Parser::SkipUntil(ArrayRef Toks, SkipUntilFlags Flags) { 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 @@ Parser::ParseExternalDeclaration(ParsedAttributesWithRange &attrs, 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); diff --git a/clang/test/OpenMP/allocate_codegen_attr.cpp b/clang/test/OpenMP/allocate_codegen_attr.cpp new file mode 100644 index 0000000..0ef5c77 --- /dev/null +++ b/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 + diff --git a/clang/test/OpenMP/assumes_messages_attr.c b/clang/test/OpenMP/assumes_messages_attr.c new file mode 100644 index 0000000..bd06e85 --- /dev/null +++ b/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)]]; + diff --git a/clang/test/OpenMP/critical_codegen_attr.cpp b/clang/test/OpenMP/critical_codegen_attr.cpp new file mode 100644 index 0000000..e3890ff60 --- /dev/null +++ b/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 + diff --git a/clang/test/OpenMP/masked_messages_attr.cpp b/clang/test/OpenMP/masked_messages_attr.cpp new file mode 100644 index 0000000..3725293 --- /dev/null +++ b/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; +} + diff --git a/clang/test/OpenMP/openmp_attribute.cpp b/clang/test/OpenMP/openmp_attribute.cpp new file mode 100644 index 0000000..5b6f64e --- /dev/null +++ b/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 + diff --git a/clang/test/OpenMP/openmp_attribute_compat.cpp b/clang/test/OpenMP/openmp_attribute_compat.cpp new file mode 100644 index 0000000..5f51e30 --- /dev/null +++ b/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}} + diff --git a/clang/test/OpenMP/openmp_attribute_parsing.cpp b/clang/test/OpenMP/openmp_attribute_parsing.cpp new file mode 100644 index 0000000..4d2c84c --- /dev/null +++ b/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}} +} diff --git a/clang/test/OpenMP/target_map_names_attr.cpp b/clang/test/OpenMP/target_map_names_attr.cpp new file mode 100644 index 0000000..c45e7c8 --- /dev/null +++ b/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 + diff --git a/clang/test/OpenMP/taskloop_reduction_messages_attr.cpp b/clang/test/OpenMP/taskloop_reduction_messages_attr.cpp new file mode 100644 index 0000000..3dc2258 --- /dev/null +++ b/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}} +} + diff --git a/clang/test/OpenMP/teams_distribute_parallel_for_simd_num_teams_messages_attr.cpp b/clang/test/OpenMP/teams_distribute_parallel_for_simd_num_teams_messages_attr.cpp new file mode 100644 index 0000000..9c4bde7 --- /dev/null +++ b/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}} +} diff --git a/clang/test/OpenMP/unroll_codegen_unroll_for_attr.cpp b/clang/test/OpenMP/unroll_codegen_unroll_for_attr.cpp new file mode 100644 index 0000000..f431201 --- /dev/null +++ b/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]]} -- 2.7.4