[llvm-branch-commits] [flang] [llvm] [flang][OpenMP] Don't treat different runtime conditions as subsets (PR #233493)

via llvm-branch-commits llvm-branch-commits at lists.llvm.org
Mon Oct 19 21:55:58 PDT 2026


https://github.com/chichunchen updated https://github.com/llvm/llvm-project/pull/233493

>From 41e782626ef06bc71452ab02b27a97108245dfbd Mon Sep 17 00:00:00 2001
From: "Chi Chun, Chen" <chichun.chen at hpe.com>
Date: Sat, 17 Oct 2026 22:32:07 -0500
Subject: [PATCH 1/2] [flang][OpenMP] Don't treat different runtime conditions
 as subsets

When two METADIRECTIVE candidates have the same score and one selector is
a strict subset of the other, the superset wins. Flang ranks every
runtime condition with the same property, so isStrictSubset saw
different conditions as the same:

  !$omp metadirective &
  !$omp& when(user={condition(a)}, &
  !$omp&      implementation={vendor(score(4): llvm)}: taskyield) &
  !$omp& when(user={condition(b)}, device={kind(host)}, &
  !$omp&      implementation={vendor(score(3): llvm)}: taskwait)

Both candidates score 5, and `b` was tested first, so TASKWAIT ran when
both conditions were true. Neither selector is a subset of the other, so
clause order should decide.

This patch adds a UserCondition key to VariantMatchInfo, and
isStrictSubset requires equal keys when the first selector has a
condition. Flang gives each distinct runtime condition of a METADIRECTIVE
its own key. It compares the conditions the same way as the existing
check for repeated conditions, so condition(a) and condition((a)) still
form a strict subset. Like that check, it treats different names for the
same entity as different conditions. Ranking also sees conditions with
function calls, which that check rejects, so the comparison now also
collects the symbols in the length of a character array constructor.
Conditions that fold to a constant of any kind, including all of Clang's,
leave the key empty and are still compared by truth value. A constant
condition and a runtime condition are now different.

Fixes #233038.

Assisted-by: GitHub Copilot (Claude Opus 5.5)
---
 flang/lib/Semantics/openmp-utils.cpp          |  45 +++-
 .../test/Lower/OpenMP/metadirective-user.f90  | 224 ++++++++++++++++++
 .../include/llvm/Frontend/OpenMP/OMPContext.h |   4 +
 llvm/lib/Frontend/OpenMP/OMPContext.cpp       |   7 +
 llvm/unittests/Frontend/OpenMPContextTest.cpp |  37 +++
 5 files changed, 314 insertions(+), 3 deletions(-)

diff --git a/flang/lib/Semantics/openmp-utils.cpp b/flang/lib/Semantics/openmp-utils.cpp
index 0c86239136553..9a116c886f1a7 100644
--- a/flang/lib/Semantics/openmp-utils.cpp
+++ b/flang/lib/Semantics/openmp-utils.cpp
@@ -2473,6 +2473,12 @@ static void AppendConstructTraitsForDirective(
     add(llvm::omp::TraitProperty::construct_dispatch_dispatch);
 }
 
