diff --git a/clang/include/clang/AST/ASTNodeTraverser.h b/clang/include/clang/AST/ASTNodeTraverser.h --- a/clang/include/clang/AST/ASTNodeTraverser.h +++ b/clang/include/clang/AST/ASTNodeTraverser.h @@ -214,6 +214,10 @@ } void Visit(const OMPClause *C) { + if (OMPFailClause::classof(C)) { + Visit(static_cast(C)); + return; + } getNodeDelegate().AddChild([=] { getNodeDelegate().Visit(C); for (const auto *S : C->children()) @@ -221,6 +225,13 @@ }); } + void Visit(const OMPFailClause *C) { + getNodeDelegate().AddChild([=] { + getNodeDelegate().Visit(C); + const OMPClause *MOC = C->const_getMemoryOrderClause(); + Visit(MOC); + }); + } void Visit(const GenericSelectionExpr::ConstAssociation &A) { getNodeDelegate().AddChild([=] { getNodeDelegate().Visit(A); diff --git a/clang/include/clang/AST/OpenMPClause.h b/clang/include/clang/AST/OpenMPClause.h --- a/clang/include/clang/AST/OpenMPClause.h +++ b/clang/include/clang/AST/OpenMPClause.h @@ -2266,6 +2266,133 @@ } }; +/// This represents 'fail' clause in the '#pragma omp atomic' +/// directive. +/// +/// \code +/// #pragma omp atomic compare fail +/// \endcode +/// In this example directive '#pragma omp atomic compare' has 'fail' clause. +class OMPFailClause final + : public OMPClause, + private llvm::TrailingObjects { + OMPClause *MemoryOrderClause; + + friend class OMPClauseReader; + friend TrailingObjects; + + /// Define the sizes of each trailing object array except the last one. This + /// is required for TrailingObjects to work properly. + size_t numTrailingObjects(OverloadToken) const { + // 2 locations: for '(' and argument location. + return 2; + } + + /// Sets the location of '(' in fail clause. + void setLParenLoc(SourceLocation Loc) { + *getTrailingObjects() = Loc; + } + + /// Sets the location of memoryOrder clause argument in fail clause. + void setArgumentLoc(SourceLocation Loc) { + *std::next(getTrailingObjects(), 1) = Loc; + } + + /// Sets the mem_order clause for 'atomic compare fail' directive. + void setMemOrderClauseKind(OpenMPClauseKind MemOrder) { + OpenMPClauseKind *MOCK = getTrailingObjects(); + *MOCK = MemOrder; + } + + /// Sets the mem_order clause for 'atomic compare fail' directive. + void setMemOrderClause(OMPClause *MemoryOrderClauseParam) { + MemoryOrderClause = MemoryOrderClauseParam; + } +public: + /// Build 'fail' clause. + /// + /// \param StartLoc Starting location of the clause. + /// \param EndLoc Ending location of the clause. + OMPFailClause(SourceLocation StartLoc, SourceLocation EndLoc) + : OMPClause(llvm::omp::OMPC_fail, StartLoc, EndLoc) {} + + /// Build an empty clause. + OMPFailClause() + : OMPClause(llvm::omp::OMPC_fail, SourceLocation(), SourceLocation()) {} + + static OMPFailClause *CreateEmpty(const ASTContext &C); + static OMPFailClause *Create(const ASTContext &C, SourceLocation StartLoc, + SourceLocation EndLoc); + + child_range children() { + return child_range(child_iterator(), child_iterator()); + } + + + const_child_range children() const { + return const_child_range(const_child_iterator(), const_child_iterator()); + } + + child_range used_children() { + return child_range(child_iterator(), child_iterator()); + } + const_child_range used_children() const { + return const_child_range(const_child_iterator(), const_child_iterator()); + } + + static bool classof(const OMPClause *T) { + return T->getClauseKind() == llvm::omp::OMPC_fail; + } + + void initFailClause(SourceLocation LParenLoc, OMPClause *MemOClause, + SourceLocation MemOrderLoc) { + + setLParenLoc(LParenLoc); + MemoryOrderClause = MemOClause; + setArgumentLoc(MemOrderLoc); + + OpenMPClauseKind ClauseKind = MemoryOrderClause->getClauseKind(); + OpenMPClauseKind MemClauseKind = llvm::omp::OMPC_unknown; + switch(ClauseKind) { + case llvm::omp::OMPC_acq_rel: + case llvm::omp::OMPC_acquire: + MemClauseKind = llvm::omp::OMPC_acquire; + break; + case llvm::omp::OMPC_relaxed: + case llvm::omp::OMPC_release: + MemClauseKind = llvm::omp::OMPC_relaxed; + break; + case llvm::omp::OMPC_seq_cst: + MemClauseKind = llvm::omp::OMPC_seq_cst; + break; + default : break; + } + setMemOrderClauseKind(MemClauseKind); + } + + /// Gets the location of '(' in fail clause. + SourceLocation getLParenLoc() const { + return *getTrailingObjects(); + } + + OMPClause *getMemoryOrderClause() { return MemoryOrderClause; } + + const OMPClause *const_getMemoryOrderClause() const { + return static_cast(MemoryOrderClause); + } + + /// Gets the location of memoryOrder clause argument in fail clause. + SourceLocation getArgumentLoc() const { + return *std::next(getTrailingObjects(), 1); + } + + /// Gets the dependence kind in clause for 'depobj' directive. + OpenMPClauseKind getMemOrderClauseKind() const { + return *getTrailingObjects(); + } +}; + /// This represents 'seq_cst' clause in the '#pragma omp atomic' /// directive. /// diff --git a/clang/include/clang/AST/RecursiveASTVisitor.h b/clang/include/clang/AST/RecursiveASTVisitor.h --- a/clang/include/clang/AST/RecursiveASTVisitor.h +++ b/clang/include/clang/AST/RecursiveASTVisitor.h @@ -3346,6 +3346,11 @@ return true; } +template +bool RecursiveASTVisitor::VisitOMPFailClause(OMPFailClause *) { + return true; +} + template bool RecursiveASTVisitor::VisitOMPSeqCstClause(OMPSeqCstClause *) { return true; diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -10633,6 +10633,10 @@ "expect binary operator in conditional expression|expect '<', '>' or '==' as order operator|expect comparison in a form of 'x == e', 'e == x', 'x ordop expr', or 'expr ordop x'|" "expect lvalue for result value|expect scalar value|expect integer value|unexpected 'else' statement|expect '==' operator|expect an assignment statement 'v = x'|" "expect a 'if' statement|expect no more than two statements|expect a compound statement|expect 'else' statement|expect a form 'r = x == e; if (r) ...'}0">; +def err_omp_atomic_fail_wrong_or_no_clauses : Error<"expected a memory order clause">; +def err_omp_atomic_fail_extra_mem_order_clauses : Error<"directive '#pragma omp atomic compare fail' cannot contain more than one memory order clause">; +def err_omp_atomic_fail_extra_clauses : Error<"directive '#pragma omp atomic compare' cannot contain more than one fail clause">; +def err_omp_atomic_fail_no_compare : Error<"expected 'compare' clause with the 'fail' modifier">; def err_omp_atomic_several_clauses : Error< "directive '#pragma omp atomic' cannot contain more than one 'read', 'write', 'update', 'capture', or 'compare' clause">; def err_omp_several_mem_order_clauses : Error< diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h --- a/clang/include/clang/Parse/Parser.h +++ b/clang/include/clang/Parse/Parser.h @@ -433,6 +433,8 @@ /// a statement expression and builds a suitable expression statement. StmtResult handleExprStmt(ExprResult E, ParsedStmtContext StmtCtx); + OMPClause *ParseOpenMPFailClause(OMPClause *Clause); + public: Parser(Preprocessor &PP, Sema &Actions, bool SkipFunctionBodies); ~Parser() override; diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -11357,6 +11357,9 @@ /// Called on well-formed 'compare' clause. OMPClause *ActOnOpenMPCompareClause(SourceLocation StartLoc, SourceLocation EndLoc); + /// Called on well-formed 'fail' clause. + OMPClause *ActOnOpenMPFailClause(SourceLocation StartLoc, + SourceLocation EndLoc); /// Called on well-formed 'seq_cst' clause. OMPClause *ActOnOpenMPSeqCstClause(SourceLocation StartLoc, SourceLocation EndLoc); diff --git a/clang/lib/AST/OpenMPClause.cpp b/clang/lib/AST/OpenMPClause.cpp --- a/clang/lib/AST/OpenMPClause.cpp +++ b/clang/lib/AST/OpenMPClause.cpp @@ -127,6 +127,7 @@ case OMPC_update: case OMPC_capture: case OMPC_compare: + case OMPC_fail: case OMPC_seq_cst: case OMPC_acq_rel: case OMPC_acquire: @@ -220,6 +221,7 @@ case OMPC_update: case OMPC_capture: case OMPC_compare: + case OMPC_fail: case OMPC_seq_cst: case OMPC_acq_rel: case OMPC_acquire: @@ -412,6 +414,25 @@ return Clause; } +OMPFailClause *OMPFailClause::Create(const ASTContext &C, + SourceLocation StartLoc, + SourceLocation EndLoc) { + void *Mem = + C.Allocate(totalSizeToAlloc(2, 1), + alignof(OMPFailClause)); + auto *Clause = + new (Mem) OMPFailClause(StartLoc, EndLoc); + return Clause; +} + +OMPFailClause *OMPFailClause::CreateEmpty(const ASTContext &C) { + void *Mem = + C.Allocate(totalSizeToAlloc(2, 1), + alignof(OMPFailClause)); + auto *Clause = new (Mem) OMPFailClause(); + return Clause; +} + void OMPPrivateClause::setPrivateCopies(ArrayRef VL) { assert(VL.size() == varlist_size() && "Number of private copies is not the same as the preallocated buffer"); @@ -1847,6 +1868,16 @@ OS << "compare"; } +void OMPClausePrinter::VisitOMPFailClause(OMPFailClause *Node) { + OS << "fail"; + if(Node) { + OS << "("; + OS << getOpenMPSimpleClauseTypeName(Node->getClauseKind(), + static_cast(Node->getMemOrderClauseKind())); + OS << ")"; + } +} + void OMPClausePrinter::VisitOMPSeqCstClause(OMPSeqCstClause *) { OS << "seq_cst"; } diff --git a/clang/lib/AST/StmtProfile.cpp b/clang/lib/AST/StmtProfile.cpp --- a/clang/lib/AST/StmtProfile.cpp +++ b/clang/lib/AST/StmtProfile.cpp @@ -557,6 +557,8 @@ void OMPClauseProfiler::VisitOMPCompareClause(const OMPCompareClause *) {} +void OMPClauseProfiler::VisitOMPFailClause(const OMPFailClause *) {} + void OMPClauseProfiler::VisitOMPSeqCstClause(const OMPSeqCstClause *) {} void OMPClauseProfiler::VisitOMPAcqRelClause(const OMPAcqRelClause *) {} diff --git a/clang/lib/Basic/OpenMPKinds.cpp b/clang/lib/Basic/OpenMPKinds.cpp --- a/clang/lib/Basic/OpenMPKinds.cpp +++ b/clang/lib/Basic/OpenMPKinds.cpp @@ -366,6 +366,20 @@ #include "clang/Basic/OpenMPKinds.def" } llvm_unreachable("Invalid OpenMP 'depend' clause type"); + case OMPC_fail: { + OpenMPClauseKind CK = static_cast(Type); + switch (CK) { + case OMPC_acquire: + return "acquire"; + case OMPC_relaxed: + return "relaxed"; + case OMPC_seq_cst: + return "seq_cst"; + default: + return "unknown"; + } + llvm_unreachable("Invalid OpenMP 'fail' clause modifier"); + } case OMPC_device: switch (Type) { case OMPC_DEVICE_unknown: diff --git a/clang/lib/Parse/ParseOpenMP.cpp b/clang/lib/Parse/ParseOpenMP.cpp --- a/clang/lib/Parse/ParseOpenMP.cpp +++ b/clang/lib/Parse/ParseOpenMP.cpp @@ -3234,6 +3234,7 @@ case OMPC_write: case OMPC_capture: case OMPC_compare: + case OMPC_fail: case OMPC_seq_cst: case OMPC_acq_rel: case OMPC_acquire: @@ -3620,6 +3621,45 @@ Val.getValue().Loc, Val.getValue().RLoc); } +OMPClause *Parser::ParseOpenMPFailClause(OMPClause *Clause) { + + OMPFailClause *FailClause = static_cast(Clause); + SourceLocation LParenLoc; + if (Tok.is(tok::l_paren)) { + LParenLoc = Tok.getLocation(); + ConsumeAnyToken(); + } else { + Diag(diag::err_expected_lparen_after) << getOpenMPClauseName(OMPC_fail); + return Clause; + } + + + OpenMPClauseKind CKind = Tok.isAnnotation() + ? OMPC_unknown + : getOpenMPClauseKind(PP.getSpelling(Tok)); + if (CKind == OMPC_unknown) { + Diag(diag::err_omp_expected_clause) << ("atomic compare fail"); + return Clause; + } + OMPClause *MemoryOrderClause = ParseOpenMPClause(CKind, false); + SourceLocation MemOrderLoc; + // Store Memory Order SubClause for Sema. + if (MemoryOrderClause) { + MemOrderLoc = Tok.getLocation(); + } + + if (Tok.is(tok::r_paren)) { + FailClause->initFailClause(LParenLoc,MemoryOrderClause,MemOrderLoc); + ConsumeAnyToken(); + } else { + const IdentifierInfo *Arg = Tok.getIdentifierInfo(); + Diag(Tok, diag::err_expected_rparen_after) + << (Arg ? Arg->getName() : "atomic compare fail"); + } + + return Clause; +} + /// Parsing of OpenMP clauses like 'ordered'. /// /// ordered-clause: @@ -3652,7 +3692,11 @@ if (ParseOnly) return nullptr; - return Actions.ActOnOpenMPClause(Kind, Loc, Tok.getLocation()); + OMPClause *Clause = Actions.ActOnOpenMPClause(Kind, Loc, Tok.getLocation()); + if (Kind == llvm::omp::Clause::OMPC_fail) { + Clause = ParseOpenMPFailClause(Clause); + } + return Clause; } /// Parsing of OpenMP clauses with single expressions and some additional diff --git a/clang/lib/Sema/SemaOpenMP.cpp b/clang/lib/Sema/SemaOpenMP.cpp --- a/clang/lib/Sema/SemaOpenMP.cpp +++ b/clang/lib/Sema/SemaOpenMP.cpp @@ -12026,6 +12026,68 @@ return checkType(ErrorInfo); } + +class OpenMPAtomicFailChecker { + +protected: + Sema &SemaRef; + ASTContext &Context; + +public: + // Error descriptor type which will be returned to Sema + unsigned int ErrorNo; + + OpenMPAtomicFailChecker(Sema &S) : SemaRef(S), Context(S.getASTContext()) {} + + /// Check if all results conform with spec in terms of lvalue/rvalue + /// and scalar type. + bool checkSubClause(ArrayRef Clauses, SourceLocation *ErrorLoc); + /// Return the error descriptor that will guide the error message emission. + unsigned getErrorDesc() const { return ErrorNo; } +}; + +bool OpenMPAtomicFailChecker::checkSubClause(ArrayRef Clauses, + SourceLocation *ErrorLoc) { + int NoOfFails = 0; + ErrorNo = 0; + SourceLocation ClauseLoc; + for (const OMPClause *C : Clauses) { + if (C->getClauseKind() == OMPC_fail) { + NoOfFails++; + const OMPFailClause *FC = static_cast(C); + const OMPClause *MemOrderC = FC->const_getMemoryOrderClause(); + /* Clauses contains OMPC_fail and the subclause */ + if (MemOrderC) { + OpenMPClauseKind ClauseKind = MemOrderC->getClauseKind(); + if ((ClauseKind == OMPC_acq_rel) || (ClauseKind == OMPC_acquire) || + (ClauseKind == OMPC_relaxed) || (ClauseKind == OMPC_release) || + (ClauseKind == OMPC_seq_cst)) { + switch(ClauseKind) { + case OMPC_acq_rel : + ClauseKind = OMPC_acquire; + break; + case OMPC_release : + ClauseKind = OMPC_relaxed; + break; + default : break; + } + continue; + } else { + ErrorNo = diag::err_omp_atomic_fail_wrong_or_no_clauses; + *ErrorLoc = MemOrderC->getBeginLoc(); + continue; + } + } + } + } + if (NoOfFails > 1) { + ErrorNo = diag::err_omp_atomic_fail_extra_clauses; + *ErrorLoc = ClauseLoc; + } + + return !ErrorNo; +} + } // namespace StmtResult Sema::ActOnOpenMPAtomicDirective(ArrayRef Clauses, @@ -12046,6 +12108,8 @@ SourceLocation AtomicKindLoc; OpenMPClauseKind MemOrderKind = OMPC_unknown; SourceLocation MemOrderLoc; + llvm::omp::Clause SubClause = OMPC_unknown; + SourceLocation SubClauseLoc; bool MutexClauseEncountered = false; llvm::SmallSet EncounteredAtomicKinds; for (const OMPClause *C : Clauses) { @@ -12074,6 +12138,16 @@ } break; } + case OMPC_fail: { + if (AtomicKind != OMPC_compare) { + Diag(C->getBeginLoc(), diag::err_omp_atomic_fail_no_compare) + << SourceRange(C->getBeginLoc(), C->getEndLoc()); + return StmtError(); + } + SubClause = OMPC_fail; + SubClauseLoc = C->getBeginLoc(); + break; + } case OMPC_seq_cst: case OMPC_acq_rel: case OMPC_acquire: @@ -12552,6 +12626,17 @@ CE = Checker.getCond(); // We reuse IsXLHSInRHSPart to tell if it is in the form 'x ordop expr'. IsXLHSInRHSPart = Checker.isXBinopExpr(); + if (SubClause == OMPC_fail) { + OpenMPAtomicFailChecker Checker(*this); + SourceLocation ErrorLoc, NoteLoc; + NoteLoc = ErrorLoc = Body->getBeginLoc(); + ErrorLoc = SubClauseLoc; + if (!Checker.checkSubClause(Clauses,&ErrorLoc)) { + unsigned ErrorNo = Checker.getErrorDesc(); + Diag(ErrorLoc, ErrorNo); + return StmtError(); + } + } } } @@ -16491,6 +16576,9 @@ case OMPC_compare: Res = ActOnOpenMPCompareClause(StartLoc, EndLoc); break; + case OMPC_fail: + Res = ActOnOpenMPFailClause(StartLoc, EndLoc); + break; case OMPC_seq_cst: Res = ActOnOpenMPSeqCstClause(StartLoc, EndLoc); break; @@ -16644,6 +16732,11 @@ return new (Context) OMPCompareClause(StartLoc, EndLoc); } +OMPClause *Sema::ActOnOpenMPFailClause(SourceLocation StartLoc, + SourceLocation EndLoc) { + return OMPFailClause::Create(Context, StartLoc, EndLoc); +} + OMPClause *Sema::ActOnOpenMPSeqCstClause(SourceLocation StartLoc, SourceLocation EndLoc) { return new (Context) OMPSeqCstClause(StartLoc, EndLoc); diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -9559,6 +9559,13 @@ return C; } +template +OMPClause * +TreeTransform::TransformOMPFailClause(OMPFailClause *C) { + // No need to rebuild this clause, no template-dependent parameters. + return C; +} + template OMPClause * TreeTransform::TransformOMPSeqCstClause(OMPSeqCstClause *C) { diff --git a/clang/lib/Serialization/ASTReader.cpp b/clang/lib/Serialization/ASTReader.cpp --- a/clang/lib/Serialization/ASTReader.cpp +++ b/clang/lib/Serialization/ASTReader.cpp @@ -11744,6 +11744,9 @@ case llvm::omp::OMPC_compare: C = new (Context) OMPCompareClause(); break; + case llvm::omp::OMPC_fail: + C = OMPFailClause::CreateEmpty(Context); + break; case llvm::omp::OMPC_seq_cst: C = new (Context) OMPSeqCstClause(); break; @@ -12113,6 +12116,31 @@ void OMPClauseReader::VisitOMPCompareClause(OMPCompareClause *) {} +void OMPClauseReader::VisitOMPFailClause(OMPFailClause *C) { + C->setLParenLoc(Record.readSourceLocation()); + SourceLocation SourceLoc = Record.readSourceLocation(); + C->setArgumentLoc(SourceLoc); + OpenMPClauseKind CKind = Record.readEnum(); + C->setMemOrderClauseKind(CKind); + + SourceLocation EndLoc; + OMPClause *MemoryOrderClause = NULL; + switch(CKind) { + case llvm::omp::OMPC_acquire: + MemoryOrderClause = new (Context) OMPAcquireClause(SourceLoc, EndLoc); + break; + case llvm::omp::OMPC_relaxed: + MemoryOrderClause = new (Context) OMPRelaxedClause(SourceLoc, EndLoc); + break; + case llvm::omp::OMPC_seq_cst: + MemoryOrderClause = new (Context) OMPSeqCstClause(SourceLoc, EndLoc); + break; + default: + break; + } + C->setMemOrderClause(MemoryOrderClause); +} + void OMPClauseReader::VisitOMPSeqCstClause(OMPSeqCstClause *) {} void OMPClauseReader::VisitOMPAcqRelClause(OMPAcqRelClause *) {} diff --git a/clang/lib/Serialization/ASTWriter.cpp b/clang/lib/Serialization/ASTWriter.cpp --- a/clang/lib/Serialization/ASTWriter.cpp +++ b/clang/lib/Serialization/ASTWriter.cpp @@ -6327,6 +6327,14 @@ void OMPClauseWriter::VisitOMPCompareClause(OMPCompareClause *) {} +void OMPClauseWriter::VisitOMPFailClause(OMPFailClause *C) { + // Record.AddSourceLocation(C->getLParenLoc()); + // Copied from VisitOMPUpdateClause + Record.AddSourceLocation(C->getLParenLoc()); + Record.AddSourceLocation(C->getArgumentLoc()); + Record.writeEnum(C->getMemOrderClauseKind()); +} + void OMPClauseWriter::VisitOMPSeqCstClause(OMPSeqCstClause *) {} void OMPClauseWriter::VisitOMPAcqRelClause(OMPAcqRelClause *) {} diff --git a/clang/test/OpenMP/atomic_ast_print.cpp b/clang/test/OpenMP/atomic_ast_print.cpp --- a/clang/test/OpenMP/atomic_ast_print.cpp +++ b/clang/test/OpenMP/atomic_ast_print.cpp @@ -226,6 +226,16 @@ { v = a; if (a < b) { a = b; } } #pragma omp atomic compare capture hint(6) { v = a == b; if (v) a = c; } +#pragma omp atomic compare fail(acq_rel) + { if (a < c) { a = c; } } +#pragma omp atomic compare fail(acquire) + { if (a < c) { a = c; } } +#pragma omp atomic compare fail(release) + { if (a < c) { a = c; } } +#pragma omp atomic compare fail(relaxed) + { if (a < c) { a = c; } } +#pragma omp atomic compare fail(seq_cst) + { if (a < c) { a = c; } } #endif return T(); } @@ -1099,6 +1109,16 @@ { v = a; if (a < b) { a = b; } } #pragma omp atomic compare capture hint(6) { v = a == b; if (v) a = c; } +#pragma omp atomic compare fail(acq_rel) + if(a < b) { a = b; } +#pragma omp atomic compare fail(acquire) + if(a < b) { a = b; } +#pragma omp atomic compare fail(release) + if(a < b) { a = b; } +#pragma omp atomic compare fail(relaxed) + if(a < b) { a = b; } +#pragma omp atomic compare fail(seq_cst) + if(a < b) { a = b; } #endif // CHECK-NEXT: #pragma omp atomic // CHECK-NEXT: a++; @@ -1429,6 +1449,26 @@ // CHECK-51-NEXT: if (v) // CHECK-51-NEXT: a = c; // CHECK-51-NEXT: } + // CHECK-51-NEXT: #pragma omp atomic compare fail(acquire) + // CHECK-51-NEXT: if (a < b) { + // CHECK-51-NEXT: a = b; + // CHECK-51-NEXT: } + // CHECK-51-NEXT: #pragma omp atomic compare fail(acquire) + // CHECK-51-NEXT: if (a < b) { + // CHECK-51-NEXT: a = b; + // CHECK-51-NEXT: } + // CHECK-51-NEXT: #pragma omp atomic compare fail(relaxed) + // CHECK-51-NEXT: if (a < b) { + // CHECK-51-NEXT: a = b; + // CHECK-51-NEXT: } + // CHECK-51-NEXT: #pragma omp atomic compare fail(relaxed) + // CHECK-51-NEXT: if (a < b) { + // CHECK-51-NEXT: a = b; + // CHECK-51-NEXT: } + // CHECK-51-NEXT: #pragma omp atomic compare fail(seq_cst) + // CHECK-51-NEXT: if (a < b) { + // CHECK-51-NEXT: a = b; + // CHECK-51-NEXT: } // expect-note@+1 {{in instantiation of function template specialization 'foo' requested here}} return foo(a); } diff --git a/clang/test/OpenMP/atomic_messages.cpp b/clang/test/OpenMP/atomic_messages.cpp --- a/clang/test/OpenMP/atomic_messages.cpp +++ b/clang/test/OpenMP/atomic_messages.cpp @@ -958,6 +958,24 @@ // expected-error@+1 {{directive '#pragma omp atomic' cannot contain more than one 'capture' clause}} #pragma omp atomic compare compare capture capture { v = a; if (a > b) a = b; } +// expected-error@+1 {{expected 'compare' clause with the 'fail' modifier}} +#pragma omp atomic fail(seq_cst) + if(v == a) { v = a; } +// expected-error@+1 {{expected '(' after 'fail'}} +#pragma omp atomic compare fail + if(v < a) { v = a; } +// expected-error@+1 {{expected a memory order clause}} +#pragma omp atomic compare fail(capture) + if(v < a) { v = a; } + // expected-error@+2 {{expected ')' after 'atomic compare fail'}} + // expected-warning@+1 {{extra tokens at the end of '#pragma omp atomic' are ignored}} +#pragma omp atomic compare fail(seq_cst | acquire) + if(v < a) { v = a; } +// expected-error@+1 {{directive '#pragma omp atomic' cannot contain more than one 'fail' clause}} +#pragma omp atomic compare fail(relaxed) fail(seq_cst) + if(v < a) { v = a; } + + #endif // expected-note@+1 {{in instantiation of function template specialization 'mixed' requested here}} return mixed(); diff --git a/clang/tools/libclang/CIndex.cpp b/clang/tools/libclang/CIndex.cpp --- a/clang/tools/libclang/CIndex.cpp +++ b/clang/tools/libclang/CIndex.cpp @@ -2368,6 +2368,8 @@ void OMPClauseEnqueue::VisitOMPCompareClause(const OMPCompareClause *) {} +void OMPClauseEnqueue::VisitOMPFailClause(const OMPFailClause *) {} + void OMPClauseEnqueue::VisitOMPSeqCstClause(const OMPSeqCstClause *) {} void OMPClauseEnqueue::VisitOMPAcqRelClause(const OMPAcqRelClause *) {} diff --git a/flang/lib/Semantics/check-omp-structure.cpp b/flang/lib/Semantics/check-omp-structure.cpp --- a/flang/lib/Semantics/check-omp-structure.cpp +++ b/flang/lib/Semantics/check-omp-structure.cpp @@ -1807,6 +1807,7 @@ CHECK_SIMPLE_CLAUSE(Align, OMPC_align) CHECK_SIMPLE_CLAUSE(Compare, OMPC_compare) CHECK_SIMPLE_CLAUSE(CancellationConstructType, OMPC_cancellation_construct_type) +CHECK_SIMPLE_CLAUSE(Fail, OMPC_fail) CHECK_REQ_SCALAR_INT_CLAUSE(Grainsize, OMPC_grainsize) CHECK_REQ_SCALAR_INT_CLAUSE(NumTasks, OMPC_num_tasks) diff --git a/llvm/include/llvm/Frontend/OpenMP/OMP.td b/llvm/include/llvm/Frontend/OpenMP/OMP.td --- a/llvm/include/llvm/Frontend/OpenMP/OMP.td +++ b/llvm/include/llvm/Frontend/OpenMP/OMP.td @@ -199,6 +199,7 @@ def OMPC_Update : Clause<"update"> { let clangClass = "OMPUpdateClause"; } def OMPC_Capture : Clause<"capture"> { let clangClass = "OMPCaptureClause"; } def OMPC_Compare : Clause<"compare"> { let clangClass = "OMPCompareClause"; } +def OMPC_Fail : Clause<"fail"> { let clangClass = "OMPFailClause"; } def OMPC_SeqCst : Clause<"seq_cst"> { let clangClass = "OMPSeqCstClause"; } def OMPC_AcqRel : Clause<"acq_rel"> { let clangClass = "OMPAcqRelClause"; } def OMPC_Acquire : Clause<"acquire"> { let clangClass = "OMPAcquireClause"; } @@ -569,7 +570,8 @@ VersionedClause, VersionedClause, VersionedClause, - VersionedClause + VersionedClause, + VersionedClause ]; } def OMP_Target : Directive<"target"> {