[flang-commits] [flang] [flang][OpenMP] Lower DO, SIMD, and DO SIMD metadirective variants (PR #218555)
via flang-commits
flang-commits at lists.llvm.org
Wed Aug 26 18:47:54 PDT 2026
================
@@ -0,0 +1,624 @@
+! Test lowering of metadirectives with ordinary loop-associated variants.
+
+! RUN: %flang_fc1 -fopenmp -emit-hlfir -fopenmp-version=52 %s -o - | FileCheck %s
+
+! 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.parallel
+! CHECK: omp.wsloop private({{.*}}Ei_private_i32
+! CHECK: omp.loop_nest
+! CHECK: hlfir.assign
+! CHECK: omp.yield
+! 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-NOT: omp.wsloop
+! CHECK: omp.simd linear(
+! CHECK: omp.loop_nest
+! CHECK: hlfir.assign
+! CHECK: omp.yield
+! CHECK-NOT: fir.do_loop
+! CHECK: fir.load
+! CHECK: hlfir.assign
+! 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.parallel
+! CHECK: omp.wsloop
+! CHECK: omp.simd linear(
+! CHECK: omp.loop_nest
+! CHECK: hlfir.assign
+! CHECK: omp.yield
+! 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: fir.if {{.*}} {
+! CHECK: omp.wsloop private({{.*}}Ei_private_i32
+! CHECK: omp.loop_nest
+! CHECK: hlfir.assign
+! CHECK: } else {
+! CHECK-NOT: omp.
+! CHECK: fir.do_loop
+! CHECK: hlfir.assign
+! 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: fir.if {{.*}} {
+! CHECK: omp.wsloop
+! CHECK: } else {
+! CHECK: fir.do_loop
+! CHECK: }
+! CHECK-NOT: fir.do_loop
+! CHECK: return
+! CHECK-LABEL: func.func @_QPtest_alt_standalone_entry_no_directive(
+! CHECK: fir.if {{.*}} {
+! CHECK: omp.wsloop
+! CHECK: } else {
+! CHECK: fir.do_loop
+! CHECK: }
+! CHECK-NOT: fir.do_loop
+! 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
+! CHECK-NOT: omp.loop_nest
+! CHECK: fir.do_loop
+! CHECK: hlfir.assign
+! CHECK-NOT: 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
+! CHECK-NOT: omp.loop_nest
+! CHECK: omp.masked
+! CHECK-NOT: omp.wsloop
+! CHECK-NOT: omp.loop_nest
+! CHECK: fir.do_loop
+! CHECK: hlfir.assign
+! CHECK-NOT: 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
+! CHECK-NOT: omp.loop_nest
+! CHECK: fir.if {{.*}} {
+! CHECK: omp.barrier
+! CHECK-NOT: omp.wsloop
+! CHECK-NOT: omp.loop_nest
+! CHECK: } else {
+! CHECK-NOT: omp.wsloop
+! CHECK-NOT: omp.loop_nest
+! CHECK: }
+! CHECK: fir.do_loop
+! CHECK: hlfir.assign
+! CHECK-NOT: omp.wsloop
+! CHECK-NOT: omp.loop_nest
+! 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
+! CHECK-NOT: omp.loop_nest
+! CHECK: fir.if {{.*}} {
+! CHECK: omp.barrier
+! CHECK: } else {
+! CHECK-NOT: omp.wsloop
+! CHECK-NOT: omp.loop_nest
+! CHECK: }
+! CHECK: fir.do_loop
+! CHECK-NOT: omp.wsloop
+! CHECK-NOT: omp.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
+! CHECK-NOT: omp.loop_nest
+! CHECK: fir.if {{.*}} {
+! CHECK: omp.barrier
+! CHECK: } else {
+! CHECK-NOT: omp.wsloop
+! CHECK-NOT: omp.loop_nest
+! CHECK: }
+! CHECK: fir.do_loop
+! CHECK-NOT: omp.wsloop
+! CHECK-NOT: omp.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: fir.if {{.*}} {
+! CHECK: omp.wsloop
+! CHECK: omp.loop_nest
+! CHECK: hlfir.assign
+! CHECK: } else {
+! CHECK: omp.simd
+! CHECK: omp.loop_nest
+! CHECK: hlfir.assign
+! CHECK: }
+! 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: fir.if {{.*}} {
+! CHECK: omp.wsloop
+! CHECK: omp.loop_nest
+! CHECK: hlfir.assign
+! CHECK: } else {
+! CHECK: omp.barrier
+! CHECK: fir.do_loop
+! CHECK: hlfir.assign
+! CHECK: }
+! 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: fir.if {{.*}} {
+! CHECK: omp.wsloop
+! CHECK: omp.loop_nest
+! CHECK: hlfir.assign
+! CHECK: } else {
+! CHECK-NOT: omp.
+! CHECK: fir.do_loop
+! CHECK: hlfir.assign
+! CHECK: }
+! 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: fir.if {{.*}} {
+! CHECK: omp.wsloop
+! CHECK: } else {
+! CHECK: fir.do_loop
+! CHECK-SAME: attributes {loopAnnotation = #[[UNROLL_ANNOTATION]]}
+! CHECK: }
+! 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: omp.wsloop
----------------
MattPD wrote:
I confirmed that both arms now require the source body operations inside their loop regions.
https://github.com/llvm/llvm-project/pull/218555
More information about the flang-commits
mailing list