+static SomeExpr NormalizeMetadirectiveCondition(const SomeExpr &expr);
+namespace {
+template <typename T>
+bool AreSameMetadirectiveCondition(const T &left, const T &right);
+} // namespace
+
 static void AddTraitPropertiesFromSelector(llvm::omp::TraitSet set,
     const parser::OmpTraitSelector &selector, llvm::omp::VariantMatchInfo &vmi,
     SemanticsContext &semaCtx,
@@ -2579,6 +2585,24 @@ std::optional<MetadirectiveCandidateSet> BuildMetadirectiveCandidateSet(
     return spec.DirId() == llvm::omp::Directive::OMPD_nothing ? nullptr : &spec;
   };
 
+  // Identify each distinct runtime condition, so that different conditions
+  // are not strict subsets of each other.
+  llvm::SmallVector<SomeExpr, 4> runtimeConditions;
+  auto getConditionKey = [&](const DynamicUserCondition &condition) {
+    const SomeExpr *expr{GetExpr(context, *condition.expr)};
+    if (!expr ||
+        evaluate::IsActuallyConstant(
+            evaluate::Fold(context.foldingContext(), common::Clone(*expr))))
+      return std::string{};
+    SomeExpr normalized{NormalizeMetadirectiveCondition(*expr)};
+    auto *it{llvm::find_if(runtimeConditions, [&](const SomeExpr &condition) {
+      return AreSameMetadirectiveCondition(condition, normalized);
+    })};
+    if (it == runtimeConditions.end())
+      it = &runtimeConditions.emplace_back(std::move(normalized));
+    return std::to_string(it - runtimeConditions.begin() + 1);
+  };
+
   for (const parser::OmpClause &clause : clauses.v) {
     if (const auto *whenClause{
             std::get_if<parser::OmpClause::When>(&clause.u)}) {
@@ -2593,6 +2617,8 @@ std::optional<MetadirectiveCandidateSet> BuildMetadirectiveCandidateSet(
       llvm::omp::VariantMatchInfo rawVMI;
       std::optional<DynamicUserCondition> dynamicCondition{
           MakeVariantMatchInfo(rawVMI, *ctxSel, context)};
+      if (dynamicCondition)
+        rawVMI.UserCondition = getConditionKey(*dynamicCondition);
       if (llvm::any_of(
               rawVMI.ConstructTraits, [](llvm::omp::TraitProperty property) {
                 return llvm::omp::getOpenMPContextTraitSetForProperty(
@@ -2751,6 +2777,14 @@ struct ConditionSymbols : evaluate::Traverse<ConditionSymbols, bool, false> {
     symbols.push_back(&symbol);
     return true;
   }
+  // Expr equality compares the length, but Traverse doesn't visit it.
+  template <int KIND>
+  bool operator()(const evaluate::ArrayConstructor<
+      evaluate::Type<common::TypeCategory::Character, KIND>> &x) const {
+    if (const auto *len{x.LEN()})
+      (*this)(*len);
+    return Base::operator()(x);
+  }
   std::vector<const Symbol *> &symbols;
 };
 
@@ -2805,6 +2839,12 @@ bool isRepeatableMetadirectiveCondition(const SomeExpr &expr) {
 }
 } // namespace
 
+static SomeExpr NormalizeMetadirectiveCondition(const SomeExpr &expr) {
+  MetadirectiveConditionNormalizer normalizer;
+  evaluate::rewrite::Mutator normalize{normalizer};
+  return normalize(expr);
+}
+
 bool IsRepeatableMetadirectiveCondition(
     const parser::ScalarExpr &condition, SemanticsContext &context) {
   const SomeExpr *expr{GetExpr(context, condition)};
@@ -2820,10 +2860,9 @@ bool AreSameRepeatableMetadirectiveCondition(const parser::ScalarExpr &left,
       !isRepeatableMetadirectiveCondition(*rightExpr))
     return false;
 
-  MetadirectiveConditionNormalizer normalizer;
-  evaluate::rewrite::Mutator normalize{normalizer};
   return AreSameMetadirectiveCondition(
-      normalize(*leftExpr), normalize(*rightExpr));
+      NormalizeMetadirectiveCondition(*leftExpr),
+      NormalizeMetadirectiveCondition(*rightExpr));
 }
 
 llvm::SmallVector<unsigned, 4> GetMetadirectiveElsePathCandidates(
diff --git a/flang/test/Lower/OpenMP/metadirective-user.f90 b/flang/test/Lower/OpenMP/metadirective-user.f90
index cdfbddd4151a0..7198d36ac2f1e 100644
--- a/flang/test/Lower/OpenMP/metadirective-user.f90
+++ b/flang/test/Lower/OpenMP/metadirective-user.f90
@@ -544,3 +544,227 @@ subroutine test_multi_dynamic_multi_static(a, b)
   !$omp & default(nothing)
 #endif
 end subroutine
+
+! Both candidates score 5. The conditions differ, so neither selector is a
+! subset of the other and `a` is tested first.
+! CHECK-LABEL: func.func @_QPtest_distinct_conditions(
+! CHECK-SAME:    %[[ARG0:[^,]*]]: !fir.ref<!fir.logical<4>>
+! CHECK-SAME:    %[[ARG1:.*]]: !fir.ref<!fir.logical<4>>
+! CHECK:         %[[DECLA:.*]]:2 = hlfir.declare %[[ARG0]]
+! CHECK:         %[[DECLB:.*]]:2 = hlfir.declare %[[ARG1]]
+! CHECK:         %[[LOADA:.*]] = fir.load %[[DECLA]]#0
+! CHECK:         %[[CONDA:.*]] = fir.convert %[[LOADA]] : (!fir.logical<4>) -> i1
+! CHECK:         fir.if %[[CONDA]] {
+! CHECK-NEXT:      omp.taskyield
+! CHECK-NEXT:    } else {
+! CHECK:           %[[LOADB:.*]] = fir.load %[[DECLB]]#0
+! CHECK:           %[[CONDB:.*]] = fir.convert %[[LOADB]] : (!fir.logical<4>) -> i1
+! CHECK:           fir.if %[[CONDB]] {
+! CHECK-NEXT:        omp.taskwait
+! CHECK:         return
+subroutine test_distinct_conditions(a, b)
+  logical, intent(in) :: a, b
+  !$omp metadirective &
+  !$omp & when(user={condition(a)}, &
+  !$omp &      implementation={vendor(score(4): llvm)}: taskyield) &
+  !$omp & when(user={condition(b)}, device={kind(host)}, &
+  !$omp &      implementation={vendor(score(3): llvm)}: taskwait)
+end subroutine
+
+! With the same condition, the first selector is a strict subset of the second.
+! CHECK-LABEL: func.func @_QPtest_same_condition(
+! CHECK:         fir.if
+! CHECK-NEXT:      omp.taskwait
+! CHECK-NOT:     omp.taskyield
+! CHECK:         return
+subroutine test_same_condition(a)
+  logical, intent(in) :: a
+  !$omp metadirective &
+  !$omp & when(user={condition(a)}, &
+  !$omp &      implementation={vendor(score(4): llvm)}: taskyield) &
+  !$omp & when(user={condition(a)}, device={kind(host)}, &
+  !$omp &      implementation={vendor(score(3): llvm)}: taskwait)
+end subroutine
+
+! Outer parentheses don't change the condition.
+! CHECK-LABEL: func.func @_QPtest_parenthesized_condition(
+! CHECK:         fir.if
+! CHECK-NEXT:      omp.taskwait
+! CHECK-NOT:     omp.taskyield
+! CHECK:         return
+subroutine test_parenthesized_condition(a)
+  logical, intent(in) :: a
+  !$omp metadirective &
+  !$omp & when(user={condition((a))}, &
+  !$omp &      implementation={vendor(score(4): llvm)}: taskyield) &
+  !$omp & when(user={condition(a)}, device={kind(host)}, &
+  !$omp &      implementation={vendor(score(3): llvm)}: taskwait)
+end subroutine
+
+! Under match_none, both candidates score 3 and `a` is tested first.
+! CHECK-LABEL: func.func @_QPtest_distinct_conditions_match_none(
+! CHECK-SAME:    %[[ARG0:[^,]*]]: !fir.ref<!fir.logical<4>>
+! CHECK:         %[[DECLA:.*]]:2 = hlfir.declare %[[ARG0]]
+! CHECK:         %[[LOADA:.*]] = fir.load %[[DECLA]]#0
+! CHECK:         %[[CONDA:.*]] = fir.convert %[[LOADA]] : (!fir.logical<4>) -> i1
+! CHECK:         %[[NOTA:.*]] = arith.xori %[[CONDA]]
+! CHECK:         fir.if %[[NOTA]] {
+! CHECK-NEXT:      omp.taskyield
+! CHECK-NEXT:    } else {
+! CHECK:           omp.taskwait
+! CHECK:         return
+subroutine test_distinct_conditions_match_none(a, b)
+  logical, intent(in) :: a, b
+  !$omp metadirective &
+  !$omp & when(user={condition(score(2): a)}, &
+  !$omp &      implementation={extension(match_none)}: taskyield) &
+  !$omp & when(user={condition(b)}, device={arch(nvptx)}, &
+  !$omp &      implementation={extension(match_none)}: taskwait)
+end subroutine
+
+! Constant conditions are compared by truth value, whatever their kind.
+! CHECK-LABEL: func.func @_QPtest_constant_condition_kinds(
+! CHECK-NOT:     omp.taskyield
+! CHECK:         omp.taskwait
+! CHECK-NOT:     omp.taskyield
+! CHECK:         return
+subroutine test_constant_condition_kinds()
+  !$omp metadirective &
+  !$omp & when(user={condition(.true._8)}, &
+  !$omp &      implementation={vendor(score(4): llvm)}: taskyield) &
+  !$omp & when(user={condition(.true._4)}, device={kind(host)}, &
+  !$omp &      implementation={vendor(score(3): llvm)}: taskwait)
+end subroutine
+
+! Calls to intrinsics without arguments are evaluated at runtime.
+! CHECK-LABEL: func.func @_QPtest_intrinsic_call_conditions(
+! CHECK:         arith.cmpi sgt, %{{.*}}, %c0
+! CHECK:         fir.if
+! CHECK-NEXT:      omp.taskyield
+! CHECK-NEXT:    } else {
+! CHECK:           arith.cmpi sgt, %{{.*}}, %c1
+! CHECK:           fir.if
+! CHECK-NEXT:        omp.taskwait
+! CHECK:         return
+subroutine test_intrinsic_call_conditions()
+  !$omp metadirective &
+  !$omp & when(user={condition(command_argument_count() > 0)}, &
+  !$omp &      implementation={vendor(score(4): llvm)}: taskyield) &
+  !$omp & when(user={condition(command_argument_count() > 1)}, &
+  !$omp &      device={kind(host)}, &
+  !$omp &      implementation={vendor(score(3): llvm)}: taskwait)
+end subroutine
+
+module metadirective_user_a
+contains
+  logical function predicate()
+    predicate = .true.
+  end function
+  logical function invoke(p)
+    interface
+      logical function p()
+      end function
+    end interface
+    invoke = p()
+  end function
+end module
+
+module metadirective_user_b
+contains
+  logical function predicate()
+    predicate = .false.
+  end function
+end module
+
+! The conditions pass different procedures with the same name, so `a` is
+! tested first.
+! CHECK-LABEL: func.func @_QPtest_same_name_conditions(
+! CHECK:         fir.address_of(@_QMmetadirective_user_aPpredicate)
+! CHECK:         fir.if
+! CHECK-NEXT:      omp.taskyield
+! CHECK-NEXT:    } else {
+! CHECK:           fir.address_of(@_QMmetadirective_user_bPpredicate)
+! CHECK:           fir.if
+! CHECK-NEXT:        omp.taskwait
+! CHECK:         return
+subroutine test_same_name_conditions()
+  use metadirective_user_a, only: a => predicate, invoke
+  use metadirective_user_b, only: b => predicate
+  !$omp metadirective &
+  !$omp & when(user={condition(invoke(a))}, &
+  !$omp &      implementation={vendor(score(4): llvm)}: taskyield) &
+  !$omp & when(user={condition(invoke(b))}, device={kind(host)}, &
+  !$omp &      implementation={vendor(score(3): llvm)}: taskwait)
+end subroutine
+
+! `a` and `b` are the first dummy arguments of different procedures, but they
+! are different variables, so `a` is tested first.
+! CHECK-LABEL: func.func private @_QFtest_host_dummy_conditionsPinner(
+! CHECK-SAME:    %[[ARG0:[^,]*]]: !fir.ref<!fir.logical<4>>
+! CHECK:         %[[A:.*]]:2 = hlfir.declare {{.*}}"_QFtest_host_dummy_conditionsEa"
+! CHECK:         %[[B:.*]]:2 = hlfir.declare %[[ARG0]] {{.*}}"_QFtest_host_dummy_conditionsFinnerEb"
+! CHECK:         %[[LOADA:.*]] = fir.load %[[A]]#0
+! CHECK:         %[[CONDA:.*]] = fir.convert %[[LOADA]]
+! CHECK:         fir.if %[[CONDA]] {
+! CHECK-NEXT:      omp.taskyield
+! CHECK-NEXT:    } else {
+! CHECK:           fir.load %[[B]]#0
+! CHECK:           omp.taskwait
+! CHECK:         return
+subroutine test_host_dummy_conditions(a)
+  logical, intent(in) :: a
+  call inner(a)
+contains
+  subroutine inner(b)
+    logical, intent(in) :: b
+    !$omp metadirective &
+    !$omp & when(user={condition(a)}, &
+    !$omp &      implementation={vendor(score(4): llvm)}: taskyield) &
+    !$omp & when(user={condition(b)}, device={kind(host)}, &
+    !$omp &      implementation={vendor(score(3): llvm)}: taskwait)
+  end subroutine
+end subroutine
+
+! `a .and. b` is not the same condition as `a`, so it is tested first.
+! CHECK-LABEL: func.func private @_QFtest_host_dummy_and_conditionPinner(
+! CHECK:         fir.logical_and
+! CHECK:         fir.if
+! CHECK-NEXT:      omp.taskyield
+! CHECK-NEXT:    } else {
+! CHECK:           omp.taskwait
+! CHECK:         return
+subroutine test_host_dummy_and_condition(a)
+  logical, intent(in) :: a
+  call inner(a)
+contains
+  subroutine inner(b)
+    logical, intent(in) :: b
+    !$omp metadirective &
+    !$omp & when(user={condition(a .and. b)}, &
+    !$omp &      implementation={vendor(score(4): llvm)}: taskyield) &
+    !$omp & when(user={condition(a)}, device={kind(host)}, &
+    !$omp &      implementation={vendor(score(3): llvm)}: taskwait)
+  end subroutine
+end subroutine
+
+! The array constructors have lengths from different dummy arguments.
+! CHECK-LABEL: func.func private @_QFtest_host_dummy_lengthsPinner(
+! CHECK:         fir.if
+! CHECK-NEXT:      omp.taskyield
+! CHECK-NEXT:    } else {
+! CHECK:           omp.taskwait
+! CHECK:         return
+subroutine test_host_dummy_lengths(n)
+  integer, intent(in) :: n
+  call inner(n)
+contains
+  subroutine inner(m)
+    integer, intent(in) :: m
+    !$omp metadirective &
+    !$omp & when(user={condition(maxval([character(len=n)::]) == '')}, &
+    !$omp &      implementation={vendor(score(4): llvm)}: taskyield) &
+    !$omp & when(user={condition(maxval([character(len=m)::]) == '')}, &
+    !$omp &      device={kind(host)}, &
+    !$omp &      implementation={vendor(score(3): llvm)}: taskwait)
+  end subroutine
+end subroutine
diff --git a/llvm/include/llvm/Frontend/OpenMP/OMPContext.h b/llvm/include/llvm/Frontend/OpenMP/OMPContext.h
index 7849d32665994..c8c69823d59ed 100644
--- a/llvm/include/llvm/Frontend/OpenMP/OMPContext.h
+++ b/llvm/include/llvm/Frontend/OpenMP/OMPContext.h
@@ -22,6 +22,8 @@
 #include "llvm/Frontend/OpenMP/OMPConstants.h"
 #include "llvm/Support/Compiler.h"
 
+#include <string>
+
 namespace llvm {
 class Triple;
 namespace omp {
@@ -156,6 +158,8 @@ struct VariantMatchInfo {
   SmallVector<StringRef, 8> ISATraits;
   SmallVector<TraitProperty, 8> ConstructTraits;
   SmallDenseMap<TraitProperty, APInt> ScoreMap;
+  /// Identifies a runtime user condition; empty for a constant condition.
+  std::string UserCondition;
 };
 
 /// The context for a source location is made up of active property traits,
diff --git a/llvm/lib/Frontend/OpenMP/OMPContext.cpp b/llvm/lib/Frontend/OpenMP/OMPContext.cpp
index f2cdd9bbaf5e4..dca1b6c63af39 100644
--- a/llvm/lib/Frontend/OpenMP/OMPContext.cpp
+++ b/llvm/lib/Frontend/OpenMP/OMPContext.cpp
@@ -172,6 +172,13 @@ static bool isStrictSubset(const VariantMatchInfo &VMI0,
   for (unsigned Bit : VMI0.RequiredTraits.set_bits())
     if (!VMI1.RequiredTraits.test(Bit))
       return false;
+  // Different runtime conditions share a trait, so compare them as well.
+  for (TraitProperty Property :
+       {TraitProperty::user_condition_true, TraitProperty::user_condition_false,
+        TraitProperty::user_condition_unknown})
+    if (VMI0.RequiredTraits.test(unsigned(Property)) &&
+        VMI0.UserCondition != VMI1.UserCondition)
+      return false;
   if (!isSubset<TraitProperty>(VMI0.ConstructTraits, VMI1.ConstructTraits))
     return false;
   return true;
diff --git a/llvm/unittests/Frontend/OpenMPContextTest.cpp b/llvm/unittests/Frontend/OpenMPContextTest.cpp
index f9683ae56e933..cdf39813fe0f1 100644
--- a/llvm/unittests/Frontend/OpenMPContextTest.cpp
+++ b/llvm/unittests/Frontend/OpenMPContextTest.cpp
@@ -317,4 +317,41 @@ TEST_F(OpenMPContextTest, ScoringSimple) {
   // TODO: Add scoring tests (via getBestVariantMatchForContext).
 }
 
+TEST_F(OpenMPContextTest, UserConditionInStrictSubset) {
+  OMPContext Context(false, Triple("x86_64-unknown-linux"), Triple(), -1);
+  APInt Three(64, 3), Four(64, 4), Five(64, 5);
+  VariantMatchInfo A, B;
+  A.addTrait(TraitProperty::user_condition_true, "");
+  A.addTrait(TraitProperty::implementation_vendor_llvm, "", &Four);
+  B.addTrait(TraitProperty::user_condition_true, "");
+  B.addTrait(TraitProperty::implementation_vendor_llvm, "", &Three);
+  B.addTrait(TraitProperty::device_kind_host, "");
+  auto Best = [&](const VariantMatchInfo &First,
+                  const VariantMatchInfo &Second) {
+    return getBestVariantMatchForContext(
+        SmallVector<VariantMatchInfo, 2>{First, Second}, Context);
+  };
+
+  // Both score 5. Constant conditions are compared by their trait alone.
+  EXPECT_EQ(Best(A, B), 1);
+  EXPECT_EQ(Best(B, A), 0);
+
+  // Different runtime conditions keep clause order.
+  A.UserCondition = "a";
+  B.UserCondition = "b";
+  EXPECT_EQ(Best(A, B), 0);
+  EXPECT_EQ(Best(B, A), 0);
+
+  // The same runtime condition still makes A a strict subset of B.
+  B.UserCondition = "a";
+  EXPECT_EQ(Best(A, B), 1);
+  EXPECT_EQ(Best(B, A), 0);
+
+  // A higher score wins regardless of the conditions.
+  B.UserCondition = "b";
+  A.ScoreMap[TraitProperty::implementation_vendor_llvm] = Five;
+  EXPECT_EQ(Best(A, B), 0);
+  EXPECT_EQ(Best(B, A), 1);
+}
+
 } // namespace

>From c11d452ec8f021b8ba9dfbc4a253a1770e8918c9 Mon Sep 17 00:00:00 2001
From: "Chi Chun, Chen" <chichun.chen at hpe.com>
Date: Mon, 19 Oct 2026 23:49:38 -0500
Subject: [PATCH 2/2] Compare the condition of an unguarded match_any candidate

A match_any WHEN clause with a runtime condition also gets a candidate
without the condition, since its other traits can still match. That
candidate has no condition trait, so isStrictSubset skipped the key
check, and it became a subset of a WHEN clause with a different
condition. The key is now compared whenever it is set.
---
 .../test/Lower/OpenMP/metadirective-user.f90  | 35 +++++++++++++++++++
 llvm/lib/Frontend/OpenMP/OMPContext.cpp       |  3 ++
 llvm/unittests/Frontend/OpenMPContextTest.cpp | 14 ++++++++
 3 files changed, 52 insertions(+)

diff --git a/flang/test/Lower/OpenMP/metadirective-user.f90 b/flang/test/Lower/OpenMP/metadirective-user.f90
index 7198d36ac2f1e..4dd26e35e8385 100644
--- a/flang/test/Lower/OpenMP/metadirective-user.f90
+++ b/flang/test/Lower/OpenMP/metadirective-user.f90
@@ -622,6 +622,41 @@ subroutine test_distinct_conditions_match_none(a, b)
   !$omp &      implementation={extension(match_none)}: taskwait)
 end subroutine
 
+! Under match_any, the first WHEN also matches through `vendor(llvm)` when `a` is
+! false. Both score 3 and the conditions differ, so TASKYIELD is always selected.
+! CHECK-LABEL: func.func @_QPtest_distinct_conditions_match_any(
+! CHECK-NOT:     omp.taskwait
+! CHECK:         omp.taskyield
+! CHECK-NOT:     omp.taskwait
+! CHECK:         return
+subroutine test_distinct_conditions_match_any(a, b)
+  logical, intent(in) :: a, b
+  !$omp metadirective &
+  !$omp & when(user={condition(a)}, &
+  !$omp &      implementation={vendor(score(2): llvm), extension(match_any)}: &
+  !$omp &      taskyield) &
+  !$omp & when(user={condition(b)}, device={kind(host)}, &
+  !$omp &      implementation={vendor(score(1): llvm), extension(match_any)}: &
+  !$omp &      taskwait)
+end subroutine
+
+! A constant condition is different from a runtime condition.
+! CHECK-LABEL: func.func @_QPtest_constant_and_runtime_conditions_match_any(
+! CHECK-NOT:     omp.taskwait
+! CHECK:         omp.taskyield
+! CHECK-NOT:     omp.taskwait
+! CHECK:         return
+subroutine test_constant_and_runtime_conditions_match_any(b)
+  logical, intent(in) :: b
+  !$omp metadirective &
+  !$omp & when(user={condition(.true.)}, &
+  !$omp &      implementation={vendor(score(2): llvm), extension(match_any)}: &
+  !$omp &      taskyield) &
+  !$omp & when(user={condition(b)}, device={kind(host)}, &
+  !$omp &      implementation={vendor(score(1): llvm), extension(match_any)}: &
+  !$omp &      taskwait)
+end subroutine
+
 ! Constant conditions are compared by truth value, whatever their kind.
 ! CHECK-LABEL: func.func @_QPtest_constant_condition_kinds(
 ! CHECK-NOT:     omp.taskyield
diff --git a/llvm/lib/Frontend/OpenMP/OMPContext.cpp b/llvm/lib/Frontend/OpenMP/OMPContext.cpp
index dca1b6c63af39..dffb7b2baece7 100644
--- a/llvm/lib/Frontend/OpenMP/OMPContext.cpp
+++ b/llvm/lib/Frontend/OpenMP/OMPContext.cpp
@@ -173,6 +173,9 @@ static bool isStrictSubset(const VariantMatchInfo &VMI0,
     if (!VMI1.RequiredTraits.test(Bit))
       return false;
   // Different runtime conditions share a trait, so compare them as well.
+  // A runtime condition can be left out of the traits but keep its key.
+  if (!VMI0.UserCondition.empty() && VMI0.UserCondition != VMI1.UserCondition)
+    return false;
   for (TraitProperty Property :
        {TraitProperty::user_condition_true, TraitProperty::user_condition_false,
         TraitProperty::user_condition_unknown})
diff --git a/llvm/unittests/Frontend/OpenMPContextTest.cpp b/llvm/unittests/Frontend/OpenMPContextTest.cpp
index cdf39813fe0f1..2450bcc1f63d0 100644
--- a/llvm/unittests/Frontend/OpenMPContextTest.cpp
+++ b/llvm/unittests/Frontend/OpenMPContextTest.cpp
@@ -352,6 +352,20 @@ TEST_F(OpenMPContextTest, UserConditionInStrictSubset) {
   A.ScoreMap[TraitProperty::implementation_vendor_llvm] = Five;
   EXPECT_EQ(Best(A, B), 0);
   EXPECT_EQ(Best(B, A), 1);
+
+  // A runtime condition left out of the traits is still compared by its key.
+  VariantMatchInfo C;
+  C.addTrait(TraitProperty::implementation_vendor_llvm, "", &Four);
+  C.UserCondition = "a";
+  EXPECT_EQ(Best(C, B), 0);
+  EXPECT_EQ(Best(B, C), 0);
+  C.UserCondition = "b";
+  EXPECT_EQ(Best(C, B), 1);
+
+  // A constant condition is different from a runtime condition.
+  C.UserCondition = "";
+  C.addTrait(TraitProperty::user_condition_true, "");
+  EXPECT_EQ(Best(C, B), 0);
 }
 
 } // namespace



More information about the llvm-branch-commits mailing list