From 4ccd96e07cc1fb910203c771ea5801208d99dde4 Mon Sep 17 00:00:00 2001 From: Krzysztof Parzyszek Date: Fri, 10 Oct 2025 10:55:48 -0500 Subject: [PATCH 01/11] [flang][OpenMP] Set REQUIRES flags on program unit symbol REQUIRES clauses apply to the compilation unit, which the OpenMP spec defines as the program unit in Fortran. Don't set REQUIRES flags on all containing scopes, only on the containng program unit, where flags coming from different directives are gathered. If we wanted to set the flags on subprograms, we would need to first accummulate all of them, then propagate them down to all subprograms. That is not done as it is not necessary (the containing program unit is always available). --- flang/include/flang/Semantics/openmp-utils.h | 1 + flang/lib/Semantics/openmp-utils.cpp | 23 +++++++- flang/lib/Semantics/resolve-directives.cpp | 60 ++++++++++---------- 3 files changed, 52 insertions(+), 32 deletions(-) diff --git a/flang/include/flang/Semantics/openmp-utils.h b/flang/include/flang/Semantics/openmp-utils.h index 2954a1c4769f7..0f851830edd46 100644 --- a/flang/include/flang/Semantics/openmp-utils.h +++ b/flang/include/flang/Semantics/openmp-utils.h @@ -38,6 +38,7 @@ template > U AsRvalue(T &t) { template T &&AsRvalue(T &&t) { return std::move(t); } const Scope &GetScopingUnit(const Scope &scope); +const Scope &GetProgramUnit(const Scope &scope); // There is no consistent way to get the source of an ActionStmt, but there // is "source" in Statement. This structure keeps the ActionStmt with the diff --git a/flang/lib/Semantics/openmp-utils.cpp b/flang/lib/Semantics/openmp-utils.cpp index a8ec4d6c24beb..292e73b4899c0 100644 --- a/flang/lib/Semantics/openmp-utils.cpp +++ b/flang/lib/Semantics/openmp-utils.cpp @@ -13,6 +13,7 @@ #include "flang/Semantics/openmp-utils.h" #include "flang/Common/Fortran-consts.h" +#include "flang/Common/idioms.h" #include "flang/Common/indirection.h" #include "flang/Common/reference.h" #include "flang/Common/visit.h" @@ -59,6 +60,26 @@ const Scope &GetScopingUnit(const Scope &scope) { return *iter; } +const Scope &GetProgramUnit(const Scope &scope) { + const Scope *unit{nullptr}; + for (const Scope *iter{&scope}; !iter->IsTopLevel(); iter = &iter->parent()) { + switch (iter->kind()) { + case Scope::Kind::BlockData: + case Scope::Kind::MainProgram: + case Scope::Kind::Module: + return *iter; + case Scope::Kind::Subprogram: + // Ignore subprograms that are nested. + unit = iter; + break; + default: + break; + } + } + assert(unit && "Scope not in a program unit"); + return *unit; +} + SourcedActionStmt GetActionStmt(const parser::ExecutionPartConstruct *x) { if (x == nullptr) { return SourcedActionStmt{}; @@ -202,7 +223,7 @@ std::optional GetEvaluateExpr(const parser::Expr &parserExpr) { // ForwardOwningPointer typedExpr // `- GenericExprWrapper ^.get() // `- std::optional ^->v - return typedExpr.get()->v; + return DEREF(typedExpr.get()).v; } std::optional GetDynamicType( diff --git a/flang/lib/Semantics/resolve-directives.cpp b/flang/lib/Semantics/resolve-directives.cpp index 18fc63814d973..de680b41d1524 100644 --- a/flang/lib/Semantics/resolve-directives.cpp +++ b/flang/lib/Semantics/resolve-directives.cpp @@ -3549,40 +3549,38 @@ void OmpAttributeVisitor::CheckLabelContext(const parser::CharBlock source, void OmpAttributeVisitor::AddOmpRequiresToScope(Scope &scope, WithOmpDeclarative::RequiresFlags flags, std::optional memOrder) { - Scope *scopeIter = &scope; - do { - if (Symbol * symbol{scopeIter->symbol()}) { - common::visit( - [&](auto &details) { - // Store clauses information into the symbol for the parent and - // enclosing modules, programs, functions and subroutines. - if constexpr (std::is_convertible_v) { - if (flags.any()) { - if (const WithOmpDeclarative::RequiresFlags * - otherFlags{details.ompRequires()}) { - flags |= *otherFlags; - } - details.set_ompRequires(flags); + const Scope &programUnit{omp::GetProgramUnit(scope)}; + + if (auto *symbol{const_cast(programUnit.symbol())}) { + common::visit( + [&](auto &details) { + // Store clauses information into the symbol for the parent and + // enclosing modules, programs, functions and subroutines. + if constexpr (std::is_convertible_v) { + if (flags.any()) { + if (const WithOmpDeclarative::RequiresFlags *otherFlags{ + details.ompRequires()}) { + flags |= *otherFlags; } - if (memOrder) { - if (details.has_ompAtomicDefaultMemOrder() && - *details.ompAtomicDefaultMemOrder() != *memOrder) { - context_.Say(scopeIter->sourceRange(), - "Conflicting '%s' REQUIRES clauses found in compilation " - "unit"_err_en_US, - parser::ToUpperCaseLetters(llvm::omp::getOpenMPClauseName( - llvm::omp::Clause::OMPC_atomic_default_mem_order) - .str())); - } - details.set_ompAtomicDefaultMemOrder(*memOrder); + details.set_ompRequires(flags); + } + if (memOrder) { + if (details.has_ompAtomicDefaultMemOrder() && + *details.ompAtomicDefaultMemOrder() != *memOrder) { + context_.Say(programUnit.sourceRange(), + "Conflicting '%s' REQUIRES clauses found in compilation " + "unit"_err_en_US, + parser::ToUpperCaseLetters(llvm::omp::getOpenMPClauseName( + llvm::omp::Clause::OMPC_atomic_default_mem_order) + .str())); } + details.set_ompAtomicDefaultMemOrder(*memOrder); } - }, - symbol->details()); - } - scopeIter = &scopeIter->parent(); - } while (!scopeIter->IsGlobal()); + } + }, + symbol->details()); + } } void OmpAttributeVisitor::IssueNonConformanceWarning(llvm::omp::Directive D, From 2fb2af4b9bd9dd75f093da820addac484a0e70e1 Mon Sep 17 00:00:00 2001 From: Krzysztof Parzyszek Date: Tue, 14 Oct 2025 14:47:42 -0500 Subject: [PATCH 02/11] [flang][OpenMP] Emit requirements in module files For each program unit, collect the set of requirements from REQUIRES directives in the source, and modules used by the program unit, and add them to the details of the program unit symbol. The requirements in the symbol details as now stored as clauses. Since requirements need to be emitted in the module files as OpenMP directives, this makes the clause emission straightforward via getOpenMPClauseName. Each program unit, including modules, the corresponding symbol will have the transitive closure of the requirements for everything contained or used in that program unit. --- flang/include/flang/Semantics/symbol.h | 22 +-- flang/lib/Lower/OpenMP/OpenMP.cpp | 15 +- flang/lib/Semantics/mod-file.cpp | 37 ++++ flang/lib/Semantics/resolve-directives.cpp | 161 ++++++------------ flang/lib/Semantics/resolve-directives.h | 2 - flang/lib/Semantics/resolve-names.cpp | 3 - .../Semantics/OpenMP/requires-modfile.f90 | 39 +++++ flang/test/Semantics/OpenMP/requires09.f90 | 6 +- 8 files changed, 147 insertions(+), 138 deletions(-) create mode 100644 flang/test/Semantics/OpenMP/requires-modfile.f90 diff --git a/flang/include/flang/Semantics/symbol.h b/flang/include/flang/Semantics/symbol.h index 77f567e69ce55..14da5b443633f 100644 --- a/flang/include/flang/Semantics/symbol.h +++ b/flang/include/flang/Semantics/symbol.h @@ -16,6 +16,7 @@ #include "flang/Semantics/module-dependences.h" #include "flang/Support/Fortran.h" #include "llvm/ADT/DenseMapInfo.h" +#include "llvm/Frontend/OpenMP/OMP.h" #include #include @@ -50,32 +51,31 @@ using MutableSymbolVector = std::vector; // Mixin for details with OpenMP declarative constructs. class WithOmpDeclarative { - using OmpAtomicOrderType = common::OmpMemoryOrderType; - public: - ENUM_CLASS(RequiresFlag, ReverseOffload, UnifiedAddress, UnifiedSharedMemory, - DynamicAllocators); - using RequiresFlags = common::EnumSet; + // The set of requirements for any program unit include requirements + // from any module used in the program unit. + using RequiresClauses = + common::EnumSet; bool has_ompRequires() const { return ompRequires_.has_value(); } - const RequiresFlags *ompRequires() const { + const RequiresClauses *ompRequires() const { return ompRequires_ ? &*ompRequires_ : nullptr; } - void set_ompRequires(RequiresFlags flags) { ompRequires_ = flags; } + void set_ompRequires(RequiresClauses clauses) { ompRequires_ = clauses; } bool has_ompAtomicDefaultMemOrder() const { return ompAtomicDefaultMemOrder_.has_value(); } - const OmpAtomicOrderType *ompAtomicDefaultMemOrder() const { + const common::OmpMemoryOrderType *ompAtomicDefaultMemOrder() const { return ompAtomicDefaultMemOrder_ ? &*ompAtomicDefaultMemOrder_ : nullptr; } - void set_ompAtomicDefaultMemOrder(OmpAtomicOrderType flags) { + void set_ompAtomicDefaultMemOrder(common::OmpMemoryOrderType flags) { ompAtomicDefaultMemOrder_ = flags; } private: - std::optional ompRequires_; - std::optional ompAtomicDefaultMemOrder_; + std::optional ompRequires_; + std::optional ompAtomicDefaultMemOrder_; }; // A module or submodule. diff --git a/flang/lib/Lower/OpenMP/OpenMP.cpp b/flang/lib/Lower/OpenMP/OpenMP.cpp index 444f27471020b..f86ee01355104 100644 --- a/flang/lib/Lower/OpenMP/OpenMP.cpp +++ b/flang/lib/Lower/OpenMP/OpenMP.cpp @@ -4208,18 +4208,17 @@ bool Fortran::lower::markOpenMPDeferredDeclareTargetFunctions( void Fortran::lower::genOpenMPRequires(mlir::Operation *mod, const semantics::Symbol *symbol) { using MlirRequires = mlir::omp::ClauseRequires; - using SemaRequires = semantics::WithOmpDeclarative::RequiresFlag; if (auto offloadMod = llvm::dyn_cast(mod)) { - semantics::WithOmpDeclarative::RequiresFlags semaFlags; + semantics::WithOmpDeclarative::RequiresClauses reqs; if (symbol) { common::visit( [&](const auto &details) { if constexpr (std::is_base_of_v>) { if (details.has_ompRequires()) - semaFlags = *details.ompRequires(); + reqs = *details.ompRequires(); } }, symbol->details()); @@ -4228,14 +4227,14 @@ void Fortran::lower::genOpenMPRequires(mlir::Operation *mod, // Use pre-populated omp.requires module attribute if it was set, so that // the "-fopenmp-force-usm" compiler option is honored. MlirRequires mlirFlags = offloadMod.getRequires(); - if (semaFlags.test(SemaRequires::ReverseOffload)) + if (reqs.test(llvm::omp::Clause::OMPC_dynamic_allocators)) + mlirFlags = mlirFlags | MlirRequires::dynamic_allocators; + if (reqs.test(llvm::omp::Clause::OMPC_reverse_offload)) mlirFlags = mlirFlags | MlirRequires::reverse_offload; - if (semaFlags.test(SemaRequires::UnifiedAddress)) + if (reqs.test(llvm::omp::Clause::OMPC_unified_address)) mlirFlags = mlirFlags | MlirRequires::unified_address; - if (semaFlags.test(SemaRequires::UnifiedSharedMemory)) + if (reqs.test(llvm::omp::Clause::OMPC_unified_shared_memory)) mlirFlags = mlirFlags | MlirRequires::unified_shared_memory; - if (semaFlags.test(SemaRequires::DynamicAllocators)) - mlirFlags = mlirFlags | MlirRequires::dynamic_allocators; offloadMod.setRequires(mlirFlags); } diff --git a/flang/lib/Semantics/mod-file.cpp b/flang/lib/Semantics/mod-file.cpp index 8074c94b41e1a..86cc4632a5763 100644 --- a/flang/lib/Semantics/mod-file.cpp +++ b/flang/lib/Semantics/mod-file.cpp @@ -17,6 +17,7 @@ #include "flang/Semantics/semantics.h" #include "flang/Semantics/symbol.h" #include "flang/Semantics/tools.h" +#include "llvm/Frontend/OpenMP/OMP.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/raw_ostream.h" @@ -24,6 +25,7 @@ #include #include #include +#include #include #include @@ -359,6 +361,40 @@ void ModFileWriter::PrepareRenamings(const Scope &scope) { } } +static void PutOpenMPRequirements(llvm::raw_ostream &os, const Symbol &symbol) { + using RequiresClauses = WithOmpDeclarative::RequiresClauses; + using OmpMemoryOrderType = common::OmpMemoryOrderType; + + const auto [reqs, order]{common::visit( + [&](auto &&details) + -> std::pair { + if constexpr (std::is_convertible_v) { + return {details.ompRequires(), details.ompAtomicDefaultMemOrder()}; + } else { + return {nullptr, nullptr}; + } + }, + symbol.details())}; + + if (order) { + llvm::omp::Clause atmo{llvm::omp::Clause::OMPC_atomic_default_mem_order}; + os << "!$omp requires " + << parser::ToLowerCaseLetters(llvm::omp::getOpenMPClauseName(atmo)) + << '(' << parser::ToLowerCaseLetters(EnumToString(*order)) << ")\n"; + } + if (reqs) { + os << "!$omp requires"; + reqs->IterateOverMembers([&](llvm::omp::Clause f) { + if (f != llvm::omp::Clause::OMPC_atomic_default_mem_order) { + os << ' ' + << parser::ToLowerCaseLetters(llvm::omp::getOpenMPClauseName(f)); + } + }); + os << "\n"; + } +} + // Put out the visible symbols from scope. void ModFileWriter::PutSymbols( const Scope &scope, UnorderedSymbolSet *hermeticModules) { @@ -396,6 +432,7 @@ void ModFileWriter::PutSymbols( for (const Symbol &symbol : uses) { PutUse(symbol); } + PutOpenMPRequirements(decls_, DEREF(scope.symbol())); for (const auto &set : scope.equivalenceSets()) { if (!set.empty() && !set.front().symbol.test(Symbol::Flag::CompilerCreated)) { diff --git a/flang/lib/Semantics/resolve-directives.cpp b/flang/lib/Semantics/resolve-directives.cpp index de680b41d1524..122849356ca39 100644 --- a/flang/lib/Semantics/resolve-directives.cpp +++ b/flang/lib/Semantics/resolve-directives.cpp @@ -435,6 +435,22 @@ class OmpAttributeVisitor : DirectiveAttributeVisitor { return true; } + bool Pre(const parser::UseStmt &x) { + if (x.moduleName.symbol) { + Scope &thisScope{context_.FindScope(x.moduleName.source)}; + common::visit( + [&](auto &&details) { + if constexpr (std::is_convertible_v) { + AddOmpRequiresToScope(thisScope, details.ompRequires(), + details.ompAtomicDefaultMemOrder()); + } + }, + x.moduleName.symbol->details()); + } + return true; + } + bool Pre(const parser::OmpMetadirectiveDirective &x) { PushContext(x.v.source, llvm::omp::Directive::OMPD_metadirective); return true; @@ -538,38 +554,37 @@ class OmpAttributeVisitor : DirectiveAttributeVisitor { void Post(const parser::OpenMPFlushConstruct &) { PopContext(); } bool Pre(const parser::OpenMPRequiresConstruct &x) { - using Flags = WithOmpDeclarative::RequiresFlags; - using Requires = WithOmpDeclarative::RequiresFlag; + using RequiresClauses = WithOmpDeclarative::RequiresClauses; PushContext(x.source, llvm::omp::Directive::OMPD_requires); // Gather information from the clauses. - Flags flags; - std::optional memOrder; + RequiresClauses reqs; + const common::OmpMemoryOrderType *memOrder{nullptr}; for (const parser::OmpClause &clause : x.v.Clauses().v) { - flags |= common::visit( + using OmpClause = parser::OmpClause; + reqs |= common::visit( common::visitors{ - [&memOrder]( - const parser::OmpClause::AtomicDefaultMemOrder &atomic) { - memOrder = atomic.v.v; - return Flags{}; - }, - [](const parser::OmpClause::ReverseOffload &) { - return Flags{Requires::ReverseOffload}; - }, - [](const parser::OmpClause::UnifiedAddress &) { - return Flags{Requires::UnifiedAddress}; + [&](const OmpClause::AtomicDefaultMemOrder &atomic) { + memOrder = &atomic.v.v; + return RequiresClauses{}; }, - [](const parser::OmpClause::UnifiedSharedMemory &) { - return Flags{Requires::UnifiedSharedMemory}; - }, - [](const parser::OmpClause::DynamicAllocators &) { - return Flags{Requires::DynamicAllocators}; + [&](auto &&s) { + using TypeS = llvm::remove_cvref_t; + if constexpr ( // + std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v) { + return RequiresClauses{clause.Id()}; + } else { + return RequiresClauses{}; + } }, - [](const auto &) { return Flags{}; }}, + }, clause.u); } // Merge clauses into parents' symbols details. - AddOmpRequiresToScope(currScope(), flags, memOrder); + AddOmpRequiresToScope(currScope(), &reqs, memOrder); return true; } void Post(const parser::OpenMPRequiresConstruct &) { PopContext(); } @@ -1001,8 +1016,9 @@ class OmpAttributeVisitor : DirectiveAttributeVisitor { std::int64_t ordCollapseLevel{0}; - void AddOmpRequiresToScope(Scope &, WithOmpDeclarative::RequiresFlags, - std::optional); + void AddOmpRequiresToScope(Scope &, + const WithOmpDeclarative::RequiresClauses *, + const common::OmpMemoryOrderType *); void IssueNonConformanceWarning(llvm::omp::Directive D, parser::CharBlock source, unsigned EmitFromVersion); @@ -3309,86 +3325,6 @@ void ResolveOmpParts( } } -void ResolveOmpTopLevelParts( - SemanticsContext &context, const parser::Program &program) { - if (!context.IsEnabled(common::LanguageFeature::OpenMP)) { - return; - } - - // Gather REQUIRES clauses from all non-module top-level program unit symbols, - // combine them together ensuring compatibility and apply them to all these - // program units. Modules are skipped because their REQUIRES clauses should be - // propagated via USE statements instead. - WithOmpDeclarative::RequiresFlags combinedFlags; - std::optional combinedMemOrder; - - // Function to go through non-module top level program units and extract - // REQUIRES information to be processed by a function-like argument. - auto processProgramUnits{[&](auto processFn) { - for (const parser::ProgramUnit &unit : program.v) { - if (!std::holds_alternative>( - unit.u) && - !std::holds_alternative>( - unit.u) && - !std::holds_alternative< - common::Indirection>(unit.u)) { - Symbol *symbol{common::visit( - [&context](auto &x) { - Scope *scope = GetScope(context, x.value()); - return scope ? scope->symbol() : nullptr; - }, - unit.u)}; - // FIXME There is no symbol defined for MainProgram units in certain - // circumstances, so REQUIRES information has no place to be stored in - // these cases. - if (!symbol) { - continue; - } - common::visit( - [&](auto &details) { - if constexpr (std::is_convertible_v) { - processFn(*symbol, details); - } - }, - symbol->details()); - } - } - }}; - - // Combine global REQUIRES information from all program units except modules - // and submodules. - processProgramUnits([&](Symbol &symbol, WithOmpDeclarative &details) { - if (const WithOmpDeclarative::RequiresFlags * - flags{details.ompRequires()}) { - combinedFlags |= *flags; - } - if (const common::OmpMemoryOrderType * - memOrder{details.ompAtomicDefaultMemOrder()}) { - if (combinedMemOrder && *combinedMemOrder != *memOrder) { - context.Say(symbol.scope()->sourceRange(), - "Conflicting '%s' REQUIRES clauses found in compilation " - "unit"_err_en_US, - parser::ToUpperCaseLetters(llvm::omp::getOpenMPClauseName( - llvm::omp::Clause::OMPC_atomic_default_mem_order) - .str())); - } - combinedMemOrder = *memOrder; - } - }); - - // Update all program units except modules and submodules with the combined - // global REQUIRES information. - processProgramUnits([&](Symbol &, WithOmpDeclarative &details) { - if (combinedFlags.any()) { - details.set_ompRequires(combinedFlags); - } - if (combinedMemOrder) { - details.set_ompAtomicDefaultMemOrder(*combinedMemOrder); - } - }); -} - static bool IsSymbolThreadprivate(const Symbol &symbol) { if (const auto *details{symbol.detailsIf()}) { return details->symbol().test(Symbol::Flag::OmpThreadprivate); @@ -3547,23 +3483,22 @@ void OmpAttributeVisitor::CheckLabelContext(const parser::CharBlock source, } void OmpAttributeVisitor::AddOmpRequiresToScope(Scope &scope, - WithOmpDeclarative::RequiresFlags flags, - std::optional memOrder) { + const WithOmpDeclarative::RequiresClauses *reqs, + const common::OmpMemoryOrderType *memOrder) { const Scope &programUnit{omp::GetProgramUnit(scope)}; + using RequiresClauses = WithOmpDeclarative::RequiresClauses; + RequiresClauses combinedReqs{reqs ? *reqs : RequiresClauses{}}; if (auto *symbol{const_cast(programUnit.symbol())}) { common::visit( [&](auto &details) { - // Store clauses information into the symbol for the parent and - // enclosing modules, programs, functions and subroutines. if constexpr (std::is_convertible_v) { - if (flags.any()) { - if (const WithOmpDeclarative::RequiresFlags *otherFlags{ - details.ompRequires()}) { - flags |= *otherFlags; + if (combinedReqs.any()) { + if (const RequiresClauses *otherReqs{details.ompRequires()}) { + combinedReqs |= *otherReqs; } - details.set_ompRequires(flags); + details.set_ompRequires(combinedReqs); } if (memOrder) { if (details.has_ompAtomicDefaultMemOrder() && diff --git a/flang/lib/Semantics/resolve-directives.h b/flang/lib/Semantics/resolve-directives.h index 5a890c26aa334..36d3ce988b1b1 100644 --- a/flang/lib/Semantics/resolve-directives.h +++ b/flang/lib/Semantics/resolve-directives.h @@ -23,7 +23,5 @@ class SemanticsContext; void ResolveAccParts( SemanticsContext &, const parser::ProgramUnit &, Scope *topScope); void ResolveOmpParts(SemanticsContext &, const parser::ProgramUnit &); -void ResolveOmpTopLevelParts(SemanticsContext &, const parser::Program &); - } // namespace Fortran::semantics #endif diff --git a/flang/lib/Semantics/resolve-names.cpp b/flang/lib/Semantics/resolve-names.cpp index 861218809c0f9..ae0ff9ca8068d 100644 --- a/flang/lib/Semantics/resolve-names.cpp +++ b/flang/lib/Semantics/resolve-names.cpp @@ -10687,9 +10687,6 @@ void ResolveNamesVisitor::Post(const parser::Program &x) { CHECK(!attrs_); CHECK(!cudaDataAttr_); CHECK(!GetDeclTypeSpec()); - // Top-level resolution to propagate information across program units after - // each of them has been resolved separately. - ResolveOmpTopLevelParts(context(), x); } // A singleton instance of the scope -> IMPLICIT rules mapping is diff --git a/flang/test/Semantics/OpenMP/requires-modfile.f90 b/flang/test/Semantics/OpenMP/requires-modfile.f90 new file mode 100644 index 0000000000000..2f06104e208ef --- /dev/null +++ b/flang/test/Semantics/OpenMP/requires-modfile.f90 @@ -0,0 +1,39 @@ +!RUN: %python %S/../test_modfile.py %s %flang_fc1 -fopenmp -fopenmp-version=52 + +module req +contains + +! The requirements from the subprograms should be added to the module. +subroutine f00 + !$omp requires reverse_offload +end + +subroutine f01 + !$omp requires atomic_default_mem_order(seq_cst) +end +end module + +module user +! The requirements from module req should be propagated to this module. +use req +end module + + +!Expect: req.mod +!module req +!!$omp requires atomic_default_mem_order(seq_cst) +!!$omp requires reverse_offload +!contains +!subroutine f00() +!end +!subroutine f01() +!end +!end + +!Expect: user.mod +!module user +!use req,only:f00 +!use req,only:f01 +!!$omp requires atomic_default_mem_order(seq_cst) +!!$omp requires reverse_offload +!end diff --git a/flang/test/Semantics/OpenMP/requires09.f90 b/flang/test/Semantics/OpenMP/requires09.f90 index 2fa5d950b9c2d..ca6ad5e8b7b8a 100644 --- a/flang/test/Semantics/OpenMP/requires09.f90 +++ b/flang/test/Semantics/OpenMP/requires09.f90 @@ -3,12 +3,16 @@ ! 2.4 Requires directive ! All atomic_default_mem_order clauses in 'requires' directives found within a ! compilation unit must specify the same ordering. +!ERROR: Conflicting 'ATOMIC_DEFAULT_MEM_ORDER' REQUIRES clauses found in compilation unit +module m +contains subroutine f !$omp requires atomic_default_mem_order(seq_cst) end subroutine f -!ERROR: Conflicting 'ATOMIC_DEFAULT_MEM_ORDER' REQUIRES clauses found in compilation unit subroutine g !$omp requires atomic_default_mem_order(relaxed) end subroutine g + +end module From 639f10efafc662f28644f71301f40f8c51012cf8 Mon Sep 17 00:00:00 2001 From: Krzysztof Parzyszek Date: Tue, 14 Oct 2025 15:02:19 -0500 Subject: [PATCH 03/11] [flang][OpenMP] Dump requirement clauses/flags in WithOmpDeclarative --- flang/include/flang/Semantics/symbol.h | 3 ++ flang/lib/Semantics/symbol.cpp | 32 ++++++++++++++++++- .../OpenMP/dump-requires-details.f90 | 14 ++++++++ 3 files changed, 48 insertions(+), 1 deletion(-) create mode 100644 flang/test/Semantics/OpenMP/dump-requires-details.f90 diff --git a/flang/include/flang/Semantics/symbol.h b/flang/include/flang/Semantics/symbol.h index 14da5b443633f..04a063957082a 100644 --- a/flang/include/flang/Semantics/symbol.h +++ b/flang/include/flang/Semantics/symbol.h @@ -73,6 +73,9 @@ class WithOmpDeclarative { ompAtomicDefaultMemOrder_ = flags; } + friend llvm::raw_ostream &operator<<( + llvm::raw_ostream &, const WithOmpDeclarative &); + private: std::optional ompRequires_; std::optional ompAtomicDefaultMemOrder_; diff --git a/flang/lib/Semantics/symbol.cpp b/flang/lib/Semantics/symbol.cpp index 69169469fe8ce..a5f9706a73cf7 100644 --- a/flang/lib/Semantics/symbol.cpp +++ b/flang/lib/Semantics/symbol.cpp @@ -70,6 +70,32 @@ static void DumpList(llvm::raw_ostream &os, const char *label, const T &list) { } } +llvm::raw_ostream &operator<<( + llvm::raw_ostream &os, const WithOmpDeclarative &x) { + if (x.has_ompRequires() || x.has_ompAtomicDefaultMemOrder()) { + os << " OmpRequirements:("; + if (const common::OmpMemoryOrderType *atmo{x.ompAtomicDefaultMemOrder()}) { + os << parser::ToLowerCaseLetters(llvm::omp::getOpenMPClauseName( + llvm::omp::Clause::OMPC_atomic_default_mem_order)) + << '(' << parser::ToLowerCaseLetters(EnumToString(*atmo)) << ')'; + if (x.has_ompRequires()) { + os << ','; + } + } + if (const WithOmpDeclarative::RequiresClauses *reqs{x.ompRequires()}) { + size_t num{0}, size{reqs->count()}; + reqs->IterateOverMembers([&](llvm::omp::Clause f) { + os << parser::ToLowerCaseLetters(llvm::omp::getOpenMPClauseName(f)); + if (++num < size) { + os << ','; + } + }); + } + os << ')'; + } + return os; +} + void SubprogramDetails::set_moduleInterface(Symbol &symbol) { CHECK(!moduleInterface_); moduleInterface_ = &symbol; @@ -150,6 +176,7 @@ llvm::raw_ostream &operator<<( os << x; } } + os << static_cast(x); return os; } @@ -580,7 +607,9 @@ llvm::raw_ostream &operator<<(llvm::raw_ostream &os, const Details &details) { common::visit( // common::visitors{ [&](const UnknownDetails &) {}, - [&](const MainProgramDetails &) {}, + [&](const MainProgramDetails &x) { + os << static_cast(x); + }, [&](const ModuleDetails &x) { if (x.isSubmodule()) { os << " ("; @@ -599,6 +628,7 @@ llvm::raw_ostream &operator<<(llvm::raw_ostream &os, const Details &details) { if (x.isDefaultPrivate()) { os << " isDefaultPrivate"; } + os << static_cast(x); }, [&](const SubprogramNameDetails &x) { os << ' ' << EnumToString(x.kind()); diff --git a/flang/test/Semantics/OpenMP/dump-requires-details.f90 b/flang/test/Semantics/OpenMP/dump-requires-details.f90 new file mode 100644 index 0000000000000..9c844c092c5e6 --- /dev/null +++ b/flang/test/Semantics/OpenMP/dump-requires-details.f90 @@ -0,0 +1,14 @@ +!RUN: %flang_fc1 -fopenmp -fopenmp-version=60 -fdebug-dump-symbols %s | FileCheck %s + +module fred +!$omp requires atomic_default_mem_order(relaxed) +contains +subroutine f00 + !$omp requires unified_address +end +subroutine f01 + !$omp requires unified_shared_memory +end +end module + +!CHECK: fred: Module OmpRequirements:(atomic_default_mem_order(relaxed),unified_address,unified_shared_memory) From f3532248a2569e21ad476d66bd302dfb5d028185 Mon Sep 17 00:00:00 2001 From: Krzysztof Parzyszek Date: Tue, 14 Oct 2025 15:58:14 -0500 Subject: [PATCH 04/11] format --- flang/lib/Semantics/symbol.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/flang/lib/Semantics/symbol.cpp b/flang/lib/Semantics/symbol.cpp index a5f9706a73cf7..2a0ac80d28629 100644 --- a/flang/lib/Semantics/symbol.cpp +++ b/flang/lib/Semantics/symbol.cpp @@ -176,7 +176,7 @@ llvm::raw_ostream &operator<<( os << x; } } - os << static_cast(x); + os << static_cast(x); return os; } @@ -608,7 +608,7 @@ llvm::raw_ostream &operator<<(llvm::raw_ostream &os, const Details &details) { common::visitors{ [&](const UnknownDetails &) {}, [&](const MainProgramDetails &x) { - os << static_cast(x); + os << static_cast(x); }, [&](const ModuleDetails &x) { if (x.isSubmodule()) { @@ -628,7 +628,7 @@ llvm::raw_ostream &operator<<(llvm::raw_ostream &os, const Details &details) { if (x.isDefaultPrivate()) { os << " isDefaultPrivate"; } - os << static_cast(x); + os << static_cast(x); }, [&](const SubprogramNameDetails &x) { os << ' ' << EnumToString(x.kind()); From 0ccf7a0d647864fc84fd81440c7cecee29615cd0 Mon Sep 17 00:00:00 2001 From: Krzysztof Parzyszek Date: Tue, 14 Oct 2025 15:59:20 -0500 Subject: [PATCH 05/11] fix typo --- flang/lib/Semantics/symbol.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/flang/lib/Semantics/symbol.cpp b/flang/lib/Semantics/symbol.cpp index 2a0ac80d28629..0ec44b7c40491 100644 --- a/flang/lib/Semantics/symbol.cpp +++ b/flang/lib/Semantics/symbol.cpp @@ -74,10 +74,10 @@ llvm::raw_ostream &operator<<( llvm::raw_ostream &os, const WithOmpDeclarative &x) { if (x.has_ompRequires() || x.has_ompAtomicDefaultMemOrder()) { os << " OmpRequirements:("; - if (const common::OmpMemoryOrderType *atmo{x.ompAtomicDefaultMemOrder()}) { + if (const common::OmpMemoryOrderType *admo{x.ompAtomicDefaultMemOrder()}) { os << parser::ToLowerCaseLetters(llvm::omp::getOpenMPClauseName( llvm::omp::Clause::OMPC_atomic_default_mem_order)) - << '(' << parser::ToLowerCaseLetters(EnumToString(*atmo)) << ')'; + << '(' << parser::ToLowerCaseLetters(EnumToString(*admo)) << ')'; if (x.has_ompRequires()) { os << ','; } From 0bde0179ec60559efdcafbca73f68dddf2ce51d2 Mon Sep 17 00:00:00 2001 From: Krzysztof Parzyszek Date: Thu, 9 Oct 2025 14:23:21 -0500 Subject: [PATCH 06/11] [flang][OpenMP] Add optional argument to requirement clauses OpenMP 6.0 added an optional logical parameter to the requirement clauses (except ATOMIC_DEFAULT_MEM_ORDER) to indicate whether the clause should take effect or not. The parameter defaults to true if not specified. The parameter value is a compile-time constant expression, but it may require folding to get the final value. Since name resolution happens before folding, the argument expression needs to be analyzed by hand. The determination of the value needs to happen during name resolution because the requirement directives need to be available through module files (and the module reader doesn't to semantic checks beyonn name resolution). --- flang/include/flang/Lower/OpenMP/Clauses.h | 1 + flang/include/flang/Parser/dump-parse-tree.h | 5 ++ flang/include/flang/Parser/parse-tree.h | 49 +++++++++++++- flang/lib/Lower/OpenMP/Clauses.cpp | 68 +++++++++++++++++--- flang/lib/Parser/openmp-parsers.cpp | 14 ++-- flang/lib/Semantics/check-omp-structure.cpp | 41 +++++++++--- flang/lib/Semantics/resolve-directives.cpp | 24 ++++++- flang/test/Parser/OpenMP/requires.f90 | 14 ++++ flang/test/Semantics/OpenMP/requires10.f90 | 13 ++++ llvm/include/llvm/Frontend/OpenMP/ClauseT.h | 57 +++++++++------- llvm/include/llvm/Frontend/OpenMP/OMP.td | 10 +++ 11 files changed, 247 insertions(+), 49 deletions(-) create mode 100644 flang/test/Semantics/OpenMP/requires10.f90 diff --git a/flang/include/flang/Lower/OpenMP/Clauses.h b/flang/include/flang/Lower/OpenMP/Clauses.h index 5cd196a7869a2..273e61166fd9d 100644 --- a/flang/include/flang/Lower/OpenMP/Clauses.h +++ b/flang/include/flang/Lower/OpenMP/Clauses.h @@ -282,6 +282,7 @@ using Replayable = tomp::clause::ReplayableT; using ReverseOffload = tomp::clause::ReverseOffloadT; using Safelen = tomp::clause::SafelenT; using Schedule = tomp::clause::ScheduleT; +using SelfMaps = tomp::clause::SelfMapsT; using SeqCst = tomp::clause::SeqCstT; using Severity = tomp::clause::SeverityT; using Shared = tomp::clause::SharedT; diff --git a/flang/include/flang/Parser/dump-parse-tree.h b/flang/include/flang/Parser/dump-parse-tree.h index 14885293fd5eb..8854dc2267c8e 100644 --- a/flang/include/flang/Parser/dump-parse-tree.h +++ b/flang/include/flang/Parser/dump-parse-tree.h @@ -566,6 +566,7 @@ class ParseTreeDumper { NODE(OmpDoacross, Sink) NODE(OmpDoacross, Source) NODE(parser, OmpDoacrossClause) + NODE(parser, OmpDynamicAllocatorsClause) NODE(parser, OmpDynGroupprivateClause) NODE(OmpDynGroupprivateClause, Modifier) NODE(parser, OmpEndDirective) @@ -659,9 +660,11 @@ class ParseTreeDumper { NODE(parser, OmpRefModifier) NODE_ENUM(OmpRefModifier, Value) NODE(parser, OmpReplayableClause) + NODE(parser, OmpReverseOffloadClause) NODE(parser, OmpScheduleClause) NODE(OmpScheduleClause, Modifier) NODE_ENUM(OmpScheduleClause, Kind) + NODE(parser, OmpSelfMapsClause) NODE(parser, OmpSelfModifier) NODE_ENUM(OmpSelfModifier, Value) NODE(parser, OmpSeverityClause) @@ -689,6 +692,8 @@ class ParseTreeDumper { NODE(parser, OmpTransparentClause) NODE(parser, OmpTypeNameList) NODE(parser, OmpTypeSpecifier) + NODE(parser, OmpUnifiedAddressClause) + NODE(parser, OmpUnifiedSharedMemoryClause) NODE(parser, OmpUpdateClause) NODE(parser, OmpUseClause) NODE(parser, OmpVariableCategory) diff --git a/flang/include/flang/Parser/parse-tree.h b/flang/include/flang/Parser/parse-tree.h index d919b777d7487..18b583669421a 100644 --- a/flang/include/flang/Parser/parse-tree.h +++ b/flang/include/flang/Parser/parse-tree.h @@ -337,6 +337,7 @@ using IntConstantExpr = Integer; // R1031 using ScalarLogicalExpr = Scalar; using ScalarIntExpr = Scalar; using ScalarIntConstantExpr = Scalar; +using ScalarLogicalConstantExpr = Scalar>; using ScalarDefaultCharExpr = Scalar; // R1030 default-char-constant-expr is used in the Standard only as part of // scalar-default-char-constant-expr. @@ -4414,6 +4415,16 @@ struct OmpDeviceTypeClause { WRAPPER_CLASS_BOILERPLATE(OmpDeviceTypeClause, DeviceTypeDescription); }; +// Ref: [5.0:60-63], [5.1:83-86], [5.2:212-213], [6.0:356-362] +// +// dynamic-allocators-clause -> +// DYNAMIC_ALLOCATORS // since 5.0 +// [(scalar-logical-const-expr)] // since 6.0 +struct OmpDynamicAllocatorsClause { + WRAPPER_CLASS_BOILERPLATE( + OmpDynamicAllocatorsClause, ScalarLogicalConstantExpr); +}; + struct OmpDynGroupprivateClause { TUPLE_CLASS_BOILERPLATE(OmpDynGroupprivateClause); MODIFIER_BOILERPLATE(OmpAccessGroup, OmpPrescriptiveness); @@ -4690,7 +4701,16 @@ struct OmpReductionClause { // replayable-clause -> // REPLAYABLE[(replayable-expression)] // since 6.0 struct OmpReplayableClause { - WRAPPER_CLASS_BOILERPLATE(OmpReplayableClause, Scalar>); + WRAPPER_CLASS_BOILERPLATE(OmpReplayableClause, ScalarLogicalConstantExpr); +}; + +// Ref: [5.0:60-63], [5.1:83-86], [5.2:212-213], [6.0:356-362] +// +// reverse-offload-clause -> +// REVERSE_OFFLOAD // since 5.0 +// [(scalar-logical-const-expr)] // since 6.0 +struct OmpReverseOffloadClause { + WRAPPER_CLASS_BOILERPLATE(OmpReverseOffloadClause, ScalarLogicalConstantExpr); }; // Ref: [4.5:56-63], [5.0:101-109], [5.1:126-133], [5.2:252-254] @@ -4708,6 +4728,14 @@ struct OmpScheduleClause { std::tuple> t; }; +// ref: [6.0:361-362] +// +// self-maps-clause -> +// SELF_MAPS [(scalar-logical-const-expr)] // since 6.0 +struct OmpSelfMapsClause { + WRAPPER_CLASS_BOILERPLATE(OmpSelfMapsClause, ScalarLogicalConstantExpr); +}; + // REF: [5.2:217] // severity-clause -> // SEVERITY(warning|fatal) @@ -4750,6 +4778,25 @@ struct OmpTransparentClause { WRAPPER_CLASS_BOILERPLATE(OmpTransparentClause, ScalarIntExpr); }; +// Ref: [5.0:60-63], [5.1:83-86], [5.2:212-213], [6.0:356-362] +// +// unified-address-clause -> +// UNIFIED_ADDRESS // since 5.0 +// [(scalar-logical-const-expr)] // since 6.0 +struct OmpUnifiedAddressClause { + WRAPPER_CLASS_BOILERPLATE(OmpUnifiedAddressClause, ScalarLogicalConstantExpr); +}; + +// Ref: [5.0:60-63], [5.1:83-86], [5.2:212-213], [6.0:356-362] +// +// unified-shared-memory-clause -> +// UNIFIED_SHARED_MEMORY // since 5.0 +// [(scalar-logical-const-expr)] // since 6.0 +struct OmpUnifiedSharedMemoryClause { + WRAPPER_CLASS_BOILERPLATE( + OmpUnifiedSharedMemoryClause, ScalarLogicalConstantExpr); +}; + // Ref: [5.0:254-255], [5.1:287-288], [5.2:321-322] // // In ATOMIC construct diff --git a/flang/lib/Lower/OpenMP/Clauses.cpp b/flang/lib/Lower/OpenMP/Clauses.cpp index fac37a372caaf..60e569efa964a 100644 --- a/flang/lib/Lower/OpenMP/Clauses.cpp +++ b/flang/lib/Lower/OpenMP/Clauses.cpp @@ -219,7 +219,6 @@ MAKE_EMPTY_CLASS(AcqRel, AcqRel); MAKE_EMPTY_CLASS(Acquire, Acquire); MAKE_EMPTY_CLASS(Capture, Capture); MAKE_EMPTY_CLASS(Compare, Compare); -MAKE_EMPTY_CLASS(DynamicAllocators, DynamicAllocators); MAKE_EMPTY_CLASS(Full, Full); MAKE_EMPTY_CLASS(Inbranch, Inbranch); MAKE_EMPTY_CLASS(Mergeable, Mergeable); @@ -235,13 +234,9 @@ MAKE_EMPTY_CLASS(OmpxBare, OmpxBare); MAKE_EMPTY_CLASS(Read, Read); MAKE_EMPTY_CLASS(Relaxed, Relaxed); MAKE_EMPTY_CLASS(Release, Release); -MAKE_EMPTY_CLASS(ReverseOffload, ReverseOffload); MAKE_EMPTY_CLASS(SeqCst, SeqCst); -MAKE_EMPTY_CLASS(SelfMaps, SelfMaps); MAKE_EMPTY_CLASS(Simd, Simd); MAKE_EMPTY_CLASS(Threads, Threads); -MAKE_EMPTY_CLASS(UnifiedAddress, UnifiedAddress); -MAKE_EMPTY_CLASS(UnifiedSharedMemory, UnifiedSharedMemory); MAKE_EMPTY_CLASS(Unknown, Unknown); MAKE_EMPTY_CLASS(Untied, Untied); MAKE_EMPTY_CLASS(Weak, Weak); @@ -775,7 +770,18 @@ Doacross make(const parser::OmpClause::Doacross &inp, return makeDoacross(inp.v.v, semaCtx); } -// DynamicAllocators: empty +DynamicAllocators make(const parser::OmpClause::DynamicAllocators &inp, + semantics::SemanticsContext &semaCtx) { + // inp.v -> td::optional + auto &&maybeRequired = maybeApply( + [&](const parser::OmpDynamicAllocatorsClause &c) { + return makeExpr(c.v, semaCtx); + }, + inp.v); + + return DynamicAllocators{/*Required=*/std::move(maybeRequired)}; +} + DynGroupprivate make(const parser::OmpClause::DynGroupprivate &inp, semantics::SemanticsContext &semaCtx) { @@ -1321,7 +1327,18 @@ Reduction make(const parser::OmpClause::Reduction &inp, // Relaxed: empty // Release: empty -// ReverseOffload: empty + +ReverseOffload make(const parser::OmpClause::ReverseOffload &inp, + semantics::SemanticsContext &semaCtx) { + // inp.v -> std::optional + auto &&maybeRequired = maybeApply( + [&](const parser::OmpReverseOffloadClause &c) { + return makeExpr(c.v, semaCtx); + }, + inp.v); + + return ReverseOffload{/*Required=*/std::move(maybeRequired)}; +} Safelen make(const parser::OmpClause::Safelen &inp, semantics::SemanticsContext &semaCtx) { @@ -1374,6 +1391,18 @@ Schedule make(const parser::OmpClause::Schedule &inp, // SeqCst: empty +SelfMaps make(const parser::OmpClause::SelfMaps &inp, + semantics::SemanticsContext &semaCtx) { + // inp.v -> std::optional + auto &&maybeRequired = maybeApply( + [&](const parser::OmpSelfMapsClause &c) { + return makeExpr(c.v, semaCtx); + }, + inp.v); + + return SelfMaps{/*Required=*/std::move(maybeRequired)}; +} + Severity make(const parser::OmpClause::Severity &inp, semantics::SemanticsContext &semaCtx) { // inp -> empty @@ -1463,8 +1492,29 @@ To make(const parser::OmpClause::To &inp, /*LocatorList=*/makeObjects(t3, semaCtx)}}; } -// UnifiedAddress: empty -// UnifiedSharedMemory: empty +UnifiedAddress make(const parser::OmpClause::UnifiedAddress &inp, + semantics::SemanticsContext &semaCtx) { + // inp.v -> std::optional + auto &&maybeRequired = maybeApply( + [&](const parser::OmpUnifiedAddressClause &c) { + return makeExpr(c.v, semaCtx); + }, + inp.v); + + return UnifiedAddress{/*Required=*/std::move(maybeRequired)}; +} + +UnifiedSharedMemory make(const parser::OmpClause::UnifiedSharedMemory &inp, + semantics::SemanticsContext &semaCtx) { + // inp.v -> std::optional + auto &&maybeRequired = maybeApply( + [&](const parser::OmpUnifiedSharedMemoryClause &c) { + return makeExpr(c.v, semaCtx); + }, + inp.v); + + return UnifiedSharedMemory{/*Required=*/std::move(maybeRequired)}; +} Uniform make(const parser::OmpClause::Uniform &inp, semantics::SemanticsContext &semaCtx) { diff --git a/flang/lib/Parser/openmp-parsers.cpp b/flang/lib/Parser/openmp-parsers.cpp index 9507021057476..0a70930a0db33 100644 --- a/flang/lib/Parser/openmp-parsers.cpp +++ b/flang/lib/Parser/openmp-parsers.cpp @@ -1158,7 +1158,8 @@ TYPE_PARSER( // "DOACROSS" >> construct(parenthesized(Parser{})) || "DYNAMIC_ALLOCATORS" >> - construct(construct()) || + construct(construct( + maybe(parenthesized(scalarLogicalConstantExpr)))) || "DYN_GROUPPRIVATE" >> construct(construct( parenthesized(Parser{}))) || @@ -1270,12 +1271,15 @@ TYPE_PARSER( // "REPLAYABLE" >> construct(construct( maybe(parenthesized(Parser{})))) || "REVERSE_OFFLOAD" >> - construct(construct()) || + construct(construct( + maybe(parenthesized(scalarLogicalConstantExpr)))) || "SAFELEN" >> construct(construct( parenthesized(scalarIntConstantExpr))) || "SCHEDULE" >> construct(construct( parenthesized(Parser{}))) || "SEQ_CST" >> construct(construct()) || + "SELF_MAPS" >> construct(construct( + maybe(parenthesized(scalarLogicalConstantExpr)))) || "SEVERITY" >> construct(construct( parenthesized(Parser{}))) || "SHARED" >> construct(construct( @@ -1303,9 +1307,11 @@ TYPE_PARSER( // construct(construct( parenthesized(Parser{}))) || "UNIFIED_ADDRESS" >> - construct(construct()) || + construct(construct( + maybe(parenthesized(scalarLogicalConstantExpr)))) || "UNIFIED_SHARED_MEMORY" >> - construct(construct()) || + construct(construct( + maybe(parenthesized(scalarLogicalConstantExpr)))) || "UNIFORM" >> construct(construct( parenthesized(nonemptyList(name)))) || "UNTIED" >> construct(construct()) || diff --git a/flang/lib/Semantics/check-omp-structure.cpp b/flang/lib/Semantics/check-omp-structure.cpp index d65a89e768466..1c27df18fba20 100644 --- a/flang/lib/Semantics/check-omp-structure.cpp +++ b/flang/lib/Semantics/check-omp-structure.cpp @@ -1517,19 +1517,42 @@ void OmpStructureChecker::Leave(const parser::OpenMPDepobjConstruct &x) { void OmpStructureChecker::Enter(const parser::OpenMPRequiresConstruct &x) { const auto &dirName{x.v.DirName()}; PushContextAndClauseSets(dirName.source, dirName.v); + unsigned version{context_.langOptions().OpenMPVersion}; - if (visitedAtomicSource_.empty()) { - return; - } for (const parser::OmpClause &clause : x.v.Clauses().v) { llvm::omp::Clause id{clause.Id()}; if (id == llvm::omp::Clause::OMPC_atomic_default_mem_order) { - parser::MessageFormattedText txt( - "REQUIRES directive with '%s' clause found lexically after atomic operation without a memory order clause"_err_en_US, - parser::ToUpperCaseLetters(llvm::omp::getOpenMPClauseName(id))); - parser::Message message(clause.source, txt); - message.Attach(visitedAtomicSource_, "Previous atomic construct"_en_US); - context_.Say(std::move(message)); + if (!visitedAtomicSource_.empty()) { + parser::MessageFormattedText txt( + "REQUIRES directive with '%s' clause found lexically after atomic operation without a memory order clause"_err_en_US, + parser::ToUpperCaseLetters(llvm::omp::getOpenMPClauseName(id))); + parser::Message message(clause.source, txt); + message.Attach(visitedAtomicSource_, "Previous atomic construct"_en_US); + context_.Say(std::move(message)); + } + } else { + bool hasArgument{common::visit( + [&](auto &&s) { + using TypeS = llvm::remove_cvref_t; + if constexpr ( // + std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v) { + return s.v.has_value(); + } else { + return false; + } + }, + clause.u)}; + if (version < 60 && hasArgument) { + context_.Say(clause.source, + "An argument to %s is an %s feature, %s"_warn_en_US, + parser::ToUpperCaseLetters( + llvm::omp::getOpenMPClauseName(clause.Id())), + ThisVersion(60), TryVersion(60)); + } } } } diff --git a/flang/lib/Semantics/resolve-directives.cpp b/flang/lib/Semantics/resolve-directives.cpp index 122849356ca39..7067ed3d99286 100644 --- a/flang/lib/Semantics/resolve-directives.cpp +++ b/flang/lib/Semantics/resolve-directives.cpp @@ -557,6 +557,21 @@ class OmpAttributeVisitor : DirectiveAttributeVisitor { using RequiresClauses = WithOmpDeclarative::RequiresClauses; PushContext(x.source, llvm::omp::Directive::OMPD_requires); + auto getArgument{[&](auto &&maybeClause) { + if (maybeClause) { + // Scalar>>> + auto &parserExpr{maybeClause->v.thing.thing.thing.value()}; + evaluate::ExpressionAnalyzer ea{context_}; + if (auto &&maybeExpr{ea.Analyze(parserExpr)}) { + if (auto v{omp::GetLogicalValue(*maybeExpr)}) { + return *v; + } + } + } + // If the argument is missing, it is assumed to be true. + return true; + }}; + // Gather information from the clauses. RequiresClauses reqs; const common::OmpMemoryOrderType *memOrder{nullptr}; @@ -573,16 +588,19 @@ class OmpAttributeVisitor : DirectiveAttributeVisitor { if constexpr ( // std::is_same_v || std::is_same_v || + std::is_same_v || std::is_same_v || std::is_same_v) { - return RequiresClauses{clause.Id()}; - } else { - return RequiresClauses{}; + if (getArgument(s.v)) { + return RequiresClauses{clause.Id()}; + } } + return RequiresClauses{}; }, }, clause.u); } + // Merge clauses into parents' symbols details. AddOmpRequiresToScope(currScope(), &reqs, memOrder); return true; diff --git a/flang/test/Parser/OpenMP/requires.f90 b/flang/test/Parser/OpenMP/requires.f90 index 6cbb06eaf93c0..8169403835705 100644 --- a/flang/test/Parser/OpenMP/requires.f90 +++ b/flang/test/Parser/OpenMP/requires.f90 @@ -30,4 +30,18 @@ !PARSE-TREE: | OmpClause -> ReverseOffload !PARSE-TREE: | Flags = None +!$omp requires self_maps(.true.) unified_address(.false.) + +!UNPARSE: !$OMP REQUIRES SELF_MAPS(.true._4) UNIFIED_ADDRESS(.false._4) + +!PARSE-TREE: OpenMPDeclarativeConstruct -> OpenMPRequiresConstruct -> OmpDirectiveSpecification +!PARSE-TREE: | OmpDirectiveName -> llvm::omp::Directive = requires +!PARSE-TREE: | OmpClauseList -> OmpClause -> SelfMaps -> OmpSelfMapsClause -> Scalar -> Logical -> Constant -> Expr = '.true._4' +!PARSE-TREE: | | LiteralConstant -> LogicalLiteralConstant +!PARSE-TREE: | | | bool = 'true' +!PARSE-TREE: | OmpClause -> UnifiedAddress -> OmpUnifiedAddressClause -> Scalar -> Logical -> Constant -> Expr = '.false._4' +!PARSE-TREE: | | LiteralConstant -> LogicalLiteralConstant +!PARSE-TREE: | | | bool = 'false' +!PARSE-TREE: | Flags = None + end diff --git a/flang/test/Semantics/OpenMP/requires10.f90 b/flang/test/Semantics/OpenMP/requires10.f90 new file mode 100644 index 0000000000000..9f9832da3726e --- /dev/null +++ b/flang/test/Semantics/OpenMP/requires10.f90 @@ -0,0 +1,13 @@ +!RUN: %python %S/../test_errors.py %s %flang -fopenmp -fopenmp-version=52 + +subroutine f00(x) + logical :: x + !ERROR: An argument to REVERSE_OFFLOAD is an OpenMP v6.0 feature, try -fopenmp-version=60 + !ERROR: Must be a constant value + !$omp requires reverse_offload(x) +end + +subroutine f01 + !WARNING: An argument to REVERSE_OFFLOAD is an OpenMP v6.0 feature, try -fopenmp-version=60 + !$omp requires reverse_offload(.true.) +end diff --git a/llvm/include/llvm/Frontend/OpenMP/ClauseT.h b/llvm/include/llvm/Frontend/OpenMP/ClauseT.h index db781b58944bc..9153dd1c56b7f 100644 --- a/llvm/include/llvm/Frontend/OpenMP/ClauseT.h +++ b/llvm/include/llvm/Frontend/OpenMP/ClauseT.h @@ -571,7 +571,9 @@ struct DoacrossT { // V5.2: [8.2.1] `requirement` clauses template // struct DynamicAllocatorsT { - using EmptyTrait = std::true_type; + using Requires = E; + using WrapperTrait = std::true_type; + OPT(Requires) v; }; template // @@ -1055,7 +1057,9 @@ struct ReplayableT { // V5.2: [8.2.1] `requirement` clauses template // struct ReverseOffloadT { - using EmptyTrait = std::true_type; + using Requires = E; + using WrapperTrait = std::true_type; + OPT(Requires) v; }; // V5.2: [10.4.2] `safelen` clause @@ -1077,6 +1081,14 @@ struct ScheduleT { std::tuple t; }; +// [6.0:361] +template // +struct SelfMapsT { + using Requires = E; + using WrapperTrait = std::true_type; + OPT(Requires) v; +}; + // V5.2: [15.8.1] Memory-order clauses template // struct SeqCstT { @@ -1168,18 +1180,17 @@ struct TransparentT { // V5.2: [8.2.1] `requirement` clauses template // struct UnifiedAddressT { - using EmptyTrait = std::true_type; + using Requires = E; + using WrapperTrait = std::true_type; + OPT(Requires) v; }; // V5.2: [8.2.1] `requirement` clauses template // struct UnifiedSharedMemoryT { - using EmptyTrait = std::true_type; -}; - -template // -struct SelfMapsT { - using EmptyTrait = std::true_type; + using Requires = E; + using WrapperTrait = std::true_type; + OPT(Requires) v; }; // V5.2: [5.10] `uniform` clause @@ -1287,14 +1298,12 @@ using ExtensionClausesT = template using EmptyClausesT = std::variant< AcqRelT, AcquireT, CaptureT, CompareT, - DynamicAllocatorsT, FullT, InbranchT, - MergeableT, NogroupT, NoOpenmpRoutinesT, + FullT, InbranchT, MergeableT, NogroupT, + NoOpenmpConstructsT, NoOpenmpRoutinesT, NoOpenmpT, NoParallelismT, NotinbranchT, NowaitT, ReadT, RelaxedT, ReleaseT, - ReverseOffloadT, SeqCstT, SimdT, - ThreadsT, UnifiedAddressT, UnifiedSharedMemoryT, - UnknownT, UntiedT, UseT, WeakT, - WriteT, NoOpenmpConstructsT, SelfMapsT>; + SeqCstT, SimdT, ThreadsT, UnknownT, + UntiedT, UseT, WeakT, WriteT>; template using IncompleteClausesT = @@ -1322,18 +1331,20 @@ using WrapperClausesT = std::variant< AtomicDefaultMemOrderT, AtT, BindT, CollapseT, ContainsT, CopyinT, CopyprivateT, DefaultT, DestroyT, - DetachT, DeviceTypeT, EnterT, - ExclusiveT, FailT, FilterT, FinalT, - FirstprivateT, HasDeviceAddrT, HintT, - HoldsT, InclusiveT, IndirectT, + DetachT, DeviceTypeT, DynamicAllocatorsT, + EnterT, ExclusiveT, FailT, FilterT, + FinalT, FirstprivateT, HasDeviceAddrT, + HintT, HoldsT, InclusiveT, IndirectT, InitializerT, IsDevicePtrT, LinkT, MessageT, NocontextT, NontemporalT, NovariantsT, NumTeamsT, NumThreadsT, OrderedT, PartialT, PriorityT, PrivateT, - ProcBindT, SafelenT, SeverityT, SharedT, - SimdlenT, SizesT, PermutationT, - ThreadLimitT, UniformT, UpdateT, - UseDeviceAddrT, UseDevicePtrT, UsesAllocatorsT>; + ProcBindT, ReverseOffloadT, SafelenT, + SelfMapsT, SeverityT, SharedT, SimdlenT, + SizesT, PermutationT, ThreadLimitT, + UnifiedAddressT, UnifiedSharedMemoryT, UniformT, + UpdateT, UseDeviceAddrT, UseDevicePtrT, + UsesAllocatorsT>; template using UnionOfAllClausesT = typename type::Union< // diff --git a/llvm/include/llvm/Frontend/OpenMP/OMP.td b/llvm/include/llvm/Frontend/OpenMP/OMP.td index 86a9e249054a0..edcf7a92c2a84 100644 --- a/llvm/include/llvm/Frontend/OpenMP/OMP.td +++ b/llvm/include/llvm/Frontend/OpenMP/OMP.td @@ -177,6 +177,8 @@ def OMPC_Doacross : Clause<[Spelling<"doacross">]> { } def OMPC_DynamicAllocators : Clause<[Spelling<"dynamic_allocators">]> { let clangClass = "OMPDynamicAllocatorsClause"; + let flangClass = "OmpDynamicAllocatorsClause"; + let isValueOptional = true; } def OMPC_DynGroupprivate : Clause<[Spelling<"dyn_groupprivate">]> { let flangClass = "OmpDynGroupprivateClause"; @@ -467,6 +469,8 @@ def OMPC_Replayable : Clause<[Spelling<"replayable">]> { } def OMPC_ReverseOffload : Clause<[Spelling<"reverse_offload">]> { let clangClass = "OMPReverseOffloadClause"; + let flangClass = "OmpReverseOffloadClause"; + let isValueOptional = true; } def OMPC_SafeLen : Clause<[Spelling<"safelen">]> { let clangClass = "OMPSafelenClause"; @@ -541,12 +545,18 @@ def OMPC_Transparent : Clause<[Spelling<"transparent">]> { } def OMPC_UnifiedAddress : Clause<[Spelling<"unified_address">]> { let clangClass = "OMPUnifiedAddressClause"; + let flangClass = "OmpUnifiedAddressClause"; + let isValueOptional = true; } def OMPC_UnifiedSharedMemory : Clause<[Spelling<"unified_shared_memory">]> { let clangClass = "OMPUnifiedSharedMemoryClause"; + let flangClass = "OmpUnifiedSharedMemoryClause"; + let isValueOptional = true; } def OMPC_SelfMaps : Clause<[Spelling<"self_maps">]> { let clangClass = "OMPSelfMapsClause"; + let flangClass = "OmpSelfMapsClause"; + let isValueOptional = true; } def OMPC_Uniform : Clause<[Spelling<"uniform">]> { let flangClass = "Name"; From 404527f36421be7ac2bc12c4f55b6aa53a51f890 Mon Sep 17 00:00:00 2001 From: Krzysztof Parzyszek Date: Sat, 11 Oct 2025 15:25:15 -0500 Subject: [PATCH 07/11] [flang][OpenMP] Frontend support for DEVICE_SAFESYNC Add parsing and semantic checks for DEVICE_SAFESYNC clause. No lowering. --- flang/include/flang/Lower/OpenMP/Clauses.h | 1 + flang/include/flang/Parser/dump-parse-tree.h | 1 + flang/include/flang/Parser/parse-tree.h | 8 +++++++ flang/lib/Lower/OpenMP/Clauses.cpp | 12 ++++++++++ flang/lib/Parser/openmp-parsers.cpp | 3 +++ flang/lib/Semantics/check-omp-structure.cpp | 5 ++++ flang/lib/Semantics/resolve-directives.cpp | 1 + llvm/include/llvm/Frontend/OpenMP/ClauseT.h | 25 +++++++++++++------- llvm/include/llvm/Frontend/OpenMP/OMP.td | 4 ++++ 9 files changed, 52 insertions(+), 8 deletions(-) diff --git a/flang/include/flang/Lower/OpenMP/Clauses.h b/flang/include/flang/Lower/OpenMP/Clauses.h index 273e61166fd9d..74924661d9a03 100644 --- a/flang/include/flang/Lower/OpenMP/Clauses.h +++ b/flang/include/flang/Lower/OpenMP/Clauses.h @@ -214,6 +214,7 @@ using Depend = tomp::clause::DependT; using Destroy = tomp::clause::DestroyT; using Detach = tomp::clause::DetachT; using Device = tomp::clause::DeviceT; +using DeviceSafesync = tomp::clause::DeviceSafesyncT; using DeviceType = tomp::clause::DeviceTypeT; using DistSchedule = tomp::clause::DistScheduleT; using Doacross = tomp::clause::DoacrossT; diff --git a/flang/include/flang/Parser/dump-parse-tree.h b/flang/include/flang/Parser/dump-parse-tree.h index 8854dc2267c8e..7377a3a4e3ca1 100644 --- a/flang/include/flang/Parser/dump-parse-tree.h +++ b/flang/include/flang/Parser/dump-parse-tree.h @@ -557,6 +557,7 @@ class ParseTreeDumper { NODE(OmpDeviceClause, Modifier) NODE(parser, OmpDeviceModifier) NODE_ENUM(OmpDeviceModifier, Value) + NODE(parser, OmpDeviceSafesyncClause) NODE(parser, OmpDeviceTypeClause) NODE_ENUM(OmpDeviceTypeClause, DeviceTypeDescription) NODE(parser, OmpDirectiveName) diff --git a/flang/include/flang/Parser/parse-tree.h b/flang/include/flang/Parser/parse-tree.h index 18b583669421a..079c0d642c32b 100644 --- a/flang/include/flang/Parser/parse-tree.h +++ b/flang/include/flang/Parser/parse-tree.h @@ -4406,6 +4406,14 @@ struct OmpDeviceClause { std::tuple t; }; +// Ref: [6.0:356-362] +// +// device-safesync-clause -> +// DEVICE_SAFESYNC [(scalar-logical-const-expr)] // since 6.0 +struct OmpDeviceSafesyncClause { + WRAPPER_CLASS_BOILERPLATE(OmpDeviceSafesyncClause, ScalarLogicalConstantExpr); +}; + // Ref: [5.0:180-185], [5.1:210-216], [5.2:275] // // device-type-clause -> diff --git a/flang/lib/Lower/OpenMP/Clauses.cpp b/flang/lib/Lower/OpenMP/Clauses.cpp index 60e569efa964a..c11a3dda7dc41 100644 --- a/flang/lib/Lower/OpenMP/Clauses.cpp +++ b/flang/lib/Lower/OpenMP/Clauses.cpp @@ -740,6 +740,18 @@ Device make(const parser::OmpClause::Device &inp, /*DeviceDescription=*/makeExpr(t1, semaCtx)}}; } +DeviceSafesync make(const parser::OmpClause::DeviceSafesync &inp, + semantics::SemanticsContext &semaCtx) { + // inp.v -> std::optional + auto &&maybeRequired = maybeApply( + [&](const parser::OmpDeviceSafesyncClause &c) { + return makeExpr(c.v, semaCtx); + }, + inp.v); + + return DeviceSafesync{/*Required=*/std::move(maybeRequired)}; +} + DeviceType make(const parser::OmpClause::DeviceType &inp, semantics::SemanticsContext &semaCtx) { // inp.v -> parser::OmpDeviceTypeClause diff --git a/flang/lib/Parser/openmp-parsers.cpp b/flang/lib/Parser/openmp-parsers.cpp index 0a70930a0db33..63216384a6582 100644 --- a/flang/lib/Parser/openmp-parsers.cpp +++ b/flang/lib/Parser/openmp-parsers.cpp @@ -1150,6 +1150,9 @@ TYPE_PARSER( // construct(Parser{}))))) || "DEVICE" >> construct(construct( parenthesized(Parser{}))) || + "DEVICE_SAFESYNC" >> + construct(construct( + maybe(parenthesized(scalarLogicalConstantExpr)))) || "DEVICE_TYPE" >> construct(construct( parenthesized(Parser{}))) || "DIST_SCHEDULE" >> diff --git a/flang/lib/Semantics/check-omp-structure.cpp b/flang/lib/Semantics/check-omp-structure.cpp index 1c27df18fba20..7b7dfb87763a5 100644 --- a/flang/lib/Semantics/check-omp-structure.cpp +++ b/flang/lib/Semantics/check-omp-structure.cpp @@ -1535,6 +1535,7 @@ void OmpStructureChecker::Enter(const parser::OpenMPRequiresConstruct &x) { [&](auto &&s) { using TypeS = llvm::remove_cvref_t; if constexpr ( // + std::is_same_v || std::is_same_v || std::is_same_v || std::is_same_v || @@ -5155,6 +5156,10 @@ void OmpStructureChecker::Enter( CheckAllowedRequiresClause(llvm::omp::Clause::OMPC_atomic_default_mem_order); } +void OmpStructureChecker::Enter(const parser::OmpClause::DeviceSafesync &x) { + CheckAllowedRequiresClause(llvm::omp::Clause::OMPC_device_safesync); +} + void OmpStructureChecker::Enter(const parser::OmpClause::DynamicAllocators &x) { CheckAllowedRequiresClause(llvm::omp::Clause::OMPC_dynamic_allocators); } diff --git a/flang/lib/Semantics/resolve-directives.cpp b/flang/lib/Semantics/resolve-directives.cpp index 7067ed3d99286..db061bdce18ea 100644 --- a/flang/lib/Semantics/resolve-directives.cpp +++ b/flang/lib/Semantics/resolve-directives.cpp @@ -586,6 +586,7 @@ class OmpAttributeVisitor : DirectiveAttributeVisitor { [&](auto &&s) { using TypeS = llvm::remove_cvref_t; if constexpr ( // + std::is_same_v || std::is_same_v || std::is_same_v || std::is_same_v || diff --git a/llvm/include/llvm/Frontend/OpenMP/ClauseT.h b/llvm/include/llvm/Frontend/OpenMP/ClauseT.h index 9153dd1c56b7f..09cbb7f9fcf4e 100644 --- a/llvm/include/llvm/Frontend/OpenMP/ClauseT.h +++ b/llvm/include/llvm/Frontend/OpenMP/ClauseT.h @@ -541,6 +541,14 @@ struct DeviceT { std::tuple t; }; +// [6.0:362] +template // +struct DeviceSafesyncT { + using Requires = E; + using WrapperTrait = std::true_type; + OPT(Requires) v; +}; + // V5.2: [13.1] `device_type` clause template // struct DeviceTypeT { @@ -1331,14 +1339,15 @@ using WrapperClausesT = std::variant< AtomicDefaultMemOrderT, AtT, BindT, CollapseT, ContainsT, CopyinT, CopyprivateT, DefaultT, DestroyT, - DetachT, DeviceTypeT, DynamicAllocatorsT, - EnterT, ExclusiveT, FailT, FilterT, - FinalT, FirstprivateT, HasDeviceAddrT, - HintT, HoldsT, InclusiveT, IndirectT, - InitializerT, IsDevicePtrT, LinkT, - MessageT, NocontextT, NontemporalT, - NovariantsT, NumTeamsT, NumThreadsT, - OrderedT, PartialT, PriorityT, PrivateT, + DetachT, DeviceSafesyncT, DeviceTypeT, + DynamicAllocatorsT, EnterT, ExclusiveT, + FailT, FilterT, FinalT, FirstprivateT, + HasDeviceAddrT, HintT, HoldsT, + InclusiveT, IndirectT, InitializerT, + IsDevicePtrT, LinkT, MessageT, + NocontextT, NontemporalT, NovariantsT, + NumTeamsT, NumThreadsT, OrderedT, + PartialT, PriorityT, PrivateT, ProcBindT, ReverseOffloadT, SafelenT, SelfMapsT, SeverityT, SharedT, SimdlenT, SizesT, PermutationT, ThreadLimitT, diff --git a/llvm/include/llvm/Frontend/OpenMP/OMP.td b/llvm/include/llvm/Frontend/OpenMP/OMP.td index edcf7a92c2a84..00c43d084b216 100644 --- a/llvm/include/llvm/Frontend/OpenMP/OMP.td +++ b/llvm/include/llvm/Frontend/OpenMP/OMP.td @@ -163,6 +163,10 @@ def OMPC_Device : Clause<[Spelling<"device">]> { let clangClass = "OMPDeviceClause"; let flangClass = "OmpDeviceClause"; } +def OMPC_DeviceSafesync : Clause<[Spelling<"device_safesync">]> { + let flangClass = "OmpDeviceSafesyncClause"; + let isValueOptional = true; +} def OMPC_DeviceType : Clause<[Spelling<"device_type">]> { let flangClass = "OmpDeviceTypeClause"; } From 44e964fca47957dbd4a8b5c100c9065e591d2b87 Mon Sep 17 00:00:00 2001 From: Krzysztof Parzyszek Date: Thu, 16 Oct 2025 10:57:50 -0500 Subject: [PATCH 08/11] Add tests for false argument, and expression folding --- .../test/Semantics/OpenMP/requires-modfile.f90 | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/flang/test/Semantics/OpenMP/requires-modfile.f90 b/flang/test/Semantics/OpenMP/requires-modfile.f90 index 2f06104e208ef..8f4a3ac471018 100644 --- a/flang/test/Semantics/OpenMP/requires-modfile.f90 +++ b/flang/test/Semantics/OpenMP/requires-modfile.f90 @@ -1,4 +1,4 @@ -!RUN: %python %S/../test_modfile.py %s %flang_fc1 -fopenmp -fopenmp-version=52 +!RUN: %python %S/../test_modfile.py %s %flang_fc1 -fopenmp -fopenmp-version=61 module req contains @@ -16,8 +16,16 @@ subroutine f01 module user ! The requirements from module req should be propagated to this module. use req + ! This has no effect, and should not be emitted. + !$omp requires unified_shared_memory(.false.) end module +module fold + integer, parameter :: x = 10 + integer, parameter :: y = 33 + ! Make sure we can fold this expression to "true". + !$omp requires dynamic_allocators(x < y) +end module !Expect: req.mod !module req @@ -37,3 +45,10 @@ module user !!$omp requires atomic_default_mem_order(seq_cst) !!$omp requires reverse_offload !end + +!Expect: fold.mod +!module fold +!integer(4),parameter::x=10_4 +!integer(4),parameter::y=33_4 +!!$omp requires dynamic_allocators +!end From 010814a2c88e6885d4b84ee215286c33a3319820 Mon Sep 17 00:00:00 2001 From: Krzysztof Parzyszek Date: Thu, 16 Oct 2025 11:01:00 -0500 Subject: [PATCH 09/11] Use version=60 instead of 61 --- flang/test/Semantics/OpenMP/requires-modfile.f90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flang/test/Semantics/OpenMP/requires-modfile.f90 b/flang/test/Semantics/OpenMP/requires-modfile.f90 index 8f4a3ac471018..52a43c2ef37ac 100644 --- a/flang/test/Semantics/OpenMP/requires-modfile.f90 +++ b/flang/test/Semantics/OpenMP/requires-modfile.f90 @@ -1,4 +1,4 @@ -!RUN: %python %S/../test_modfile.py %s %flang_fc1 -fopenmp -fopenmp-version=61 +!RUN: %python %S/../test_modfile.py %s %flang_fc1 -fopenmp -fopenmp-version=60 module req contains From a62b3282ef80dae789fc7852a16de9632bad6705 Mon Sep 17 00:00:00 2001 From: Krzysztof Parzyszek Date: Thu, 16 Oct 2025 11:31:49 -0500 Subject: [PATCH 10/11] bug fix + parser test --- flang/test/Parser/OpenMP/requires.f90 | 13 +++++++++++-- llvm/include/llvm/Frontend/OpenMP/OMP.td | 7 ++++--- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/flang/test/Parser/OpenMP/requires.f90 b/flang/test/Parser/OpenMP/requires.f90 index 8169403835705..ab4f4371480f7 100644 --- a/flang/test/Parser/OpenMP/requires.f90 +++ b/flang/test/Parser/OpenMP/requires.f90 @@ -1,5 +1,5 @@ -!RUN: %flang_fc1 -fdebug-unparse -fopenmp -fopenmp-version=50 %s | FileCheck --ignore-case --check-prefix="UNPARSE" %s -!RUN: %flang_fc1 -fdebug-dump-parse-tree -fopenmp -fopenmp-version=50 %s | FileCheck --check-prefix="PARSE-TREE" %s +!RUN: %flang_fc1 -fdebug-unparse -fopenmp -fopenmp-version=60 %s | FileCheck --ignore-case --check-prefix="UNPARSE" %s +!RUN: %flang_fc1 -fdebug-dump-parse-tree -fopenmp -fopenmp-version=60 %s | FileCheck --check-prefix="PARSE-TREE" %s !$omp requires atomic_default_mem_order(seq_cst) @@ -44,4 +44,13 @@ !PARSE-TREE: | | | bool = 'false' !PARSE-TREE: | Flags = None +!$omp requires device_safesync + +!UNPARSE: !$OMP REQUIRES DEVICE_SAFESYNC + +!PARSE-TREE: OpenMPDeclarativeConstruct -> OpenMPRequiresConstruct -> OmpDirectiveSpecification +!PARSE-TREE: | OmpDirectiveName -> llvm::omp::Directive = requires +!PARSE-TREE: | OmpClauseList -> OmpClause -> DeviceSafesync +!PARSE-TREE: | Flags = None + end diff --git a/llvm/include/llvm/Frontend/OpenMP/OMP.td b/llvm/include/llvm/Frontend/OpenMP/OMP.td index 00c43d084b216..0aa674f28dc39 100644 --- a/llvm/include/llvm/Frontend/OpenMP/OMP.td +++ b/llvm/include/llvm/Frontend/OpenMP/OMP.td @@ -1022,16 +1022,17 @@ def OMP_Requires : Directive<[Spelling<"requires">]> { let allowedOnceClauses = [ VersionedClause, VersionedClause, + VersionedClause, + VersionedClause, + VersionedClause, VersionedClause, // OpenMP 5.2 Spec: If an implementation is not supporting a requirement // (reverse offload in this case) then it should give compile-time error // termination. - // Seeting supported version for reverse_offload to a distant future version + // Setting supported version for reverse_offload to a distant future version // 9.9 so that its partial support can be tested in the meantime. // // TODO: Correct this supprted version number whenever complete // implementation of reverse_offload is available. - VersionedClause, - VersionedClause, VersionedClause, VersionedClause, ]; let association = AS_None; From 4c147701a1c8cc27fc855836e8030455a34c1163 Mon Sep 17 00:00:00 2001 From: Krzysztof Parzyszek Date: Fri, 17 Oct 2025 08:53:11 -0500 Subject: [PATCH 11/11] undo unrelated code movement --- llvm/include/llvm/Frontend/OpenMP/OMP.td | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/llvm/include/llvm/Frontend/OpenMP/OMP.td b/llvm/include/llvm/Frontend/OpenMP/OMP.td index 0aa674f28dc39..61a1a05f6e904 100644 --- a/llvm/include/llvm/Frontend/OpenMP/OMP.td +++ b/llvm/include/llvm/Frontend/OpenMP/OMP.td @@ -1022,17 +1022,17 @@ def OMP_Requires : Directive<[Spelling<"requires">]> { let allowedOnceClauses = [ VersionedClause, VersionedClause, - VersionedClause, VersionedClause, - VersionedClause, VersionedClause, // OpenMP 5.2 Spec: If an implementation is not supporting a requirement // (reverse offload in this case) then it should give compile-time error // termination. - // Setting supported version for reverse_offload to a distant future version + // Seeting supported version for reverse_offload to a distant future version // 9.9 so that its partial support can be tested in the meantime. // // TODO: Correct this supprted version number whenever complete // implementation of reverse_offload is available. + VersionedClause, + VersionedClause, VersionedClause, VersionedClause, ]; let association = AS_None;