[flang-commits] [flang] [flang][OpenMP] Support lowering of metadirective (part 1) (PR #193664)
via flang-commits
flang-commits at lists.llvm.org
Mon Apr 27 00:24:12 PDT 2026
https://github.com/chichunchen updated https://github.com/llvm/llvm-project/pull/193664
>From 21e253c862b1eeb66a01e2a619fcdd63ff37e308 Mon Sep 17 00:00:00 2001
From: "Chi Chun, Chen" <chichun.chen at hpe.com>
Date: Mon, 27 Apr 2026 01:33:53 -0500
Subject: [PATCH] [flang][OpenMP] Support lowering of metadirective (part 1)
This patch implements following feature in metadirective:
- implementation={vendor(...)}
- device={kind(...), isa(...), arch(...)}
- user={condition(<constant-expr>)}
- construct={parallel, target, teams}
- default, nothing, and otherwise clause
Dynamic user conditions, target_device, and loop-associated
variants are deferred to follow-up patches.
This patch is part of the feature work for #188820.
Assisted with copilot and GPT-5.4
---
flang/lib/Lower/OpenMP/OpenMP.cpp | 187 ++++++++++++++-
flang/lib/Lower/OpenMP/Utils.cpp | 210 +++++++++++++++++
flang/lib/Lower/OpenMP/Utils.h | 14 ++
.../OpenMP/Todo/metadirective-dynamic.f90 | 10 +
.../Lower/OpenMP/Todo/metadirective-exec.f90 | 9 -
.../Lower/OpenMP/Todo/metadirective-loop.f90 | 12 +
.../Lower/OpenMP/Todo/metadirective-spec.f90 | 9 -
.../Todo/metadirective-target-device.f90 | 10 +
.../Lower/OpenMP/metadirective-construct.f90 | 30 +++
.../Lower/OpenMP/metadirective-device-isa.f90 | 213 ++++++++++++++++++
.../OpenMP/metadirective-device-kind.f90 | 21 ++
.../OpenMP/metadirective-implementation.f90 | 121 ++++++++++
.../test/Lower/OpenMP/metadirective-user.f90 | 33 +++
13 files changed, 860 insertions(+), 19 deletions(-)
create mode 100644 flang/test/Lower/OpenMP/Todo/metadirective-dynamic.f90
delete mode 100644 flang/test/Lower/OpenMP/Todo/metadirective-exec.f90
create mode 100644 flang/test/Lower/OpenMP/Todo/metadirective-loop.f90
delete mode 100644 flang/test/Lower/OpenMP/Todo/metadirective-spec.f90
create mode 100644 flang/test/Lower/OpenMP/Todo/metadirective-target-device.f90
create mode 100644 flang/test/Lower/OpenMP/metadirective-construct.f90
create mode 100644 flang/test/Lower/OpenMP/metadirective-device-isa.f90
create mode 100644 flang/test/Lower/OpenMP/metadirective-device-kind.f90
create mode 100644 flang/test/Lower/OpenMP/metadirective-implementation.f90
create mode 100644 flang/test/Lower/OpenMP/metadirective-user.f90
diff --git a/flang/lib/Lower/OpenMP/OpenMP.cpp b/flang/lib/Lower/OpenMP/OpenMP.cpp
index bfdf77340c78e..0ac0f0071ec3b 100644
--- a/flang/lib/Lower/OpenMP/OpenMP.cpp
+++ b/flang/lib/Lower/OpenMP/OpenMP.cpp
@@ -4364,11 +4364,196 @@ static void genOMP(lower::AbstractConverter &converter, lower::SymMap &symTable,
// support the case of threadprivate variable declared in module.
}
+namespace {
+struct TargetOMPContext final : public llvm::omp::OMPContext {
+ TargetOMPContext(mlir::ModuleOp module,
+ llvm::ArrayRef<llvm::omp::TraitProperty> constructTraits)
+ // DeviceNum is set to -1 (unknown) because the
+ // target_device={device_num()} selector is not yet supported. OMPContext
+ // uses DeviceNum > -1 to activate target_device traits from the offload
+ // triple; without a concrete device number those traits are left
+ // inactive.
+ : OMPContext(isDeviceCompilation(module), fir::getTargetTriple(module),
+ getOffloadTargetTriple(module),
+ /*DeviceNum=*/-1),
+ targetFeatures(fir::getTargetFeatures(module)) {
+ for (llvm::omp::TraitProperty trait : constructTraits)
+ addTrait(trait);
+ }
+
+ bool matchesISATrait(llvm::StringRef rawString) const override {
+ if (!targetFeatures || targetFeatures.nullOrEmpty())
+ return false;
+ return targetFeatures.contains(("+" + rawString).str());
+ }
+
+private:
+ static bool isDeviceCompilation(mlir::ModuleOp module) {
+ return llvm::cast<mlir::omp::OffloadModuleInterface>(*module.getOperation())
+ .getIsTargetDevice();
+ }
+
+ static llvm::Triple getOffloadTargetTriple(mlir::ModuleOp module) {
+ auto offloadMod =
+ llvm::cast<mlir::omp::OffloadModuleInterface>(*module.getOperation());
+ auto targetTriples = offloadMod.getTargetTriples();
+
+ if (!targetTriples.empty())
+ if (auto tripleAttr =
+ llvm::dyn_cast<mlir::StringAttr>(targetTriples.front()))
+ return llvm::Triple(tripleAttr.getValue());
+
+ return llvm::Triple();
+ }
+
+ mlir::LLVM::TargetFeaturesAttr targetFeatures;
+};
+} // namespace
+
static void genOMP(lower::AbstractConverter &converter, lower::SymMap &symTable,
semantics::SemanticsContext &semaCtx,
lower::pft::Evaluation &eval,
const parser::OmpMetadirectiveDirective &meta) {
- TODO(converter.getCurrentLocation(), "METADIRECTIVE");
+ const parser::OmpClauseList &clauseList = meta.v.Clauses();
+ fir::FirOpBuilder &builder = converter.getFirOpBuilder();
+
+ llvm::SmallVector<llvm::omp::TraitProperty, 8> constructTraits;
+ for (auto *parentEval = eval.parentConstruct; parentEval;
+ parentEval = parentEval->parentConstruct) {
+ const auto *ompConstruct = parentEval->getIf<parser::OpenMPConstruct>();
+ if (!ompConstruct)
+ continue;
+ llvm::omp::Directive dir =
+ parser::omp::GetOmpDirectiveName(*ompConstruct).v;
+ // Record compound directives inside-out so the final reverse yields the
+ // outermost-to-innermost order.
+ if (llvm::omp::allSimdSet.test(dir))
+ constructTraits.push_back(llvm::omp::TraitProperty::construct_simd_simd);
+ if (llvm::omp::allDoSet.test(dir))
+ constructTraits.push_back(llvm::omp::TraitProperty::construct_for_for);
+ if (llvm::omp::allParallelSet.test(dir))
+ constructTraits.push_back(
+ llvm::omp::TraitProperty::construct_parallel_parallel);
+ if (llvm::omp::allTeamsSet.test(dir))
+ constructTraits.push_back(
+ llvm::omp::TraitProperty::construct_teams_teams);
+ if (llvm::omp::allTargetSet.test(dir))
+ constructTraits.push_back(
+ llvm::omp::TraitProperty::construct_target_target);
+ }
+ std::reverse(constructTraits.begin(), constructTraits.end());
+ TargetOMPContext ompCtx(builder.getModule(), constructTraits);
+
+ llvm::SmallVector<const parser::OmpDirectiveSpecification *> candidates;
+ llvm::SmallVector<llvm::omp::VariantMatchInfo, 4> vmis;
+ // A null directive specification represents either the implicit `nothing`
+ // variant or the absence of an explicit otherwise/default clause.
+ const parser::OmpDirectiveSpecification *fallback = nullptr;
+
+ auto getContextSelector = [](const parser::OmpClause::When &whenClause)
+ -> const parser::modifier::OmpContextSelector * {
+ const auto &modifiers = std::get<0>(whenClause.v.t);
+ if (!modifiers)
+ return nullptr;
+ for (const auto &mod : *modifiers) {
+ if (const auto *ctxSel =
+ std::get_if<parser::modifier::OmpContextSelector>(&mod.u))
+ return ctxSel;
+ }
+ return nullptr;
+ };
+
+ auto getDirectiveVariant = [](const parser::OmpClause::When &whenClause)
+ -> const parser::OmpDirectiveSpecification * {
+ const auto &opt = std::get<1>(whenClause.v.t);
+ if (!opt || opt->value().DirId() == llvm::omp::Directive::OMPD_nothing)
+ return nullptr;
+ return &opt->value();
+ };
+
+ // Return the directive spec pointer, or nullptr for "nothing".
+ auto getFallbackVariant = [](const parser::OmpDirectiveSpecification &spec)
+ -> const parser::OmpDirectiveSpecification * {
+ if (spec.DirId() == llvm::omp::Directive::OMPD_nothing)
+ return nullptr;
+ return &spec;
+ };
+
+ for (const auto &clause : clauseList.v) {
+ if (const auto *whenClause =
+ std::get_if<parser::OmpClause::When>(&clause.u)) {
+ const auto *ctxSel = getContextSelector(*whenClause);
+ const auto *variant = getDirectiveVariant(*whenClause);
+
+ // Always match when there is no context selector.
+ if (!ctxSel) {
+ candidates.push_back(variant);
+ vmis.emplace_back();
+ continue;
+ }
+
+ llvm::omp::VariantMatchInfo vmi;
+ const parser::ScalarExpr *dynCondExpr = nullptr;
+ makeVariantMatchInfo(vmi, *ctxSel, semaCtx,
+ converter.genLocation(clause.source), dynCondExpr);
+
+ if (dynCondExpr)
+ TODO(converter.genLocation(clause.source),
+ "dynamic user condition in METADIRECTIVE");
+
+ if (!llvm::omp::isVariantApplicableInContext(vmi, ompCtx))
+ continue;
+
+ candidates.push_back(variant);
+ vmis.push_back(vmi);
+ } else if (const auto *otherwiseClause =
+ std::get_if<parser::OmpClause::Otherwise>(&clause.u)) {
+ if (otherwiseClause->v && otherwiseClause->v->v)
+ fallback = getFallbackVariant(otherwiseClause->v->v->value());
+ } else if (const auto *defaultClause =
+ std::get_if<parser::OmpClause::Default>(&clause.u)) {
+ if (const auto *dirSpecPtr = std::get_if<
+ common::Indirection<parser::OmpDirectiveSpecification>>(
+ &defaultClause->v.u))
+ fallback = getFallbackVariant(dirSpecPtr->value());
+ }
+ }
+
+ // Lower a single resolved candidate.
+ auto genVariant = [&](const parser::OmpDirectiveSpecification *spec) {
+ if (!spec) {
+ genNestedEvaluations(converter, eval);
+ return;
+ }
+ List<Clause> variantClauses = makeClauses(spec->Clauses(), semaCtx);
+ mlir::Location variantLoc = converter.genLocation(spec->source);
+ ConstructQueue queue{
+ buildConstructQueue(converter.getFirOpBuilder().getModule(), semaCtx,
+ eval, spec->source, spec->DirId(), variantClauses)};
+
+ if (llvm::any_of(queue, [](const auto &item) {
+ return llvm::omp::getDirectiveAssociation(item.id) ==
+ llvm::omp::Association::LoopNest;
+ })) {
+ TODO(variantLoc, "loop-associated METADIRECTIVE variant");
+ }
+
+ genOMPDispatch(converter, symTable, semaCtx, eval, variantLoc, queue,
+ queue.begin());
+ };
+
+ const parser::OmpDirectiveSpecification *selected = fallback;
+ if (candidates.size() == 1) {
+ selected = candidates.front();
+ } else if (!candidates.empty()) {
+ int bestIdx = llvm::omp::getBestVariantMatchForContext(vmis, ompCtx);
+ if (bestIdx >= 0) {
+ assert(static_cast<size_t>(bestIdx) < candidates.size() &&
+ "best variant index out of range");
+ selected = candidates[bestIdx];
+ }
+ }
+ genVariant(selected);
}
static void genOMP(lower::AbstractConverter &converter, lower::SymMap &symTable,
diff --git a/flang/lib/Lower/OpenMP/Utils.cpp b/flang/lib/Lower/OpenMP/Utils.cpp
index b7d1cb8b419fd..e10b2b581d27c 100644
--- a/flang/lib/Lower/OpenMP/Utils.cpp
+++ b/flang/lib/Lower/OpenMP/Utils.cpp
@@ -1117,6 +1117,216 @@ mlir::Value genIteratorCoordinate(Fortran::lower::AbstractConverter &converter,
/*typeparams=*/mlir::ValueRange{});
}
+llvm::omp::TraitSet mapTraitSet(parser::OmpTraitSetSelectorName::Value name) {
+ switch (name) {
+ case parser::OmpTraitSetSelectorName::Value::Construct:
+ return llvm::omp::TraitSet::construct;
+ case parser::OmpTraitSetSelectorName::Value::Device:
+ return llvm::omp::TraitSet::device;
+ case parser::OmpTraitSetSelectorName::Value::Implementation:
+ return llvm::omp::TraitSet::implementation;
+ case parser::OmpTraitSetSelectorName::Value::User:
+ return llvm::omp::TraitSet::user;
+ case parser::OmpTraitSetSelectorName::Value::Target_Device:
+ return llvm::omp::TraitSet::target_device;
+ }
+ llvm_unreachable("unknown trait set");
+}
+
+llvm::omp::TraitSelector
+mapTraitSelector(const parser::OmpTraitSelectorName &name,
+ llvm::omp::TraitSet set) {
+ if (const auto *val =
+ std::get_if<parser::OmpTraitSelectorName::Value>(&name.u)) {
+ switch (*val) {
+ case parser::OmpTraitSelectorName::Value::Kind:
+ if (set == llvm::omp::TraitSet::target_device)
+ return llvm::omp::TraitSelector::target_device_kind;
+ return llvm::omp::TraitSelector::device_kind;
+ case parser::OmpTraitSelectorName::Value::Arch:
+ if (set == llvm::omp::TraitSet::target_device)
+ return llvm::omp::TraitSelector::target_device_arch;
+ return llvm::omp::TraitSelector::device_arch;
+ case parser::OmpTraitSelectorName::Value::Isa:
+ if (set == llvm::omp::TraitSet::target_device)
+ return llvm::omp::TraitSelector::target_device_isa;
+ return llvm::omp::TraitSelector::device_isa;
+ case parser::OmpTraitSelectorName::Value::Vendor:
+ return llvm::omp::TraitSelector::implementation_vendor;
+ case parser::OmpTraitSelectorName::Value::Extension:
+ return llvm::omp::TraitSelector::implementation_extension;
+ case parser::OmpTraitSelectorName::Value::Condition:
+ return llvm::omp::TraitSelector::user_condition;
+ case parser::OmpTraitSelectorName::Value::Atomic_Default_Mem_Order:
+ case parser::OmpTraitSelectorName::Value::Requires:
+ case parser::OmpTraitSelectorName::Value::Simd:
+ case parser::OmpTraitSelectorName::Value::Device_Num:
+ case parser::OmpTraitSelectorName::Value::Uid:
+ break;
+ }
+ }
+ // Construct traits, extension strings, and remaining selectors use
+ // string-based lookup.
+ return llvm::omp::getOpenMPContextTraitSelectorKind(name.ToString(), set);
+}
+
+/// Collect trait property names (vendor, kind, arch, isa, etc.) into a VMI.
+static void processTraitProperties(
+ llvm::omp::VariantMatchInfo &vmi, llvm::omp::TraitSet set,
+ llvm::omp::TraitSelector selector,
+ const std::optional<parser::OmpTraitSelector::Properties> &props,
+ llvm::APInt *scorePtr) {
+ if (!props)
+ return;
+
+ for (const auto &prop :
+ std::get<std::list<parser::OmpTraitProperty>>(props->t)) {
+ const auto *name = std::get_if<parser::OmpTraitPropertyName>(&prop.u);
+ if (!name)
+ continue;
+
+ llvm::omp::TraitProperty propKind =
+ llvm::omp::getOpenMPContextTraitPropertyKind(set, selector, name->v);
+ if (propKind != llvm::omp::TraitProperty::invalid) {
+ vmi.addTrait(set, propKind, name->v, scorePtr);
+ continue;
+ }
+
+ // Treat unknown properties as ISA-like raw strings.
+ vmi.addTrait(set, llvm::omp::TraitProperty::device_isa___ANY, name->v,
+ scorePtr);
+ }
+}
+
+/// Try to constant-fold a user condition expression to a boolean.
+static std::optional<bool>
+evaluateUserCondition(semantics::SemanticsContext &semaCtx,
+ const parser::ScalarExpr &scalarExpr) {
+ const auto *typedExpr = semantics::GetExpr(semaCtx, scalarExpr);
+ if (!typedExpr)
+ return std::nullopt;
+
+ auto foldedExpr = Fortran::evaluate::Fold(semaCtx.foldingContext(),
+ Fortran::common::Clone(*typedExpr));
+ if (auto constVal = Fortran::evaluate::ToInt64(foldedExpr))
+ return *constVal != 0;
+
+ if (auto logicalVal = Fortran::evaluate::GetScalarConstantValue<
+ Fortran::evaluate::LogicalResult>(foldedExpr))
+ return logicalVal->IsTrue();
+
+ return std::nullopt;
+}
+
+/// Process user={condition(...)} trait properties. Constant conditions are
+/// resolved to user_condition_true/false. Non-constant conditions are marked
+/// as user_condition_unknown and the expression pointer is returned via
+/// \p dynamicCondExpr.
+static void processUserConditionTrait(
+ llvm::omp::VariantMatchInfo &vmi,
+ const std::optional<parser::OmpTraitSelector::Properties> &props,
+ semantics::SemanticsContext &semaCtx,
+ const parser::ScalarExpr *&dynamicCondExpr) {
+ if (!props)
+ return;
+
+ for (const auto &prop :
+ std::get<std::list<parser::OmpTraitProperty>>(props->t)) {
+ const auto *scalarExpr = std::get_if<parser::ScalarExpr>(&prop.u);
+ if (!scalarExpr)
+ continue;
+
+ if (auto constValue = evaluateUserCondition(semaCtx, *scalarExpr)) {
+ vmi.addTrait(*constValue ? llvm::omp::TraitProperty::user_condition_true
+ : llvm::omp::TraitProperty::user_condition_false,
+ "<condition>");
+ continue;
+ }
+
+ dynamicCondExpr = scalarExpr;
+ vmi.addTrait(llvm::omp::TraitProperty::user_condition_unknown,
+ "<condition>");
+ }
+}
+
+/// Extract the optional score value from trait properties.
+static llvm::APInt *
+getTraitScore(const std::optional<parser::OmpTraitSelector::Properties> &props,
+ semantics::SemanticsContext &semaCtx,
+ std::optional<llvm::APInt> &scoreStorage) {
+ if (!props)
+ return nullptr;
+
+ const auto &optScore =
+ std::get<std::optional<parser::OmpTraitScore>>(props->t);
+ if (!optScore)
+ return nullptr;
+
+ const auto *typedExpr = semantics::GetExpr(semaCtx, optScore->v);
+ if (!typedExpr)
+ return nullptr;
+
+ auto constVal = Fortran::evaluate::ToInt64(*typedExpr);
+ if (!constVal)
+ return nullptr;
+
+ scoreStorage = llvm::APInt(64, *constVal);
+ return &*scoreStorage;
+}
+
+/// Populate a VariantMatchInfo from context selector.
+/// For user conditions, attempts constant folding. Non-constant conditions
+/// are recorded as user_condition_unknown and the expression pointer is
+/// returned via \p dynamicCondExpr for later use in fir.if lowering.
+void makeVariantMatchInfo(llvm::omp::VariantMatchInfo &vmi,
+ const parser::modifier::OmpContextSelector &ctxSel,
+ semantics::SemanticsContext &semaCtx,
+ mlir::Location loc,
+ const parser::ScalarExpr *&dynamicCondExpr) {
+ dynamicCondExpr = nullptr;
+
+ for (const auto &traitSet : ctxSel.v) {
+ using TSSName = parser::OmpTraitSetSelectorName;
+ auto setName = std::get<TSSName>(traitSet.t).v;
+ llvm::omp::TraitSet set = mapTraitSet(setName);
+
+ for (const auto &trait :
+ std::get<std::list<parser::OmpTraitSelector>>(traitSet.t)) {
+ const auto &selectorName =
+ std::get<parser::OmpTraitSelectorName>(trait.t);
+ llvm::omp::TraitSelector selector = mapTraitSelector(selectorName, set);
+ const auto &props =
+ std::get<std::optional<parser::OmpTraitSelector::Properties>>(
+ trait.t);
+
+ // device_num requires runtime target device queries not yet supported.
+ if (const auto *val =
+ std::get_if<parser::OmpTraitSelectorName::Value>(&selectorName.u);
+ val && *val == parser::OmpTraitSelectorName::Value::Device_Num)
+ TODO(loc, "target_device={device_num()} selector in METADIRECTIVE");
+
+ if (selector == llvm::omp::TraitSelector::user_condition) {
+ processUserConditionTrait(vmi, props, semaCtx, dynamicCondExpr);
+ continue;
+ }
+
+ std::optional<llvm::APInt> score;
+ llvm::APInt *scorePtr = getTraitScore(props, semaCtx, score);
+
+ processTraitProperties(vmi, set, selector, props, scorePtr);
+
+ if (props || set != llvm::omp::TraitSet::construct)
+ continue;
+
+ // Construct traits with no properties: the selector is the property.
+ llvm::omp::TraitProperty propKind =
+ llvm::omp::getOpenMPContextTraitPropertyForSelector(selector);
+ if (propKind != llvm::omp::TraitProperty::invalid)
+ vmi.addTrait(set, propKind, selectorName.ToString(), scorePtr);
+ }
+ }
+}
+
} // namespace omp
} // namespace lower
} // namespace Fortran
diff --git a/flang/lib/Lower/OpenMP/Utils.h b/flang/lib/Lower/OpenMP/Utils.h
index 587a078c33ed8..eb784cc7911a5 100644
--- a/flang/lib/Lower/OpenMP/Utils.h
+++ b/flang/lib/Lower/OpenMP/Utils.h
@@ -14,6 +14,7 @@
#include "mlir/Dialect/OpenMP/OpenMPDialect.h"
#include "mlir/IR/Location.h"
#include "mlir/IR/Value.h"
+#include "llvm/Frontend/OpenMP/OMPContext.h"
#include "llvm/Support/CommandLine.h"
#include <cstdint>
#include <optional>
@@ -229,6 +230,19 @@ std::optional<llvm::SmallVector<mlir::Value>> getIteratorElementIndices(
Fortran::lower::AbstractConverter &converter, const omp::Object &object,
Fortran::lower::StatementContext &stmtCtx, mlir::Location loc);
+llvm::omp::TraitSet
+mapTraitSet(parser::OmpTraitSetSelectorName::Value flangSet);
+
+llvm::omp::TraitSelector
+mapTraitSelector(const parser::OmpTraitSelectorName &name,
+ llvm::omp::TraitSet set);
+
+void makeVariantMatchInfo(llvm::omp::VariantMatchInfo &vmi,
+ const parser::modifier::OmpContextSelector &ctxSel,
+ semantics::SemanticsContext &semaCtx,
+ mlir::Location loc,
+ const parser::ScalarExpr *&dynamicCondExpr);
+
} // namespace omp
} // namespace lower
} // namespace Fortran
diff --git a/flang/test/Lower/OpenMP/Todo/metadirective-dynamic.f90 b/flang/test/Lower/OpenMP/Todo/metadirective-dynamic.f90
new file mode 100644
index 0000000000000..a9a02ece1db4b
--- /dev/null
+++ b/flang/test/Lower/OpenMP/Todo/metadirective-dynamic.f90
@@ -0,0 +1,10 @@
+! RUN: %not_todo_cmd %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=50 -o - %s 2>&1 | FileCheck %s
+
+! CHECK: not yet implemented: dynamic user condition in METADIRECTIVE
+
+subroutine test_dynamic_user_condition(flag)
+ logical, intent(in) :: flag
+ !$omp metadirective &
+ !$omp & when(user={condition(flag)}: taskyield) &
+ !$omp & default(nothing)
+end subroutine
diff --git a/flang/test/Lower/OpenMP/Todo/metadirective-exec.f90 b/flang/test/Lower/OpenMP/Todo/metadirective-exec.f90
deleted file mode 100644
index 2e160a1896616..0000000000000
--- a/flang/test/Lower/OpenMP/Todo/metadirective-exec.f90
+++ /dev/null
@@ -1,9 +0,0 @@
-!RUN: %not_todo_cmd bbc -emit-hlfir -fopenmp -fopenmp-version=52 -o - %s 2>&1 | FileCheck %s
-!RUN: %not_todo_cmd %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=52 -o - %s 2>&1 | FileCheck %s
-
-!CHECK: not yet implemented: METADIRECTIVE
-subroutine f00
- continue
- !Executable
- !$omp metadirective when(user={condition(.true.)}: nothing)
-end
diff --git a/flang/test/Lower/OpenMP/Todo/metadirective-loop.f90 b/flang/test/Lower/OpenMP/Todo/metadirective-loop.f90
new file mode 100644
index 0000000000000..999a8c0839d15
--- /dev/null
+++ b/flang/test/Lower/OpenMP/Todo/metadirective-loop.f90
@@ -0,0 +1,12 @@
+! RUN: %not_todo_cmd %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=50 -o - %s 2>&1 | FileCheck %s
+
+! CHECK: not yet implemented: loop-associated METADIRECTIVE variant
+
+subroutine test_loop_variant()
+ integer :: i
+ !$omp metadirective &
+ !$omp & when(implementation={vendor(llvm)}: parallel do) &
+ !$omp & default(nothing)
+ do i = 1, 100
+ end do
+end subroutine
diff --git a/flang/test/Lower/OpenMP/Todo/metadirective-spec.f90 b/flang/test/Lower/OpenMP/Todo/metadirective-spec.f90
deleted file mode 100644
index a00612a92218a..0000000000000
--- a/flang/test/Lower/OpenMP/Todo/metadirective-spec.f90
+++ /dev/null
@@ -1,9 +0,0 @@
-!RUN: %not_todo_cmd bbc -emit-hlfir -fopenmp -fopenmp-version=52 -o - %s 2>&1 | FileCheck %s
-!RUN: %not_todo_cmd %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=52 -o - %s 2>&1 | FileCheck %s
-
-!CHECK: not yet implemented: METADIRECTIVE
-subroutine f00
- !Specification
- !$omp metadirective when(user={condition(.true.)}: nothing)
- implicit none
-end
diff --git a/flang/test/Lower/OpenMP/Todo/metadirective-target-device.f90 b/flang/test/Lower/OpenMP/Todo/metadirective-target-device.f90
new file mode 100644
index 0000000000000..d5269ea909fbc
--- /dev/null
+++ b/flang/test/Lower/OpenMP/Todo/metadirective-target-device.f90
@@ -0,0 +1,10 @@
+! RUN: %not_todo_cmd %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=51 -o - %s 2>&1 | FileCheck %s
+
+! CHECK: not yet implemented: target_device={device_num()} selector in METADIRECTIVE
+
+subroutine test_target_device_num()
+ continue
+ !$omp metadirective &
+ !$omp & when(target_device={device_num(0), kind(gpu)}: barrier) &
+ !$omp & default(nothing)
+end subroutine
diff --git a/flang/test/Lower/OpenMP/metadirective-construct.f90 b/flang/test/Lower/OpenMP/metadirective-construct.f90
new file mode 100644
index 0000000000000..911f6e77a8a6f
--- /dev/null
+++ b/flang/test/Lower/OpenMP/metadirective-construct.f90
@@ -0,0 +1,30 @@
+! Test lowering of OpenMP metadirective with construct selectors.
+
+! RUN: %flang_fc1 -fopenmp -emit-hlfir -fopenmp-version=50 %s -o - | FileCheck %s
+
+! CHECK-LABEL: func.func @_QPtest_construct_parallel()
+! CHECK: omp.parallel
+! CHECK: omp.barrier
+! CHECK: omp.terminator
+! CHECK: return
+subroutine test_construct_parallel()
+ !$omp parallel
+ !$omp metadirective &
+ !$omp & when(construct={parallel}: barrier) &
+ !$omp & default(nothing)
+ !$omp end parallel
+end subroutine
+
+! CHECK-LABEL: func.func @_QPtest_construct_no_match()
+! CHECK: omp.parallel
+! CHECK-NOT: omp.barrier
+! CHECK: omp.taskyield
+! CHECK: omp.terminator
+! CHECK: return
+subroutine test_construct_no_match()
+ !$omp parallel
+ !$omp metadirective &
+ !$omp & when(construct={target}: barrier) &
+ !$omp & default(taskyield)
+ !$omp end parallel
+end subroutine
diff --git a/flang/test/Lower/OpenMP/metadirective-device-isa.f90 b/flang/test/Lower/OpenMP/metadirective-device-isa.f90
new file mode 100644
index 0000000000000..b03c362c3ab9d
--- /dev/null
+++ b/flang/test/Lower/OpenMP/metadirective-device-isa.f90
@@ -0,0 +1,213 @@
+! Test metadirective with device={isa(...)} trait selectors.
+
+! RUN: %flang_fc1 -fopenmp -emit-hlfir -fopenmp-version=50 -target-feature +neon %s -o - | FileCheck --check-prefix=NEON %s
+! RUN: %flang_fc1 -fopenmp -emit-hlfir -fopenmp-version=50 -target-feature +neon -target-feature +sve %s -o - | FileCheck --check-prefix=SVE %s
+! RUN: %flang_fc1 -fopenmp -emit-hlfir -fopenmp-version=50 -target-feature +sse %s -o - | FileCheck --check-prefix=SSE %s
+! RUN: %flang_fc1 -fopenmp -emit-hlfir -fopenmp-version=50 -target-feature +avx %s -o - | FileCheck --check-prefix=AVX %s
+! RUN: %flang_fc1 -fopenmp -emit-hlfir -fopenmp-version=50 %s -o - | FileCheck --check-prefix=NONE %s
+
+! RUN: %flang_fc1 -fopenmp -emit-hlfir -fopenmp-version=51 -target-feature +neon %s -o - | FileCheck --check-prefix=NEON %s
+! RUN: %flang_fc1 -fopenmp -emit-hlfir -fopenmp-version=51 -target-feature +neon -target-feature +sve %s -o - | FileCheck --check-prefix=SVE %s
+! RUN: %flang_fc1 -fopenmp -emit-hlfir -fopenmp-version=51 -target-feature +sse %s -o - | FileCheck --check-prefix=SSE %s
+! RUN: %flang_fc1 -fopenmp -emit-hlfir -fopenmp-version=51 -target-feature +avx %s -o - | FileCheck --check-prefix=AVX %s
+! RUN: %flang_fc1 -fopenmp -emit-hlfir -fopenmp-version=51 %s -o - | FileCheck --check-prefix=NONE %s
+
+! RUN: %flang_fc1 -fopenmp -emit-hlfir -fopenmp-version=52 -cpp -DOMP_52 -target-feature +neon %s -o - | FileCheck --check-prefix=NEON %s
+! RUN: %flang_fc1 -fopenmp -emit-hlfir -fopenmp-version=52 -cpp -DOMP_52 -target-feature +neon -target-feature +sve %s -o - | FileCheck --check-prefix=SVE %s
+! RUN: %flang_fc1 -fopenmp -emit-hlfir -fopenmp-version=52 -cpp -DOMP_52 -target-feature +sse %s -o - | FileCheck --check-prefix=SSE %s
+! RUN: %flang_fc1 -fopenmp -emit-hlfir -fopenmp-version=52 -cpp -DOMP_52 -target-feature +avx %s -o - | FileCheck --check-prefix=AVX %s
+! RUN: %flang_fc1 -fopenmp -emit-hlfir -fopenmp-version=52 -cpp -DOMP_52 %s -o - | FileCheck --check-prefix=NONE %s
+
+! NEON-LABEL: func.func @_QPtest_isa_neon()
+! NEON: omp.barrier
+! SVE-LABEL: func.func @_QPtest_isa_neon()
+! SVE: omp.barrier
+! SSE-LABEL: func.func @_QPtest_isa_neon()
+! SSE-NOT: omp.barrier
+! SSE: return
+! AVX-LABEL: func.func @_QPtest_isa_neon()
+! AVX-NOT: omp.barrier
+! AVX: return
+! NONE-LABEL: func.func @_QPtest_isa_neon()
+! NONE-NOT: omp.barrier
+! NONE: return
+subroutine test_isa_neon()
+ !$omp metadirective &
+ !$omp & when(device={isa("neon")}: barrier) &
+#ifdef OMP_52
+ !$omp & otherwise(nothing)
+#else
+ !$omp & default(nothing)
+#endif
+end subroutine
+
+! NEON-LABEL: func.func @_QPtest_isa_sve()
+! NEON-NOT: omp.barrier
+! NEON: return
+! SVE-LABEL: func.func @_QPtest_isa_sve()
+! SVE: omp.barrier
+! SSE-LABEL: func.func @_QPtest_isa_sve()
+! SSE-NOT: omp.barrier
+! SSE: return
+! AVX-LABEL: func.func @_QPtest_isa_sve()
+! AVX-NOT: omp.barrier
+! AVX: return
+! NONE-LABEL: func.func @_QPtest_isa_sve()
+! NONE-NOT: omp.barrier
+! NONE: return
+subroutine test_isa_sve()
+ !$omp metadirective &
+ !$omp & when(device={isa("sve")}: barrier) &
+#ifdef OMP_52
+ !$omp & otherwise(nothing)
+#else
+ !$omp & default(nothing)
+#endif
+end subroutine
+
+! NEON-LABEL: func.func @_QPtest_isa_sse()
+! NEON-NOT: omp.barrier
+! NEON: return
+! SVE-LABEL: func.func @_QPtest_isa_sse()
+! SVE-NOT: omp.barrier
+! SVE: return
+! SSE-LABEL: func.func @_QPtest_isa_sse()
+! SSE: omp.barrier
+! AVX-LABEL: func.func @_QPtest_isa_sse()
+! AVX-NOT: omp.barrier
+! AVX: return
+! NONE-LABEL: func.func @_QPtest_isa_sse()
+! NONE-NOT: omp.barrier
+! NONE: return
+subroutine test_isa_sse()
+ !$omp metadirective &
+ !$omp & when(device={isa("sse")}: barrier) &
+#ifdef OMP_52
+ !$omp & otherwise(nothing)
+#else
+ !$omp & default(nothing)
+#endif
+end subroutine
+
+! NEON-LABEL: func.func @_QPtest_isa_avx()
+! NEON-NOT: omp.barrier
+! NEON: return
+! SVE-LABEL: func.func @_QPtest_isa_avx()
+! SVE-NOT: omp.barrier
+! SVE: return
+! SSE-LABEL: func.func @_QPtest_isa_avx()
+! SSE-NOT: omp.barrier
+! SSE: return
+! AVX-LABEL: func.func @_QPtest_isa_avx()
+! AVX: omp.barrier
+! NONE-LABEL: func.func @_QPtest_isa_avx()
+! NONE-NOT: omp.barrier
+! NONE: return
+subroutine test_isa_avx()
+ !$omp metadirective &
+ !$omp & when(device={isa("avx")}: barrier) &
+#ifdef OMP_52
+ !$omp & otherwise(nothing)
+#else
+ !$omp & default(nothing)
+#endif
+end subroutine
+
+! NEON-LABEL: func.func @_QPtest_isa_multi_when()
+! NEON: omp.barrier
+! NEON-NOT: omp.taskwait
+! SVE-LABEL: func.func @_QPtest_isa_multi_when()
+! SVE: omp.barrier
+! SVE-NOT: omp.taskwait
+! SSE-LABEL: func.func @_QPtest_isa_multi_when()
+! SSE-NOT: omp.barrier
+! SSE: omp.taskwait
+! AVX-LABEL: func.func @_QPtest_isa_multi_when()
+! AVX-NOT: omp.barrier
+! AVX-NOT: omp.taskwait
+! AVX: return
+! NONE-LABEL: func.func @_QPtest_isa_multi_when()
+! NONE-NOT: omp.barrier
+! NONE-NOT: omp.taskwait
+! NONE: return
+subroutine test_isa_multi_when()
+ !$omp metadirective &
+ !$omp & when(device={isa("neon")}: barrier) &
+ !$omp & when(device={isa("sse")}: taskwait) &
+#ifdef OMP_52
+ !$omp & otherwise(nothing)
+#else
+ !$omp & default(nothing)
+#endif
+end subroutine
+
+! NEON-LABEL: func.func @_QPtest_isa_no_match_default()
+! NEON: omp.barrier
+! SVE-LABEL: func.func @_QPtest_isa_no_match_default()
+! SVE: omp.barrier
+! SSE-LABEL: func.func @_QPtest_isa_no_match_default()
+! SSE: omp.barrier
+! AVX-LABEL: func.func @_QPtest_isa_no_match_default()
+! AVX: omp.barrier
+! NONE-LABEL: func.func @_QPtest_isa_no_match_default()
+! NONE: omp.barrier
+subroutine test_isa_no_match_default()
+ !$omp metadirective &
+ !$omp & when(device={isa("sve2")}: taskwait) &
+#ifdef OMP_52
+ !$omp & otherwise(barrier)
+#else
+ !$omp & default(barrier)
+#endif
+end subroutine
+
+! Test device={arch()} selector. Arch properties without a known TraitProperty
+! mapping are matched against target features, like ISA traits.
+
+! NEON-LABEL: func.func @_QPtest_arch_neon()
+! NEON: omp.barrier
+! SVE-LABEL: func.func @_QPtest_arch_neon()
+! SVE: omp.barrier
+! SSE-LABEL: func.func @_QPtest_arch_neon()
+! SSE-NOT: omp.barrier
+! SSE: return
+! AVX-LABEL: func.func @_QPtest_arch_neon()
+! AVX-NOT: omp.barrier
+! AVX: return
+! NONE-LABEL: func.func @_QPtest_arch_neon()
+! NONE-NOT: omp.barrier
+! NONE: return
+subroutine test_arch_neon()
+ !$omp metadirective &
+ !$omp & when(device={arch("neon")}: barrier) &
+#ifdef OMP_52
+ !$omp & otherwise(nothing)
+#else
+ !$omp & default(nothing)
+#endif
+end subroutine
+
+! NEON-LABEL: func.func @_QPtest_arch_no_match()
+! NEON-NOT: omp.barrier
+! NEON: omp.taskwait
+! SVE-LABEL: func.func @_QPtest_arch_no_match()
+! SVE-NOT: omp.barrier
+! SVE: omp.taskwait
+! SSE-LABEL: func.func @_QPtest_arch_no_match()
+! SSE-NOT: omp.barrier
+! SSE: omp.taskwait
+! AVX-LABEL: func.func @_QPtest_arch_no_match()
+! AVX-NOT: omp.barrier
+! AVX: omp.taskwait
+! NONE-LABEL: func.func @_QPtest_arch_no_match()
+! NONE-NOT: omp.barrier
+! NONE: omp.taskwait
+subroutine test_arch_no_match()
+ !$omp metadirective &
+ !$omp & when(device={arch("unknown_arch")}: barrier) &
+#ifdef OMP_52
+ !$omp & otherwise(taskwait)
+#else
+ !$omp & default(taskwait)
+#endif
+end subroutine
diff --git a/flang/test/Lower/OpenMP/metadirective-device-kind.f90 b/flang/test/Lower/OpenMP/metadirective-device-kind.f90
new file mode 100644
index 0000000000000..f4f5aca61b3db
--- /dev/null
+++ b/flang/test/Lower/OpenMP/metadirective-device-kind.f90
@@ -0,0 +1,21 @@
+! RUN: %flang_fc1 -fopenmp -emit-hlfir -fopenmp-version=50 %s -o - | FileCheck %s
+
+! CHECK-LABEL: func.func @_QPtest_device_kind_host()
+! CHECK: omp.taskyield
+! CHECK: return
+subroutine test_device_kind_host()
+ !$omp metadirective &
+ !$omp & when(device={kind(host)}: taskyield) &
+ !$omp & default(nothing)
+end subroutine
+
+! CHECK-LABEL: func.func @_QPtest_multiple_when_second_match()
+! CHECK-NOT: omp.taskwait
+! CHECK: omp.taskyield
+! CHECK: return
+subroutine test_multiple_when_second_match()
+ !$omp metadirective &
+ !$omp & when(implementation={vendor("amd")}: taskwait) &
+ !$omp & when(device={kind(host)}: taskyield) &
+ !$omp & default(nothing)
+end subroutine
diff --git a/flang/test/Lower/OpenMP/metadirective-implementation.f90 b/flang/test/Lower/OpenMP/metadirective-implementation.f90
new file mode 100644
index 0000000000000..9e071f67c2e67
--- /dev/null
+++ b/flang/test/Lower/OpenMP/metadirective-implementation.f90
@@ -0,0 +1,121 @@
+! Test lowering of OpenMP metadirective with implementation selectors.
+
+! RUN: %flang_fc1 -fopenmp -emit-hlfir -fopenmp-version=50 %s -o - | FileCheck %s
+! RUN: %flang_fc1 -fopenmp -emit-hlfir -fopenmp-version=51 %s -o - | FileCheck %s
+! RUN: %flang_fc1 -fopenmp -emit-hlfir -fopenmp-version=52 -cpp -DOMP_52 %s -o - | FileCheck %s
+
+! CHECK-LABEL: func.func @_QPtest_vendor_llvm()
+! CHECK: omp.taskwait
+! CHECK: return
+subroutine test_vendor_llvm()
+ !$omp metadirective &
+ !$omp & when(implementation={vendor(llvm)}: taskwait) &
+#ifdef OMP_52
+ !$omp & otherwise(nothing)
+#else
+ !$omp & default(nothing)
+#endif
+end subroutine
+
+! CHECK-LABEL: func.func @_QPtest_vendor_no_match()
+! CHECK-NOT: omp.taskwait
+! CHECK: return
+subroutine test_vendor_no_match()
+ !$omp metadirective &
+ !$omp & when(implementation={vendor("unknown")}: taskwait) &
+#ifdef OMP_52
+ !$omp & otherwise(nothing)
+#else
+ !$omp & default(nothing)
+#endif
+end subroutine
+
+! CHECK-LABEL: func.func @_QPtest_standalone_barrier_match()
+! CHECK: omp.barrier
+! CHECK: return
+subroutine test_standalone_barrier_match()
+ !$omp metadirective &
+ !$omp & when(implementation={vendor(llvm)}: barrier) &
+#ifdef OMP_52
+ !$omp & otherwise(nothing)
+#else
+ !$omp & default(nothing)
+#endif
+end subroutine
+
+! CHECK-LABEL: func.func @_QPtest_standalone_barrier_fallback()
+! CHECK: omp.barrier
+! CHECK: return
+subroutine test_standalone_barrier_fallback()
+ !$omp metadirective &
+ !$omp & when(implementation={vendor("cray")}: nothing) &
+#ifdef OMP_52
+ !$omp & otherwise(barrier)
+#else
+ !$omp & default(barrier)
+#endif
+end subroutine
+
+! CHECK-LABEL: func.func @_QPtest_nothing_variant()
+! CHECK-NOT: omp.taskwait
+! CHECK: return
+subroutine test_nothing_variant()
+ !$omp metadirective &
+ !$omp & when(implementation={vendor(llvm)}: nothing) &
+#ifdef OMP_52
+ !$omp & otherwise(taskwait)
+#else
+ !$omp & default(taskwait)
+#endif
+end subroutine
+
+! CHECK-LABEL: func.func @_QPtest_default_fallback()
+! CHECK: omp.taskwait
+! CHECK: return
+subroutine test_default_fallback()
+ !$omp metadirective &
+ !$omp & when(implementation={vendor("unknown")}: nothing) &
+#ifdef OMP_52
+ !$omp & otherwise(taskwait)
+#else
+ !$omp & default(taskwait)
+#endif
+end subroutine
+
+! CHECK-LABEL: func.func @_QPtest_no_default()
+! CHECK-NOT: omp.taskyield
+! CHECK: return
+subroutine test_no_default()
+ !$omp metadirective &
+ !$omp & when(implementation={vendor("gnu")}: taskyield)
+end subroutine
+
+! CHECK-LABEL: func.func @_QPtest_multiple_when_first_match()
+! CHECK: omp.taskwait
+! CHECK-NOT: omp.taskyield
+! CHECK: return
+subroutine test_multiple_when_first_match()
+ !$omp metadirective &
+ !$omp & when(implementation={vendor(llvm)}: taskwait) &
+ !$omp & when(user={condition(.false.)}: taskyield) &
+#ifdef OMP_52
+ !$omp & otherwise(nothing)
+#else
+ !$omp & default(nothing)
+#endif
+end subroutine
+
+! CHECK-LABEL: func.func @_QPtest_multiple_when_fallback()
+! CHECK-NOT: omp.taskyield
+! CHECK: omp.taskwait
+! CHECK: return
+subroutine test_multiple_when_fallback()
+ !$omp metadirective &
+ !$omp & when(implementation={vendor("nvidia")}: taskyield) &
+ !$omp & when(user={condition(.false.)}: taskyield) &
+#ifdef OMP_52
+ !$omp & otherwise(taskwait)
+#else
+ !$omp & default(taskwait)
+#endif
+end subroutine
diff --git a/flang/test/Lower/OpenMP/metadirective-user.f90 b/flang/test/Lower/OpenMP/metadirective-user.f90
new file mode 100644
index 0000000000000..5165268cf7cc2
--- /dev/null
+++ b/flang/test/Lower/OpenMP/metadirective-user.f90
@@ -0,0 +1,33 @@
+! Test lowering of OpenMP metadirective with constant-folded user selectors.
+
+! RUN: %flang_fc1 -fopenmp -emit-hlfir -fopenmp-version=50 %s -o - | FileCheck %s
+! RUN: %flang_fc1 -fopenmp -emit-hlfir -fopenmp-version=51 %s -o - | FileCheck %s
+! RUN: %flang_fc1 -fopenmp -emit-hlfir -fopenmp-version=52 -cpp -DOMP_52 %s -o - | FileCheck %s
+
+! CHECK-LABEL: func.func @_QPtest_condition_true()
+! CHECK: omp.taskyield
+! CHECK-NOT: fir.if
+! CHECK: return
+subroutine test_condition_true()
+ !$omp metadirective &
+ !$omp & when(user={condition(.true.)}: taskyield) &
+#ifdef OMP_52
+ !$omp & otherwise(nothing)
+#else
+ !$omp & default(nothing)
+#endif
+end subroutine
+
+! CHECK-LABEL: func.func @_QPtest_condition_false()
+! CHECK-NOT: omp.taskwait
+! CHECK-NOT: fir.if
+! CHECK: return
+subroutine test_condition_false()
+ !$omp metadirective &
+ !$omp & when(user={condition(.false.)}: taskwait) &
+#ifdef OMP_52
+ !$omp & otherwise(nothing)
+#else
+ !$omp & default(nothing)
+#endif
+end subroutine
More information about the flang-commits
mailing list