[flang-commits] [flang] [flang][OpenMP] Lower DO, SIMD, and DO SIMD metadirective variants (PR #218555)

via flang-commits flang-commits at lists.llvm.org
Fri Aug 28 15:00:09 PDT 2026


================
@@ -0,0 +1,1124 @@
+! Test lowering of metadirectives with ordinary loop-associated variants.
+
+! RUN: %flang_fc1 -fopenmp -emit-hlfir -fopenmp-version=52 %s -o - | \
+! RUN:   FileCheck %s --implicit-check-not=omp.parallel
+
+! CHECK: #[[UNROLL:loop_unroll[0-9]*]] =
+! CHECK-SAME: #llvm.loop_unroll<disable = false, count = 4 : i64>
+! CHECK: #[[VECTORIZE:loop_vectorize[0-9]*]] =
+! CHECK-SAME: #llvm.loop_vectorize<disable = false>
+! CHECK: #[[UNROLL_ANNOTATION:loop_annotation[0-9]*]] =
+! CHECK-SAME: #llvm.loop_annotation<unroll = #[[UNROLL]]>
+! CHECK: #[[VECTOR_ANNOTATION:loop_annotation[0-9]*]] =
+! CHECK-SAME: #llvm.loop_annotation<vectorize = #[[VECTORIZE]]>
+
+! CHECK-LABEL: func.func @_QPtest_do(
+! CHECK-NOT:     {{omp\.(wsloop|simd|loop_nest)|fir\.do_loop}}
+! CHECK:         omp.wsloop private(@_QFtest_doEi_private_i32 {{.*}} ->
+! CHECK-SAME:      %[[DO_PRIVATE:.*]] : !fir.ref<i32>)
+! CHECK-NOT:     {{omp\.(wsloop|simd|loop_nest)|fir\.do_loop}}
+! CHECK:           omp.loop_nest (%[[DO_IV:.*]]) :
+! CHECK-NOT:     {{omp\.(wsloop|simd|loop_nest)|fir\.do_loop}}
+! CHECK:             %[[DO_PRIVATE_DECL:.*]]:2 =
+! CHECK-SAME:          hlfir.declare %[[DO_PRIVATE]]
+! CHECK-NOT:     {{omp\.(wsloop|simd|loop_nest)|fir\.do_loop}}
+! CHECK:             hlfir.assign %[[DO_IV]] to %[[DO_PRIVATE_DECL]]#0
+! CHECK-NOT:     {{omp\.(wsloop|simd|loop_nest)|fir\.do_loop}}
+! CHECK:             %[[DO_VALUE:.*]] =
+! CHECK-SAME:          fir.load %[[DO_PRIVATE_DECL]]#0
+! CHECK-NOT:     {{omp\.(wsloop|simd|loop_nest)|fir\.do_loop}}
+! CHECK:             %[[DO_INDEX:.*]] =
+! CHECK-SAME:          fir.load %[[DO_PRIVATE_DECL]]#0
+! CHECK-NOT:     {{omp\.(wsloop|simd|loop_nest)|fir\.do_loop}}
+! CHECK:             %[[DO_INDEX_I64:.*]] = fir.convert %[[DO_INDEX]]
+! CHECK-NOT:     {{omp\.(wsloop|simd|loop_nest)|fir\.do_loop}}
+! CHECK-NOT:         {{^ *}}}
+! CHECK:             %[[DO_ELEMENT:.*]] = hlfir.designate
+! CHECK-SAME:          (%[[DO_INDEX_I64]])
+! CHECK-NOT:     {{omp\.(wsloop|simd|loop_nest)|fir\.do_loop}}
+! CHECK-NOT:         {{^ *}}}
+! CHECK:             hlfir.assign %[[DO_VALUE]] to %[[DO_ELEMENT]]
+! CHECK-NOT:     {{omp\.(wsloop|simd|loop_nest)|fir\.do_loop}}
+! CHECK:             omp.yield
+! CHECK-NOT:     {{omp\.(wsloop|simd|loop_nest)|fir\.do_loop}}
+! CHECK:         return
+subroutine test_do(n, a)
+  integer :: n, a(n), i
+  !$omp metadirective &
+  !$omp & when(implementation={vendor(llvm)}: do) &
+  !$omp & otherwise(nothing)
+  do i = 1, n
+    a(i) = i
+  end do
+end subroutine
+
+! CHECK-LABEL: func.func @_QPtest_simd(
+! CHECK:         %[[SIMD_AFTER:.*]]:2 = hlfir.declare %arg2
+! CHECK-SAME:      uniq_name = "_QFtest_simdEafter"
+! CHECK-NOT:     {{omp\.(wsloop|simd|loop_nest)|fir\.do_loop}}
+! CHECK:         omp.simd linear(
+! CHECK-SAME:      val(%[[SIMD_LINEAR:.*]] : !fir.ref<i32> =
+! CHECK-NOT:     {{omp\.(wsloop|simd|loop_nest)|fir\.do_loop}}
+! CHECK:           omp.loop_nest (%[[SIMD_IV:.*]]) :
+! CHECK-NOT:     {{omp\.(wsloop|simd|loop_nest)|fir\.do_loop}}
+! CHECK:             hlfir.assign %[[SIMD_IV]] to %[[SIMD_LINEAR]]
+! CHECK-NOT:     {{omp\.(wsloop|simd|loop_nest)|fir\.do_loop}}
+! CHECK:             %[[SIMD_VALUE:.*]] = fir.load %[[SIMD_LINEAR]]
+! CHECK-NOT:     {{omp\.(wsloop|simd|loop_nest)|fir\.do_loop}}
+! CHECK:             %[[SIMD_INDEX:.*]] = fir.load %[[SIMD_LINEAR]]
+! CHECK-NOT:     {{omp\.(wsloop|simd|loop_nest)|fir\.do_loop}}
+! CHECK:             %[[SIMD_INDEX_I64:.*]] =
+! CHECK-SAME:          fir.convert %[[SIMD_INDEX]]
+! CHECK-NOT:     {{omp\.(wsloop|simd|loop_nest)|fir\.do_loop}}
+! CHECK-NOT:         {{^ *}}}
+! CHECK:             %[[SIMD_ELEMENT:.*]] = hlfir.designate
+! CHECK-SAME:          (%[[SIMD_INDEX_I64]])
+! CHECK-NOT:     {{omp\.(wsloop|simd|loop_nest)|fir\.do_loop}}
+! CHECK-NOT:         {{^ *}}}
+! CHECK:             hlfir.assign %[[SIMD_VALUE]] to %[[SIMD_ELEMENT]]
+! CHECK:             omp.yield
+! CHECK-NOT:     {{omp\.(wsloop|simd|loop_nest)|fir\.do_loop}}
+! CHECK:         %[[SIMD_AFTER_VALUE:.*]] = fir.load %[[SIMD_LINEAR]]
+! CHECK:         hlfir.assign %[[SIMD_AFTER_VALUE]] to %[[SIMD_AFTER]]#0
+! CHECK-NOT:     {{omp\.(wsloop|simd|loop_nest)|fir\.do_loop}}
+! CHECK:         return
+subroutine test_simd(n, a, after)
+  integer :: n, a(n), after, i
+  !$omp metadirective &
+  !$omp & when(implementation={vendor(llvm)}: simd) &
+  !$omp & otherwise(nothing)
+  do i = 1, n
+    a(i) = i
+  end do
+  after = i
+end subroutine
+
+! CHECK-LABEL: func.func @_QPtest_do_simd(
+! CHECK-NOT:     {{omp\.(wsloop|simd|loop_nest)|fir\.do_loop}}
+! CHECK:         omp.wsloop
+! CHECK-NOT:     {{omp\.(wsloop|simd|loop_nest)|fir\.do_loop}}
+! CHECK:           omp.simd linear(
+! CHECK-SAME:        val(%[[DO_SIMD_LINEAR:.*]] : !fir.ref<i32> =
+! CHECK-NOT:     {{omp\.(wsloop|simd|loop_nest)|fir\.do_loop}}
+! CHECK:             omp.loop_nest (%[[DO_SIMD_IV:.*]]) :
+! CHECK-NOT:     {{omp\.(wsloop|simd|loop_nest)|fir\.do_loop}}
+! CHECK:               hlfir.assign %[[DO_SIMD_IV]] to
+! CHECK-SAME:            %[[DO_SIMD_LINEAR]]
+! CHECK-NOT:     {{omp\.(wsloop|simd|loop_nest)|fir\.do_loop}}
+! CHECK:               %[[DO_SIMD_VALUE:.*]] =
+! CHECK-SAME:            fir.load %[[DO_SIMD_LINEAR]]
+! CHECK-NOT:     {{omp\.(wsloop|simd|loop_nest)|fir\.do_loop}}
+! CHECK:               %[[DO_SIMD_INDEX:.*]] =
+! CHECK-SAME:            fir.load %[[DO_SIMD_LINEAR]]
+! CHECK-NOT:     {{omp\.(wsloop|simd|loop_nest)|fir\.do_loop}}
+! CHECK:               %[[DO_SIMD_INDEX_I64:.*]] =
+! CHECK-SAME:            fir.convert %[[DO_SIMD_INDEX]]
+! CHECK-NOT:     {{omp\.(wsloop|simd|loop_nest)|fir\.do_loop}}
+! CHECK-NOT:           {{^ *}}}
+! CHECK:               %[[DO_SIMD_ELEMENT:.*]] = hlfir.designate
+! CHECK-SAME:            (%[[DO_SIMD_INDEX_I64]])
+! CHECK-NOT:     {{omp\.(wsloop|simd|loop_nest)|fir\.do_loop}}
+! CHECK-NOT:           {{^ *}}}
+! CHECK:               hlfir.assign %[[DO_SIMD_VALUE]] to
+! CHECK-SAME:            %[[DO_SIMD_ELEMENT]]
+! CHECK:               omp.yield
+! CHECK-NOT:     {{omp\.(wsloop|simd|loop_nest)|fir\.do_loop}}
+! CHECK:         return
+subroutine test_do_simd(n, a)
+  integer :: n, a(n), i
+  !$omp metadirective &
+  !$omp & when(implementation={vendor(llvm)}: do simd) &
+  !$omp & otherwise(nothing)
+  do i = 1, n
+    a(i) = i
+  end do
+end subroutine
+
+! CHECK-LABEL: func.func @_QPtest_begin_do(
+! CHECK-NOT:     {{omp\.(wsloop|simd|loop_nest)|fir\.do_loop}}
+! CHECK:         fir.if {{.*}} {
+! CHECK-NOT:       {{omp\.(wsloop|simd|loop_nest)|fir\.do_loop}}
+! CHECK:           omp.wsloop private(@_QFtest_begin_doEi_private_i32 {{.*}} ->
+! CHECK-SAME:        %[[BEGIN_DO_PRIVATE:.*]] : !fir.ref<i32>)
+! CHECK-NOT:       {{omp\.(wsloop|simd|loop_nest)|fir\.do_loop}}
+! CHECK:             omp.loop_nest (%[[BEGIN_DO_IV:.*]]) :
+! CHECK-NOT:       {{omp\.(wsloop|simd|loop_nest)|fir\.do_loop}}
+! CHECK:               %[[BEGIN_DO_PRIVATE_DECL:.*]]:2 =
+! CHECK-SAME:            hlfir.declare %[[BEGIN_DO_PRIVATE]]
+! CHECK-NOT:       {{omp\.(wsloop|simd|loop_nest)|fir\.do_loop}}
+! CHECK:               hlfir.assign %[[BEGIN_DO_IV]] to
+! CHECK-SAME:            %[[BEGIN_DO_PRIVATE_DECL]]#0
+! CHECK-NOT:       {{omp\.(wsloop|simd|loop_nest)|fir\.do_loop}}
+! CHECK:               %[[BEGIN_DO_VALUE:.*]] =
+! CHECK-SAME:            fir.load %[[BEGIN_DO_PRIVATE_DECL]]#0
+! CHECK-NOT:       {{omp\.(wsloop|simd|loop_nest)|fir\.do_loop}}
+! CHECK:               %[[BEGIN_DO_INDEX:.*]] =
+! CHECK-SAME:            fir.load %[[BEGIN_DO_PRIVATE_DECL]]#0
+! CHECK-NOT:       {{omp\.(wsloop|simd|loop_nest)|fir\.do_loop}}
+! CHECK:               %[[BEGIN_DO_INDEX_I64:.*]] =
+! CHECK-SAME:            fir.convert %[[BEGIN_DO_INDEX]]
+! CHECK-NOT:       {{omp\.(wsloop|simd|loop_nest)|fir\.do_loop}}
+! CHECK-NOT:           {{^ *}}}
+! CHECK:               %[[BEGIN_DO_ELEMENT:.*]] = hlfir.designate
+! CHECK-SAME:            (%[[BEGIN_DO_INDEX_I64]])
+! CHECK-NOT:       {{omp\.(wsloop|simd|loop_nest)|fir\.do_loop}}
+! CHECK-NOT:           {{^ *}}}
+! CHECK:               hlfir.assign %[[BEGIN_DO_VALUE]] to
+! CHECK-SAME:            %[[BEGIN_DO_ELEMENT]]
+! CHECK:               omp.yield
+! CHECK-NOT:       {{omp\.(wsloop|simd|loop_nest)|fir\.do_loop}}
+! CHECK:         } else {
+! CHECK-NOT:       omp.
+! CHECK-NOT:       fir.do_loop
+! CHECK:           fir.do_loop %[[BEGIN_DO_FALLBACK_IV:.*]] =
+! CHECK-NOT:       omp.
+! CHECK-NOT:       fir.do_loop
+! CHECK-NOT:       {{^ *}}}
+! CHECK:             fir.store %[[BEGIN_DO_FALLBACK_IV]] to
+! CHECK-SAME:          %[[BEGIN_DO_FALLBACK_IV_ADDR:.*]]
+! CHECK-NOT:       omp.
+! CHECK-NOT:       fir.do_loop
+! CHECK-NOT:       {{^ *}}}
+! CHECK:             %[[BEGIN_DO_FALLBACK_VALUE:.*]] =
+! CHECK-SAME:          fir.load %[[BEGIN_DO_FALLBACK_IV_ADDR]]
+! CHECK-NOT:       omp.
+! CHECK-NOT:       fir.do_loop
+! CHECK-NOT:       {{^ *}}}
+! CHECK:             %[[BEGIN_DO_FALLBACK_INDEX:.*]] =
+! CHECK-SAME:          fir.load %[[BEGIN_DO_FALLBACK_IV_ADDR]]
+! CHECK-NOT:       omp.
+! CHECK-NOT:       fir.do_loop
+! CHECK-NOT:       {{^ *}}}
+! CHECK:             %[[BEGIN_DO_FALLBACK_INDEX_I64:.*]] =
+! CHECK-SAME:          fir.convert %[[BEGIN_DO_FALLBACK_INDEX]]
+! CHECK-NOT:       omp.
+! CHECK-NOT:       fir.do_loop
+! CHECK-NOT:       {{^ *}}}
+! CHECK:             %[[BEGIN_DO_FALLBACK_ELEMENT:.*]] =
+! CHECK-SAME:          hlfir.designate
+! CHECK-SAME:          (%[[BEGIN_DO_FALLBACK_INDEX_I64]])
+! CHECK-NOT:       omp.
+! CHECK-NOT:       fir.do_loop
+! CHECK-NOT:       {{^ *}}}
+! CHECK:             hlfir.assign %[[BEGIN_DO_FALLBACK_VALUE]] to
+! CHECK-SAME:          %[[BEGIN_DO_FALLBACK_ELEMENT]]
+! CHECK-NOT:       omp.
+! CHECK-NOT:       fir.do_loop
+! CHECK:         }
+! CHECK-NOT:     fir.do_loop
+! CHECK-NOT:     omp.
+! CHECK:         return
+subroutine test_begin_do(flag, n, a)
+  logical, intent(in) :: flag
+  integer :: n, a(n), i
+  !$omp begin metadirective &
+  !$omp & when(user={condition(flag)}: do) &
+  !$omp & otherwise(nothing)
+  do i = 1, n
+    a(i) = i
+  end do
+  !$omp end metadirective
+end subroutine
+
+! The following loop must remain available when the PFT is reused for ENTRY.
+! CHECK-LABEL: func.func @_QPtest_standalone_entry_no_directive(
+! CHECK-NOT:     {{omp\.(wsloop|simd|loop_nest)|fir\.do_loop}}
+! CHECK:         fir.if {{.*}} {
+! CHECK-NOT:       {{omp\.(wsloop|simd|loop_nest)|fir\.do_loop}}
+! CHECK:           omp.wsloop private({{.*}}Ei_private_i32 {{.*}} ->
+! CHECK-SAME:        %[[ENTRY_PRIVATE:.*]] : !fir.ref<i32>)
+! CHECK-NOT:       {{omp\.(wsloop|simd|loop_nest)|fir\.do_loop}}
+! CHECK:             omp.loop_nest (%[[ENTRY_IV:.*]]) :
+! CHECK:               %[[ENTRY_PRIVATE_DECL:.*]]:2 =
+! CHECK-SAME:            hlfir.declare %[[ENTRY_PRIVATE]]
+! CHECK:               hlfir.assign %[[ENTRY_IV]] to
+! CHECK-SAME:            %[[ENTRY_PRIVATE_DECL]]#0
+! CHECK-NOT:       {{omp\.(wsloop|simd|loop_nest)|fir\.do_loop}}
+! CHECK:               %[[ENTRY_VALUE:.*]] =
+! CHECK-SAME:            fir.load %[[ENTRY_PRIVATE_DECL]]#0
+! CHECK-NOT:       {{omp\.(wsloop|simd|loop_nest)|fir\.do_loop}}
+! CHECK:               %[[ENTRY_INDEX:.*]] =
+! CHECK-SAME:            fir.load %[[ENTRY_PRIVATE_DECL]]#0
+! CHECK-NOT:       {{omp\.(wsloop|simd|loop_nest)|fir\.do_loop}}
+! CHECK:               %[[ENTRY_INDEX_I64:.*]] =
+! CHECK-SAME:            fir.convert %[[ENTRY_INDEX]]
+! CHECK-NOT:       {{omp\.(wsloop|simd|loop_nest)|fir\.do_loop}}
+! CHECK-NOT:         {{^ *}}}
+! CHECK:               %[[ENTRY_ELEMENT:.*]] = hlfir.designate
+! CHECK-SAME:            (%[[ENTRY_INDEX_I64]])
+! CHECK-NOT:       {{omp\.(wsloop|simd|loop_nest)|fir\.do_loop}}
+! CHECK-NOT:         {{^ *}}}
+! CHECK:               hlfir.assign %[[ENTRY_VALUE]] to %[[ENTRY_ELEMENT]]
+! CHECK:               omp.yield
+! CHECK-NOT:       {{omp\.(wsloop|simd|loop_nest)|fir\.do_loop}}
+! CHECK:         } else {
+! CHECK-NOT:       omp.
+! CHECK-NOT:       fir.do_loop
+! CHECK:           fir.do_loop %[[ENTRY_FALLBACK_IV:.*]] =
+! CHECK-NOT:       omp.
+! CHECK-NOT:       fir.do_loop
+! CHECK-NOT:       {{^ *}}}
+! CHECK:             fir.store %[[ENTRY_FALLBACK_IV]] to
+! CHECK-SAME:          %[[ENTRY_FALLBACK_IV_ADDR:.*]]
+! CHECK-NOT:       omp.
+! CHECK-NOT:       fir.do_loop
+! CHECK-NOT:       {{^ *}}}
+! CHECK:             %[[ENTRY_FALLBACK_VALUE:.*]] =
+! CHECK-SAME:          fir.load %[[ENTRY_FALLBACK_IV_ADDR]]
+! CHECK-NOT:       omp.
+! CHECK-NOT:       fir.do_loop
+! CHECK-NOT:       {{^ *}}}
+! CHECK:             %[[ENTRY_FALLBACK_INDEX:.*]] =
+! CHECK-SAME:          fir.load %[[ENTRY_FALLBACK_IV_ADDR]]
+! CHECK-NOT:       omp.
+! CHECK-NOT:       fir.do_loop
+! CHECK-NOT:       {{^ *}}}
+! CHECK:             %[[ENTRY_FALLBACK_INDEX_I64:.*]] =
+! CHECK-SAME:          fir.convert %[[ENTRY_FALLBACK_INDEX]]
+! CHECK-NOT:       omp.
+! CHECK-NOT:       fir.do_loop
+! CHECK-NOT:       {{^ *}}}
+! CHECK:             %[[ENTRY_FALLBACK_ELEMENT:.*]] =
+! CHECK-SAME:          hlfir.designate
+! CHECK-SAME:          (%[[ENTRY_FALLBACK_INDEX_I64]])
+! CHECK-NOT:       omp.
+! CHECK-NOT:       fir.do_loop
+! CHECK-NOT:       {{^ *}}}
+! CHECK:             hlfir.assign %[[ENTRY_FALLBACK_VALUE]] to
+! CHECK-SAME:          %[[ENTRY_FALLBACK_ELEMENT]]
+! CHECK-NOT:       omp.
+! CHECK-NOT:       fir.do_loop
+! CHECK:         }
+! CHECK-NOT:     fir.do_loop
+! CHECK-NOT:     omp.
+! CHECK:         return
+! CHECK-LABEL: func.func @_QPtest_alt_standalone_entry_no_directive(
+! CHECK-NOT:     {{omp\.(wsloop|simd|loop_nest)|fir\.do_loop}}
+! CHECK:         fir.if {{.*}} {
+! CHECK-NOT:       {{omp\.(wsloop|simd|loop_nest)|fir\.do_loop}}
+! CHECK:           omp.wsloop private({{.*}}Ei_private_i32 {{.*}} ->
+! CHECK-SAME:        %[[ALT_ENTRY_PRIVATE:.*]] : !fir.ref<i32>)
+! CHECK-NOT:       {{omp\.(wsloop|simd|loop_nest)|fir\.do_loop}}
+! CHECK:             omp.loop_nest (%[[ALT_ENTRY_IV:.*]]) :
+! CHECK:               %[[ALT_ENTRY_PRIVATE_DECL:.*]]:2 =
+! CHECK-SAME:            hlfir.declare %[[ALT_ENTRY_PRIVATE]]
+! CHECK:               hlfir.assign %[[ALT_ENTRY_IV]] to
+! CHECK-SAME:            %[[ALT_ENTRY_PRIVATE_DECL]]#0
+! CHECK-NOT:       {{omp\.(wsloop|simd|loop_nest)|fir\.do_loop}}
+! CHECK:               %[[ALT_ENTRY_VALUE:.*]] =
+! CHECK-SAME:            fir.load %[[ALT_ENTRY_PRIVATE_DECL]]#0
+! CHECK-NOT:       {{omp\.(wsloop|simd|loop_nest)|fir\.do_loop}}
+! CHECK:               %[[ALT_ENTRY_INDEX:.*]] =
+! CHECK-SAME:            fir.load %[[ALT_ENTRY_PRIVATE_DECL]]#0
+! CHECK-NOT:       {{omp\.(wsloop|simd|loop_nest)|fir\.do_loop}}
+! CHECK:               %[[ALT_ENTRY_INDEX_I64:.*]] =
+! CHECK-SAME:            fir.convert %[[ALT_ENTRY_INDEX]]
+! CHECK-NOT:       {{omp\.(wsloop|simd|loop_nest)|fir\.do_loop}}
+! CHECK-NOT:         {{^ *}}}
+! CHECK:               %[[ALT_ENTRY_ELEMENT:.*]] = hlfir.designate
+! CHECK-SAME:            (%[[ALT_ENTRY_INDEX_I64]])
+! CHECK-NOT:       {{omp\.(wsloop|simd|loop_nest)|fir\.do_loop}}
+! CHECK-NOT:         {{^ *}}}
+! CHECK:               hlfir.assign %[[ALT_ENTRY_VALUE]] to
+! CHECK-SAME:            %[[ALT_ENTRY_ELEMENT]]
+! CHECK:               omp.yield
+! CHECK-NOT:       {{omp\.(wsloop|simd|loop_nest)|fir\.do_loop}}
+! CHECK:         } else {
+! CHECK-NOT:       omp.
+! CHECK-NOT:       fir.do_loop
+! CHECK:           fir.do_loop %[[ALT_ENTRY_FALLBACK_IV:.*]] =
+! CHECK-NOT:       omp.
+! CHECK-NOT:       fir.do_loop
+! CHECK-NOT:       {{^ *}}}
+! CHECK:             fir.store %[[ALT_ENTRY_FALLBACK_IV]] to
+! CHECK-SAME:          %[[ALT_ENTRY_FALLBACK_IV_ADDR:.*]]
+! CHECK-NOT:       omp.
+! CHECK-NOT:       fir.do_loop
+! CHECK-NOT:       {{^ *}}}
+! CHECK:             %[[ALT_ENTRY_FALLBACK_VALUE:.*]] =
+! CHECK-SAME:          fir.load %[[ALT_ENTRY_FALLBACK_IV_ADDR]]
+! CHECK-NOT:       omp.
+! CHECK-NOT:       fir.do_loop
+! CHECK-NOT:       {{^ *}}}
+! CHECK:             %[[ALT_ENTRY_FALLBACK_INDEX:.*]] =
+! CHECK-SAME:          fir.load %[[ALT_ENTRY_FALLBACK_IV_ADDR]]
+! CHECK-NOT:       omp.
+! CHECK-NOT:       fir.do_loop
+! CHECK-NOT:       {{^ *}}}
+! CHECK:             %[[ALT_ENTRY_FALLBACK_INDEX_I64:.*]] =
+! CHECK-SAME:          fir.convert %[[ALT_ENTRY_FALLBACK_INDEX]]
+! CHECK-NOT:       omp.
+! CHECK-NOT:       fir.do_loop
+! CHECK-NOT:       {{^ *}}}
+! CHECK:             %[[ALT_ENTRY_FALLBACK_ELEMENT:.*]] =
+! CHECK-SAME:          hlfir.designate
+! CHECK-SAME:          (%[[ALT_ENTRY_FALLBACK_INDEX_I64]])
+! CHECK-NOT:       omp.
+! CHECK-NOT:       fir.do_loop
+! CHECK-NOT:       {{^ *}}}
+! CHECK:             hlfir.assign %[[ALT_ENTRY_FALLBACK_VALUE]] to
+! CHECK-SAME:          %[[ALT_ENTRY_FALLBACK_ELEMENT]]
+! CHECK-NOT:       omp.
+! CHECK-NOT:       fir.do_loop
+! CHECK:         }
+! CHECK-NOT:     fir.do_loop
+! CHECK-NOT:     omp.
+! CHECK:         return
+! CHECK-LABEL: func.func @_QPtest_after_standalone_entry_no_directive(
+! CHECK-NOT:     fir.if
+! CHECK-NOT:     omp.
+! CHECK-NOT:     fir.do_loop
+! CHECK:         %[[AFTER_ENTRY_C77:.*]] = arith.constant 77 : i32
+! CHECK:         hlfir.assign %[[AFTER_ENTRY_C77]]
+! CHECK:         return
+subroutine test_standalone_entry_no_directive(flag, n, a)
+  logical, intent(in) :: flag
+  integer :: n, a(n), i
+  entry test_alt_standalone_entry_no_directive(flag, n, a)
+  !$omp metadirective &
+  !$omp & when(user={condition(flag)}: do) &
+  !$omp & otherwise(nothing)
+  do i = 1, n
+    a(i) = i
+  end do
+  entry test_after_standalone_entry_no_directive(n, a)
+  a(1) = 77
+end subroutine
+
+! A statically inapplicable loop variant nested in a parallel region leaves the
+! following loop sequential.
+! CHECK-LABEL: func.func @_QPtest_inapplicable_do_in_parallel(
+! CHECK:         omp.parallel
+! CHECK-NOT:       {{omp\.(wsloop|simd|loop_nest)}}
+! CHECK:           fir.do_loop
+! CHECK-NOT:       {{omp\.(wsloop|simd|loop_nest)|fir\.do_loop}}
+! CHECK-NOT:         {{^ *}}}
+! CHECK:             %[[INAPPLICABLE_ELEMENT:.*]] = hlfir.designate
+! CHECK-NOT:       {{omp\.(wsloop|simd|loop_nest)|fir\.do_loop}}
+! CHECK-NOT:         {{^ *}}}
+! CHECK:             hlfir.assign {{.*}} to %[[INAPPLICABLE_ELEMENT]]
+! CHECK-NOT:       {{omp\.(wsloop|simd|loop_nest)|fir\.do_loop}}
+! CHECK:           omp.terminator
+! CHECK:         return
+subroutine test_inapplicable_do_in_parallel(n, a, after)
+  integer :: n, a(n), after, i
+  !$omp parallel num_threads(1) shared(n, a, after)
+  !$omp metadirective &
+  !$omp & when(implementation={vendor("unknown")}: do) &
+  !$omp & otherwise(nothing)
+  do i = 1, n
+    a(i) = i
+  end do
+  after = i
+  !$omp end parallel
+end subroutine
+
+! An unreachable loop variant likewise does not turn a statically selected
+! block variant into a mixed-association metadirective.
+! CHECK-LABEL: func.func @_QPtest_unselected_do_with_block_variant(
+! CHECK-NOT:     {{omp\.(wsloop|simd|loop_nest)}}
+! CHECK:         omp.masked
+! CHECK-NOT:       {{omp\.(wsloop|simd|loop_nest)}}
+! CHECK:           fir.do_loop
+! CHECK-NOT:       {{omp\.(wsloop|simd|loop_nest)|fir\.do_loop}}
+! CHECK-NOT:         {{^ *}}}
+! CHECK:             %[[UNSELECTED_ELEMENT:.*]] = hlfir.designate
+! CHECK-NOT:       {{omp\.(wsloop|simd|loop_nest)|fir\.do_loop}}
+! CHECK-NOT:         {{^ *}}}
+! CHECK:             hlfir.assign {{.*}} to %[[UNSELECTED_ELEMENT]]
+! CHECK-NOT:       {{omp\.(wsloop|simd|loop_nest)|fir\.do_loop}}
+! CHECK:           omp.terminator
+! CHECK:         return
+subroutine test_unselected_do_with_block_variant(n, a)
+  integer :: n, a(n), i
+  !$omp begin metadirective &
+  !$omp & when(user={condition(score(2): .true.)}: masked) &
+  !$omp & when(user={condition(score(1): .true.)}: do) &
+  !$omp & otherwise(nothing)
+  do i = 1, n
+    a(i) = i
+  end do
+  !$omp end metadirective
+end subroutine
+
+! A lower-ranked candidate guarded by the same runtime expression is
+! unreachable: when FLAG is true the higher-ranked BARRIER wins, and when it
+! is false neither guarded candidate matches. Do not emit a dead OpenMP loop.
+! CHECK-LABEL: func.func @_QPtest_unreachable_same_runtime_condition(
+! CHECK-NOT:     {{omp\.(wsloop|simd|loop_nest)}}
+! CHECK:         fir.if {{.*}} {
+! CHECK:           omp.barrier
+! CHECK-NOT:       {{omp\.(wsloop|simd|loop_nest)}}
+! CHECK:         } else {
+! CHECK-NOT:       {{omp\.(wsloop|simd|loop_nest)}}
+! CHECK:         }
+! CHECK:         fir.do_loop
+! CHECK-NOT:     {{omp\.(wsloop|simd|loop_nest)|fir\.do_loop}}
+! CHECK-NOT:       {{^ *}}}
+! CHECK:           %[[UNREACHABLE_ELEMENT:.*]] = hlfir.designate
+! CHECK-NOT:     {{omp\.(wsloop|simd|loop_nest)|fir\.do_loop}}
+! CHECK-NOT:       {{^ *}}}
+! CHECK:           hlfir.assign {{.*}} to %[[UNREACHABLE_ELEMENT]]
+! CHECK-NOT:     {{omp\.(wsloop|simd|loop_nest)|fir\.do_loop}}
+! CHECK:         return
+subroutine test_unreachable_same_runtime_condition(flag, n, a)
+  logical, intent(in) :: flag
+  integer :: n, a(n), i
+  !$omp metadirective &
+  !$omp & when(user={condition(score(2): flag)}: barrier) &
+  !$omp & when(user={condition(score(1): flag)}: do) &
+  !$omp & otherwise(nothing)
+  do i = 1, n
+    a(i) = i
+  end do
+end subroutine
+
+! Parentheses do not make a repeatable condition distinct. The lower-ranked
+! loop remains unreachable and must not be emitted.
+! CHECK-LABEL: func.func @_QPtest_unreachable_parenthesized_condition(
+! CHECK-NOT:     {{omp\.(wsloop|simd|loop_nest)}}
+! CHECK:         fir.if {{.*}} {
+! CHECK:           omp.barrier
+! CHECK:         } else {
+! CHECK-NOT:       {{omp\.(wsloop|simd|loop_nest)}}
+! CHECK:         }
+! CHECK:         fir.do_loop
+! CHECK-NOT:     {{omp\.(wsloop|simd|loop_nest)}}
+! CHECK:         return
+subroutine test_unreachable_parenthesized_condition(flag, n, a)
+  logical, intent(in) :: flag
+  integer :: n, a(n), i
+  !$omp metadirective &
+  !$omp & when(user={condition(score(2): flag)}: barrier) &
+  !$omp & when(user={condition(score(1): (flag))}: do) &
+  !$omp & otherwise(nothing)
+  do i = 1, n
+    a(i) = i
+  end do
+end subroutine
+
+! Idempotent AND/OR spelling is normalized after proving that the condition is
+! repeatable.
+! CHECK-LABEL: func.func @_QPtest_unreachable_idempotent_condition(
+! CHECK-NOT:     {{omp\.(wsloop|simd|loop_nest)}}
+! CHECK:         fir.if {{.*}} {
+! CHECK:           omp.barrier
+! CHECK:         } else {
+! CHECK-NOT:       {{omp\.(wsloop|simd|loop_nest)}}
+! CHECK:         }
+! CHECK:         fir.do_loop
+! CHECK-NOT:     {{omp\.(wsloop|simd|loop_nest)}}
+! CHECK:         return
+subroutine test_unreachable_idempotent_condition(flag, n, a)
+  logical, intent(in) :: flag
+  integer :: n, a(n), i
+  !$omp metadirective &
+  !$omp & when(user={condition(score(2): flag)}: barrier) &
+  !$omp & when(user={condition(score(1): flag .or. flag)}: do) &
+  !$omp & otherwise(nothing)
+  do i = 1, n
+    a(i) = i
+  end do
+end subroutine
+
+! Calls to an opaque procedure are independent runtime conditions even when
+! their source expressions are identical. Preserve both candidates without
+! relying on clause-expression side effects.
+! CHECK-LABEL: func.func @_QPtest_opaque_runtime_conditions(
+! CHECK:         fir.call @_QPmetadirective_runtime_condition
+! CHECK:         fir.if {{.*}} {
+! CHECK:           omp.barrier
+! CHECK:         } else {
+! CHECK:           fir.call @_QPmetadirective_runtime_condition
+! CHECK:           fir.if {{.*}} {
+! CHECK:             omp.wsloop
+! CHECK:               omp.loop_nest
+! CHECK:           } else {
+! CHECK:             fir.do_loop
+! CHECK:           }
+! CHECK:         }
+! CHECK:         return
+subroutine test_opaque_runtime_conditions(n, a)
+  integer :: n, a(n), i
+  logical :: metadirective_runtime_condition
+  external :: metadirective_runtime_condition
+  !$omp metadirective &
+  !$omp & when(user={condition(score(2): metadirective_runtime_condition())}: barrier) &
+  !$omp & when(user={condition(score(1): metadirective_runtime_condition())}: do) &
+  !$omp & otherwise(nothing)
+  do i = 1, n
+    a(i) = i
+  end do
+end subroutine
+
+module metadirective_condition_helpers
+contains
+  pure logical function metadirective_identity(value)
+    logical, intent(in) :: value
+    metadirective_identity = value
+  end function
+end module
+
+! Procedure calls are conservatively kept as independent runtime conditions
+! because the expression tree does not describe the callee's state.
+! CHECK-LABEL: func.func @_QPtest_pure_runtime_conditions(
+! CHECK:         fir.call @_QMmetadirective_condition_helpersPmetadirective_identity
+! CHECK:         fir.if {{.*}} {
+! CHECK:           omp.wsloop
+! CHECK:             omp.loop_nest
+! CHECK:         } else {
+! CHECK:           fir.call @_QMmetadirective_condition_helpersPmetadirective_identity
+! CHECK:           fir.if {{.*}} {
+! CHECK:             omp.simd
+! CHECK:               omp.loop_nest
+! CHECK:           } else {
+! CHECK:             fir.do_loop
+! CHECK:           }
+! CHECK:         }
+! CHECK:         return
+subroutine test_pure_runtime_conditions(flag, n, a)
+  use metadirective_condition_helpers, only : metadirective_identity
+  logical, intent(in) :: flag
+  integer :: n, a(n), i
+  !$omp metadirective &
+  !$omp & when(user={condition(score(2): &
+  !$omp &   metadirective_identity(flag))}: do) &
+  !$omp & when(user={condition(score(1): &
+  !$omp &   metadirective_identity(flag))}: simd) &
+  !$omp & otherwise(nothing)
+  do i = 1, n
+    a(i) = i
+  end do
+end subroutine
+
+! CHECK-LABEL: func.func @_QPtest_dynamic_loop(
+! CHECK-NOT:     {{omp\.(wsloop|simd|loop_nest)|fir\.do_loop}}
+! CHECK:         fir.if {{.*}} {
+! CHECK-NOT:     {{omp\.(wsloop|simd|loop_nest)|fir\.do_loop}}
+! CHECK:           omp.wsloop private({{.*}} ->
+! CHECK-SAME:        %[[DYNAMIC_DO_PRIVATE:.*]] : !fir.ref<i32>)
+! CHECK-NOT:     {{omp\.(wsloop|simd|loop_nest)|fir\.do_loop}}
+! CHECK:             omp.loop_nest (%[[DYNAMIC_DO_IV:.*]]) :
+! CHECK-NOT:     {{omp\.(wsloop|simd|loop_nest)|fir\.do_loop}}
+! CHECK:               %[[DYNAMIC_DO_DECL:.*]]:2 =
+! CHECK-SAME:            hlfir.declare %[[DYNAMIC_DO_PRIVATE]]
+! CHECK:               hlfir.assign %[[DYNAMIC_DO_IV]] to
+! CHECK-SAME:            %[[DYNAMIC_DO_DECL]]#0
+! CHECK-NOT:     {{omp\.(wsloop|simd|loop_nest)|fir\.do_loop}}
+! CHECK:               %[[DYNAMIC_DO_VALUE:.*]] =
+! CHECK-SAME:            fir.load %[[DYNAMIC_DO_DECL]]#0
+! CHECK-NOT:     {{omp\.(wsloop|simd|loop_nest)|fir\.do_loop}}
+! CHECK:               %[[DYNAMIC_DO_INDEX:.*]] =
+! CHECK-SAME:            fir.load %[[DYNAMIC_DO_DECL]]#0
+! CHECK-NOT:     {{omp\.(wsloop|simd|loop_nest)|fir\.do_loop}}
+! CHECK:               %[[DYNAMIC_DO_INDEX_I64:.*]] =
+! CHECK-SAME:            fir.convert %[[DYNAMIC_DO_INDEX]]
+! CHECK-NOT:     {{omp\.(wsloop|simd|loop_nest)|fir\.do_loop}}
+! CHECK-NOT:           {{^ *}}}
+! CHECK:               %[[DYNAMIC_DO_ELEMENT:.*]] = hlfir.designate
+! CHECK-SAME:            (%[[DYNAMIC_DO_INDEX_I64]])
+! CHECK-NOT:     {{omp\.(wsloop|simd|loop_nest)|fir\.do_loop}}
+! CHECK-NOT:           {{^ *}}}
+! CHECK:               hlfir.assign %[[DYNAMIC_DO_VALUE]] to
+! CHECK-SAME:            %[[DYNAMIC_DO_ELEMENT]]
+! CHECK:               omp.yield
+! CHECK-NOT:     {{omp\.(wsloop|simd|loop_nest)|fir\.do_loop}}
+! CHECK:         } else {
+! CHECK-NOT:     {{omp\.(wsloop|simd|loop_nest)|fir\.do_loop}}
+! CHECK:           omp.simd linear(
+! CHECK-SAME:        val(%[[DYNAMIC_SIMD_LINEAR:.*]] : !fir.ref<i32> =
+! CHECK-NOT:     {{omp\.(wsloop|simd|loop_nest)|fir\.do_loop}}
+! CHECK:             omp.loop_nest (%[[DYNAMIC_SIMD_IV:.*]]) :
+! CHECK-NOT:     {{omp\.(wsloop|simd|loop_nest)|fir\.do_loop}}
+! CHECK:               hlfir.assign %[[DYNAMIC_SIMD_IV]] to
+! CHECK-SAME:            %[[DYNAMIC_SIMD_LINEAR]]
+! CHECK-NOT:     {{omp\.(wsloop|simd|loop_nest)|fir\.do_loop}}
+! CHECK:               %[[DYNAMIC_SIMD_VALUE:.*]] =
+! CHECK-SAME:            fir.load %[[DYNAMIC_SIMD_LINEAR]]
+! CHECK-NOT:     {{omp\.(wsloop|simd|loop_nest)|fir\.do_loop}}
+! CHECK:               %[[DYNAMIC_SIMD_INDEX:.*]] =
+! CHECK-SAME:            fir.load %[[DYNAMIC_SIMD_LINEAR]]
+! CHECK-NOT:     {{omp\.(wsloop|simd|loop_nest)|fir\.do_loop}}
+! CHECK:               %[[DYNAMIC_SIMD_INDEX_I64:.*]] =
+! CHECK-SAME:            fir.convert %[[DYNAMIC_SIMD_INDEX]]
+! CHECK-NOT:     {{omp\.(wsloop|simd|loop_nest)|fir\.do_loop}}
+! CHECK-NOT:           {{^ *}}}
+! CHECK:               %[[DYNAMIC_SIMD_ELEMENT:.*]] = hlfir.designate
+! CHECK-SAME:            (%[[DYNAMIC_SIMD_INDEX_I64]])
+! CHECK-NOT:     {{omp\.(wsloop|simd|loop_nest)|fir\.do_loop}}
+! CHECK-NOT:           {{^ *}}}
+! CHECK:               hlfir.assign %[[DYNAMIC_SIMD_VALUE]] to
+! CHECK-SAME:            %[[DYNAMIC_SIMD_ELEMENT]]
+! CHECK:               omp.yield
+! CHECK-NOT:     {{omp\.(wsloop|simd|loop_nest)|fir\.do_loop}}
+! CHECK:         return
+subroutine test_dynamic_loop(flag, n, a)
+  logical, intent(in) :: flag
+  integer :: n, a(n), i
+  !$omp metadirective &
+  !$omp & when(user={condition(flag)}: do) &
+  !$omp & otherwise(simd)
+  do i = 1, n
+    a(i) = i
+  end do
+end subroutine
+
+! When the standalone fallback is selected at runtime, the following loop is
+! lowered sequentially in that arm.
+! CHECK-LABEL: func.func @_QPtest_dynamic_standalone_fallback(
+! CHECK-NOT:     {{omp\.(wsloop|simd|loop_nest)|fir\.do_loop}}
+! CHECK:         fir.if {{.*}} {
+! CHECK-NOT:     {{omp\.(wsloop|simd|loop_nest)|fir\.do_loop}}
+! CHECK:           omp.wsloop private({{.*}} ->
+! CHECK-SAME:        %[[STANDALONE_PRIVATE:.*]] : !fir.ref<i32>)
+! CHECK-NOT:     {{omp\.(wsloop|simd|loop_nest)|fir\.do_loop}}
+! CHECK:             omp.loop_nest (%[[STANDALONE_IV:.*]]) :
+! CHECK-NOT:     {{omp\.(wsloop|simd|loop_nest)|fir\.do_loop}}
+! CHECK:               %[[STANDALONE_DECL:.*]]:2 =
+! CHECK-SAME:            hlfir.declare %[[STANDALONE_PRIVATE]]
+! CHECK:               hlfir.assign %[[STANDALONE_IV]] to
+! CHECK-SAME:            %[[STANDALONE_DECL]]#0
+! CHECK-NOT:     {{omp\.(wsloop|simd|loop_nest)|fir\.do_loop}}
+! CHECK:               %[[STANDALONE_VALUE:.*]] =
+! CHECK-SAME:            fir.load %[[STANDALONE_DECL]]#0
+! CHECK-NOT:     {{omp\.(wsloop|simd|loop_nest)|fir\.do_loop}}
+! CHECK:               %[[STANDALONE_INDEX:.*]] =
+! CHECK-SAME:            fir.load %[[STANDALONE_DECL]]#0
+! CHECK-NOT:     {{omp\.(wsloop|simd|loop_nest)|fir\.do_loop}}
+! CHECK:               %[[STANDALONE_INDEX_I64:.*]] =
+! CHECK-SAME:            fir.convert %[[STANDALONE_INDEX]]
+! CHECK-NOT:     {{omp\.(wsloop|simd|loop_nest)|fir\.do_loop}}
+! CHECK-NOT:           {{^ *}}}
+! CHECK:               %[[STANDALONE_ELEMENT:.*]] = hlfir.designate
+! CHECK-SAME:            (%[[STANDALONE_INDEX_I64]])
+! CHECK-NOT:     {{omp\.(wsloop|simd|loop_nest)|fir\.do_loop}}
+! CHECK-NOT:           {{^ *}}}
+! CHECK:               hlfir.assign %[[STANDALONE_VALUE]] to
+! CHECK-SAME:            %[[STANDALONE_ELEMENT]]
+! CHECK:               omp.yield
+! CHECK-NOT:     {{omp\.(wsloop|simd|loop_nest)|fir\.do_loop}}
+! CHECK:         } else {
+! CHECK-NOT:     {{omp\.(wsloop|simd|loop_nest)|fir\.do_loop}}
+! CHECK:           omp.barrier
+! CHECK-NOT:     {{omp\.(wsloop|simd|loop_nest)|fir\.do_loop}}
+! CHECK:           fir.do_loop %[[STANDALONE_FB_IV:.*]] =
+! CHECK-NOT:     {{omp\.(wsloop|simd|loop_nest)|fir\.do_loop}}
+! CHECK-NOT:       {{^ *}}}
+! CHECK:             fir.store %[[STANDALONE_FB_IV]] to
+! CHECK-SAME:          %[[STANDALONE_FB_ADDR:.*]]
+! CHECK-NOT:     {{omp\.(wsloop|simd|loop_nest)|fir\.do_loop}}
+! CHECK-NOT:       {{^ *}}}
+! CHECK:             %[[STANDALONE_FB_VALUE:.*]] =
+! CHECK-SAME:          fir.load %[[STANDALONE_FB_ADDR]]
+! CHECK-NOT:     {{omp\.(wsloop|simd|loop_nest)|fir\.do_loop}}
+! CHECK-NOT:       {{^ *}}}
+! CHECK:             %[[STANDALONE_FB_INDEX:.*]] =
+! CHECK-SAME:          fir.load %[[STANDALONE_FB_ADDR]]
+! CHECK-NOT:     {{omp\.(wsloop|simd|loop_nest)|fir\.do_loop}}
+! CHECK-NOT:       {{^ *}}}
+! CHECK:             %[[STANDALONE_FB_INDEX_I64:.*]] =
+! CHECK-SAME:          fir.convert %[[STANDALONE_FB_INDEX]]
+! CHECK-NOT:     {{omp\.(wsloop|simd|loop_nest)|fir\.do_loop}}
+! CHECK-NOT:       {{^ *}}}
+! CHECK:             %[[STANDALONE_FB_ELEMENT:.*]] = hlfir.designate
+! CHECK-SAME:          (%[[STANDALONE_FB_INDEX_I64]])
+! CHECK-NOT:     {{omp\.(wsloop|simd|loop_nest)|fir\.do_loop}}
+! CHECK-NOT:       {{^ *}}}
+! CHECK:             hlfir.assign %[[STANDALONE_FB_VALUE]] to
+! CHECK-SAME:          %[[STANDALONE_FB_ELEMENT]]
+! CHECK-NOT:     {{omp\.(wsloop|simd|loop_nest)|fir\.do_loop}}
+! CHECK:         }
+! CHECK-NOT:     {{omp\.(wsloop|simd|loop_nest)|fir\.do_loop}}
+! CHECK:         return
+subroutine test_dynamic_standalone_fallback(flag, n, a)
+  logical, intent(in) :: flag
+  integer :: n, a(n), i
+  !$omp metadirective &
+  !$omp & when(user={condition(flag)}: do) &
+  !$omp & otherwise(barrier)
+  do i = 1, n
+    a(i) = i
+  end do
+end subroutine
+
+! When NOTHING is selected, the following loop is lowered normally.
+! CHECK-LABEL: func.func @_QPtest_dynamic_nothing_fallback(
+! CHECK-NOT:     {{omp\.(wsloop|simd|loop_nest)|fir\.do_loop}}
+! CHECK:         fir.if {{.*}} {
+! CHECK-NOT:     {{omp\.(wsloop|simd|loop_nest)|fir\.do_loop}}
+! CHECK:           omp.wsloop private({{.*}} ->
+! CHECK-SAME:        %[[NOTHING_PRIVATE:.*]] : !fir.ref<i32>)
+! CHECK-NOT:     {{omp\.(wsloop|simd|loop_nest)|fir\.do_loop}}
+! CHECK:             omp.loop_nest (%[[NOTHING_IV:.*]]) :
+! CHECK-NOT:     {{omp\.(wsloop|simd|loop_nest)|fir\.do_loop}}
+! CHECK:               %[[NOTHING_DECL:.*]]:2 =
+! CHECK-SAME:            hlfir.declare %[[NOTHING_PRIVATE]]
+! CHECK:               hlfir.assign %[[NOTHING_IV]] to
+! CHECK-SAME:            %[[NOTHING_DECL]]#0
+! CHECK-NOT:     {{omp\.(wsloop|simd|loop_nest)|fir\.do_loop}}
+! CHECK:               %[[NOTHING_VALUE:.*]] =
+! CHECK-SAME:            fir.load %[[NOTHING_DECL]]#0
+! CHECK-NOT:     {{omp\.(wsloop|simd|loop_nest)|fir\.do_loop}}
+! CHECK:               %[[NOTHING_INDEX:.*]] =
+! CHECK-SAME:            fir.load %[[NOTHING_DECL]]#0
+! CHECK-NOT:     {{omp\.(wsloop|simd|loop_nest)|fir\.do_loop}}
+! CHECK:               %[[NOTHING_INDEX_I64:.*]] =
+! CHECK-SAME:            fir.convert %[[NOTHING_INDEX]]
+! CHECK-NOT:     {{omp\.(wsloop|simd|loop_nest)|fir\.do_loop}}
+! CHECK-NOT:           {{^ *}}}
+! CHECK:               %[[NOTHING_ELEMENT:.*]] = hlfir.designate
+! CHECK-SAME:            (%[[NOTHING_INDEX_I64]])
+! CHECK-NOT:     {{omp\.(wsloop|simd|loop_nest)|fir\.do_loop}}
+! CHECK-NOT:           {{^ *}}}
+! CHECK:               hlfir.assign %[[NOTHING_VALUE]] to
+! CHECK-SAME:            %[[NOTHING_ELEMENT]]
+! CHECK:               omp.yield
+! CHECK-NOT:     {{omp\.(wsloop|simd|loop_nest)|fir\.do_loop}}
+! CHECK:         } else {
+! CHECK-NOT:       omp.
+! CHECK-NOT:       fir.do_loop
+! CHECK:           fir.do_loop %[[NOTHING_FB_IV:.*]] =
+! CHECK-NOT:       omp.
+! CHECK-NOT:       fir.do_loop
+! CHECK-NOT:       {{^ *}}}
+! CHECK:             fir.store %[[NOTHING_FB_IV]] to
+! CHECK-SAME:          %[[NOTHING_FB_ADDR:.*]]
+! CHECK-NOT:       omp.
+! CHECK-NOT:       fir.do_loop
+! CHECK-NOT:       {{^ *}}}
+! CHECK:             %[[NOTHING_FB_VALUE:.*]] =
+! CHECK-SAME:          fir.load %[[NOTHING_FB_ADDR]]
+! CHECK-NOT:       omp.
+! CHECK-NOT:       fir.do_loop
+! CHECK-NOT:       {{^ *}}}
+! CHECK:             %[[NOTHING_FB_INDEX:.*]] =
+! CHECK-SAME:          fir.load %[[NOTHING_FB_ADDR]]
+! CHECK-NOT:       omp.
+! CHECK-NOT:       fir.do_loop
+! CHECK-NOT:       {{^ *}}}
+! CHECK:             %[[NOTHING_FB_INDEX_I64:.*]] =
+! CHECK-SAME:          fir.convert %[[NOTHING_FB_INDEX]]
+! CHECK-NOT:       omp.
+! CHECK-NOT:       fir.do_loop
+! CHECK-NOT:       {{^ *}}}
+! CHECK:             %[[NOTHING_FB_ELEMENT:.*]] = hlfir.designate
+! CHECK-SAME:          (%[[NOTHING_FB_INDEX_I64]])
+! CHECK-NOT:       omp.
+! CHECK-NOT:       fir.do_loop
+! CHECK-NOT:       {{^ *}}}
+! CHECK:             hlfir.assign %[[NOTHING_FB_VALUE]] to
+! CHECK-SAME:          %[[NOTHING_FB_ELEMENT]]
+! CHECK-NOT:       omp.
+! CHECK-NOT:       fir.do_loop
+! CHECK:         }
+! CHECK-NOT:     {{omp\.(wsloop|simd|loop_nest)|fir\.do_loop}}
+! CHECK:         return
+subroutine test_dynamic_nothing_fallback(flag, n, a)
+  logical, intent(in) :: flag
+  integer :: n, a(n), i
+  !$omp metadirective &
+  !$omp & when(user={condition(flag)}: do) &
+  !$omp & otherwise(nothing)
+  do i = 1, n
+    a(i) = i
+  end do
+end subroutine
+
+! Compiler directives preceding the associated loop are processed before it.
+! CHECK-LABEL: func.func @_QPtest_dynamic_unroll_fallback(
+! CHECK-NOT:     {{omp\.(wsloop|simd|loop_nest)|fir\.do_loop}}
+! CHECK:         fir.if {{.*}} {
+! CHECK-NOT:     {{omp\.(wsloop|simd|loop_nest)|fir\.do_loop}}
+! CHECK:           omp.wsloop
+! CHECK-NOT:     {{omp\.(wsloop|simd|loop_nest)|fir\.do_loop}}
+! CHECK:             omp.loop_nest
+! CHECK-NOT:     {{omp\.(wsloop|simd|loop_nest)|fir\.do_loop}}
+! CHECK:         } else {
+! CHECK-NOT:       omp.
+! CHECK-NOT:       fir.do_loop
+! CHECK:           fir.do_loop
+! CHECK-SAME:        attributes {loopAnnotation = #[[UNROLL_ANNOTATION]]}
+! CHECK-NOT:       omp.
+! CHECK-NOT:       fir.do_loop
+! CHECK:         }
+! CHECK-NOT:     {{omp\.(wsloop|simd|loop_nest)|fir\.do_loop}}
+! CHECK:         return
+subroutine test_dynamic_unroll_fallback(flag, n, a)
+  logical, intent(in) :: flag
+  integer :: n, a(n), i
+  !$omp metadirective &
+  !$omp & when(user={condition(flag)}: do) &
+  !$omp & otherwise(nothing)
+  !dir$ unroll 4
+  do i = 1, n
+    a(i) = i
+  end do
+end subroutine
+
+! A supported compiler directive nested inside a begin/end metadirective is
+! attached to the associated loop before runtime selection.
+! CHECK-LABEL: func.func @_QPtest_begin_unroll_fallback(
+! CHECK:         fir.if {{.*}} {
+! CHECK-NOT:       {{omp\.(wsloop|simd|loop_nest)|fir\.do_loop}}
+! CHECK:           omp.wsloop
+! CHECK-NOT:       {{omp\.(wsloop|simd|loop_nest)|fir\.do_loop}}
+! CHECK:             omp.loop_nest
+! CHECK-NOT:       {{omp\.(wsloop|simd|loop_nest)|fir\.do_loop}}
+! CHECK-NOT:         {{^ *}}}
+! CHECK:               %[[BEGIN_UNROLL_ELEMENT:.*]] = hlfir.designate
+! CHECK-NOT:       {{omp\.(wsloop|simd|loop_nest)|fir\.do_loop}}
+! CHECK-NOT:         {{^ *}}}
+! CHECK:               hlfir.assign {{.*}} to %[[BEGIN_UNROLL_ELEMENT]]
+! CHECK-NOT:       {{omp\.(wsloop|simd|loop_nest)|fir\.do_loop}}
+! CHECK:         } else {
+! CHECK-NOT:       omp.
+! CHECK-NOT:       fir.do_loop
+! CHECK:           fir.do_loop
+! CHECK-SAME:        attributes {loopAnnotation = #[[UNROLL_ANNOTATION]]}
+! CHECK-NOT:       omp.
+! CHECK-NOT:       fir.do_loop
+! CHECK-NOT:         {{^ *}}}
+! CHECK:             %[[BEGIN_UNROLL_FALLBACK_ELEMENT:.*]] = hlfir.designate
+! CHECK-NOT:       omp.
+! CHECK-NOT:       fir.do_loop
+! CHECK-NOT:         {{^ *}}}
+! CHECK:             hlfir.assign {{.*}} to %[[BEGIN_UNROLL_FALLBACK_ELEMENT]]
+! CHECK-NOT:       omp.
+! CHECK-NOT:       fir.do_loop
+! CHECK:         }
+! CHECK-NOT:     omp.
+! CHECK-NOT:     fir.do_loop
+! CHECK:         return
+subroutine test_begin_unroll_fallback(flag, n, a)
+  logical, intent(in) :: flag
+  integer :: n, a(n), i
+  !$omp begin metadirective &
+  !$omp & when(user={condition(flag)}: do) &
+  !$omp & otherwise(nothing)
+  !dir$ unroll 4
+  do i = 1, n
+    a(i) = i
+  end do
+  !$omp end metadirective
+end subroutine
+
+! Other compiler directives that do not emit executable operations may also
+! appear between the metadirective and its associated loop. Check a loop
+! annotation, an inlining annotation, and an unrecognized no-op directive.
+! CHECK-LABEL: func.func @_QPtest_dynamic_intervening_compiler_directives(
+! CHECK-NOT:     {{omp\.(wsloop|simd|loop_nest)|fir\.do_loop}}
+! CHECK:         fir.if {{.*}} {
+! CHECK-NOT:       {{omp\.(wsloop|simd|loop_nest)|fir\.do_loop}}
+! CHECK:           omp.wsloop
+! CHECK-NOT:       {{omp\.(wsloop|simd|loop_nest)|fir\.do_loop}}
+! CHECK:             omp.loop_nest
+! CHECK-NOT:       {{omp\.(wsloop|simd|loop_nest)|fir\.do_loop}}
+! CHECK-NOT:         {{^ *}}}
+! CHECK:               fir.call @_QPconsume
+! CHECK-NOT:       {{omp\.(wsloop|simd|loop_nest)|fir\.do_loop}}
+! CHECK:         } else {
+! CHECK-NOT:       {{omp\.(wsloop|simd|loop_nest)|fir\.do_loop}}
+! CHECK:           fir.do_loop
+! CHECK-SAME:        attributes {loopAnnotation = #[[VECTOR_ANNOTATION]]}
+! CHECK-NOT:       {{omp\.(wsloop|simd|loop_nest)|fir\.do_loop}}
+! CHECK-NOT:         {{^ *}}}
+! CHECK:             fir.call @_QPconsume
+! CHECK-SAME:          inline_attr = #fir.inline_attrs<always_inline>
+! CHECK-NOT:       {{omp\.(wsloop|simd|loop_nest)|fir\.do_loop}}
+! CHECK:         }
+! CHECK-NOT:     {{omp\.(wsloop|simd|loop_nest)|fir\.do_loop}}
+! CHECK:         return
+subroutine test_dynamic_intervening_compiler_directives(flag, n, a)
+  logical, intent(in) :: flag
+  integer :: n, a(n), i
+  external :: consume
+  !$omp metadirective &
+  !$omp & when(user={condition(flag)}: do) &
+  !$omp & otherwise(nothing)
+  !dir$ vector always
+  !dir$ forceinline
+  !dir$ unknown
+  do i = 1, n
+    call consume(a(i))
+  end do
+end subroutine
+
+! Each runtime arm must compute its own affected depth and restore temporary
+! loop-index attributes before lowering the next arm.
+! CHECK-LABEL: func.func @_QPtest_dynamic_collapse(
+! CHECK:         fir.if {{.*}} {
+! CHECK:           omp.simd {{.*}}private({{.*}}Ei_private_i32{{.*}}Ej_private_i32
+! CHECK:             omp.loop_nest ({{.*}}, {{.*}}) : i32 {{.*}} collapse(2)
+! CHECK-NOT:     {{omp\.(wsloop|simd|loop_nest)|fir\.do_loop}}
+! CHECK-NOT:           {{^ *}}}
+! CHECK:               %[[DYNAMIC_COLLAPSE_ELEMENT:.*]] = hlfir.designate
+! CHECK-NOT:     {{omp\.(wsloop|simd|loop_nest)|fir\.do_loop}}
+! CHECK-NOT:           {{^ *}}}
+! CHECK:               hlfir.assign {{.*}} to
+! CHECK-SAME:            %[[DYNAMIC_COLLAPSE_ELEMENT]]
+! CHECK-NOT:     {{omp\.(wsloop|simd|loop_nest)|fir\.do_loop}}
+! CHECK:               omp.yield
+! CHECK:         } else {
+! CHECK:           omp.simd linear(
----------------
MattPD wrote:

The `omp.simd linear(` prefix still matches if the fallback `simd` arm incorrectly gains `private(j)`, because MLIR prints that clause after `linear(...) linear_var_types(...)`. A verifier-valid mutation adds the private clause and redirects the inner sequential loop to the private `j`. These checks accept the mutation.

The test comment describes restoring data-sharing attributes for each metadirective candidate. Could this block exercise that behavior by capturing the host `j` declaration and requiring the inner `fir.do_loop` store to target that declaration?

https://github.com/llvm/llvm-project/pull/218555


More information about the flang-commits mailing list