! RUN: %flang_fc1 -fopenmp -emit-llvm %s -o - | FileCheck %s

! Combinational testing of control flow graph and builder insertion points
! in mlir-to-llvm conversion:
!   - mixing multiple delayed privatizations and multiple reductions
!   - multiple blocks in the private alloc region
!   - private alloc region has to read from the mold variable
!   - firstprivate
!   - multiple blocks in the private copy region
!   - multiple blocks in the reduction init region
!   - reduction init region has to read from the mold variable
!   - re-used omp.private ops
!   - re-used omp.reduction.declare ops
!   - unstructured code inside of the parallel region
!   - needs private dealloc region, and this has multiple blocks
!   - needs reduction cleanup region, and this has multiple blocks

! This maybe belongs in the mlir tests, but what we are doing here is complex
! enough that I find the kind of minimised mlir code preferred by mlir reviewers
! hard to read without some fortran here for reference. Nothing like this would
! be generated by other upstream users of the MLIR OpenMP dialect.

subroutine worst_case(a, b, c, d)
  real, allocatable :: a(:), b(:), c(:), d(:)
  integer i

  !$omp parallel firstprivate(a,b) reduction(+:c,d)
  if (sum(a) == 1) stop 1
  !$omp end parallel
end subroutine

! CHECK-LABEL: define internal void @worst_case_..omp_par
! CHECK-NEXT:  omp.par.entry:
!                [reduction alloc regions inlined here]
! CHECK:         br label %omp.region.after_alloca

! CHECK:       omp.region.after_alloca:
! CHECK-NEXT:    br label %omp.par.region

! CHECK:       omp.par.region:
! CHECK-NEXT:    br label %omp.private.init

! CHECK:       omp.private.init:
! CHECK-NEXT:  br label %omp.private.init2

! CHECK:       omp.private.init2:
!                [begin private alloc for first var]
!                [read the length from the mold argument]
!                [if it is non-zero...]
! CHECK:         br i1 %{{.*}}, label %omp.private.init3, label %omp.private.init4

! CHECK:       omp.private.init4:                               ; preds = %omp.private.init2
!                [finish private alloc for second var with zero extent]
! CHECK:         br label %omp.private.init5

! CHECK:       omp.private.init5:                               ; preds = %omp.private.init3, %omp.private.init4
! CHECK-NEXT:    br label %omp.region.cont

! CHECK:       omp.region.cont:                                  ; preds = %omp.private.init5
! CHECK-NEXT:    %{{.*}} = phi ptr
! CHECK-NEXT:    br label %omp.private.init7

! CHECK:       omp.private.init7:
!                [begin private alloc for first var]
!                [read the length from the mold argument]
!                [if it is non-zero...]
! CHECK:         br i1 {{.*}}, label %omp.private.init8, label %omp.private.init9

! CHECK:       omp.private.init9:                               ; preds = %omp.private.init7
!                [finish private alloc for first var with zero extent]
! CHECK:         br label %omp.private.init10

! CHECK:       omp.private.init10:                               ; preds = %omp.private.init8, %omp.private.init9
! CHECK-NEXT:    br label %omp.region.cont6

! CHECK:       omp.region.cont6:                                 ; preds = %omp.private.init10
! CHECK-NEXT:    %{{.*}} = phi ptr
! CHECK-NEXT:    br label %omp.private.copy

! CHECK:       omp.private.copy:
! CHECK-NEXT:    br label %omp.private.copy12

! CHECK:       omp.private.copy12:                               ; preds = %omp.private.copy
!                [begin firstprivate copy for first var]
!                [read the length, is it non-zero?]
! CHECK:         br i1 %{{.*}}, label %omp.private.copy13, label %omp.private.copy14

! CHECK:       omp.private.copy14:                               ; preds = %omp.private.copy13, %omp.private.copy12
! CHECK-NEXT:    br label %omp.region.cont11

! CHECK:       omp.region.cont11:                                 ; preds = %omp.private.copy14
! CHECK-NEXT:    %{{.*}} = phi ptr
! CHECK-NEXT:    br label %omp.private.copy16

! CHECK:       omp.private.copy16:                               ; preds = %omp.region.cont11
!                [begin firstprivate copy for second var]
!                [read the length, is it non-zero?]
! CHECK:         br i1 %{{.*}}, label %omp.private.copy17, label %omp.private.copy18

! CHECK:       omp.private.copy18:                               ; preds = %omp.private.copy17, %omp.private.copy16
! CHECK-NEXT:    br label %omp.region.cont15

! CHECK:       omp.region.cont15:                                ; preds = %omp.private.copy18
! CHECK-NEXT:    %{{.*}} = phi ptr
! CHECK-NEXT:    br label %omp.reduction.init

! CHECK:       omp.reduction.init:                               ; preds = %omp.region.cont15
!                [deffered stores for results of reduction alloc regions]
! CHECK:         br label %[[VAL_96:.*]]

! CHECK:       omp.reduction.neutral:                            ; preds = %omp.reduction.init
!                [start of reduction initialization region]
!                [null check:]
! CHECK:         br i1 %{{.*}}, label %omp.reduction.neutral20, label %omp.reduction.neutral21

! CHECK:       omp.reduction.neutral21:                          ; preds = %omp.reduction.neutral
!                [malloc and assign the default value to the reduction variable]
! CHECK:         br label %omp.reduction.neutral22

! CHECK:       omp.reduction.neutral22:                          ; preds = %omp.reduction.neutral20, %omp.reduction.neutral21
! CHECK-NEXT:    br label %omp.region.cont19

! CHECK:       omp.region.cont19:                                ; preds = %omp.reduction.neutral22
! CHECK-NEXT:    %{{.*}} = phi ptr
! CHECK-NEXT:    br label %omp.reduction.neutral24

! CHECK:       omp.reduction.neutral24:                          ; preds = %omp.region.cont19
!                [start of reduction initialization region]
!                [null check:]
! CHECK:         br i1 %{{.*}}, label %omp.reduction.neutral25, label %omp.reduction.neutral26

! CHECK:       omp.reduction.neutral26:                          ; preds = %omp.reduction.neutral24
!                [malloc and assign the default value to the reduction variable]
! CHECK:         br label %omp.reduction.neutral27

! CHECK:       omp.reduction.neutral27:                          ; preds = %omp.reduction.neutral25, %omp.reduction.neutral26
! CHECK-NEXT:    br label %omp.region.cont23

! CHECK:       omp.region.cont23:                                ; preds = %omp.reduction.neutral27
! CHECK-NEXT:    %{{.*}} = phi ptr
! CHECK-NEXT:    br label %omp.par.region29

! CHECK:       omp.par.region29:                                 ; preds = %omp.region.cont23
!                [call SUM runtime function]
!                [if (sum(a) == 1)]
! CHECK:         br i1 %{{.*}}, label %omp.par.region30, label %omp.par.region31

! CHECK:       omp.par.region31:                                 ; preds = %omp.par.region29
! CHECK-NEXT:    br label %omp.region.cont28

! CHECK:       omp.region.cont28:                                ; preds = %omp.par.region30, %omp.par.region31
!                [omp parallel region done, call into the runtime to complete reduction]
! CHECK:         %[[VAL_233:.*]] = call i32 @__kmpc_reduce(
! CHECK:         switch i32 %[[VAL_233]], label %reduce.finalize [
! CHECK-NEXT:      i32 1, label %reduce.switch.nonatomic
! CHECK-NEXT:      i32 2, label %reduce.switch.atomic
! CHECK-NEXT:    ]

! CHECK:       reduce.switch.atomic:                             ; preds = %omp.region.cont28
! CHECK-NEXT:    unreachable

! CHECK:       reduce.switch.nonatomic:                          ; preds = %omp.region.cont28
! CHECK-NEXT:    %[[red_private_value_0:.*]] = load ptr, ptr %{{.*}}, align 8
! CHECK-NEXT:    br label %omp.reduction.nonatomic.body

!              [various blocks implementing the reduction]

! CHECK:       omp.region.cont37:                                ; preds =
! CHECK-NEXT:    %{{.*}} = phi ptr
! CHECK-NEXT:    call void @__kmpc_end_reduce(
! CHECK-NEXT:    br label %reduce.finalize

! CHECK:       reduce.finalize:                                  ; preds =
! CHECK-NEXT:    br label %omp.par.pre_finalize

! CHECK:       omp.par.pre_finalize:                             ; preds = %reduce.finalize
! CHECK-NEXT:    %{{.*}} = load ptr, ptr
! CHECK-NEXT:    br label %omp.reduction.cleanup

! CHECK:       omp.reduction.cleanup:                            ; preds = %omp.par.pre_finalize
!                [null check]
! CHECK:         br i1 %{{.*}}, label %omp.reduction.cleanup43, label %omp.reduction.cleanup44

! CHECK:       omp.reduction.cleanup44:                          ; preds = %omp.reduction.cleanup43, %omp.reduction.cleanup
! CHECK-NEXT:    br label %omp.region.cont42

! CHECK:       omp.region.cont42:                                ; preds = %omp.reduction.cleanup44
! CHECK-NEXT:    %{{.*}} = load ptr, ptr
! CHECK-NEXT:    br label %omp.reduction.cleanup46

! CHECK:       omp.reduction.cleanup46:                          ; preds = %omp.region.cont42
!                [null check]
! CHECK:         br i1 %{{.*}}, label %omp.reduction.cleanup47, label %omp.reduction.cleanup48

! CHECK:       omp.par.region30:                                 ; preds = %omp.par.region29
! CHECK-NEXT:    call void @_FortranAStopStatement

! CHECK:       omp.reduction.neutral25:                          ; preds = %omp.reduction.neutral24
!                [source length was zero: finish initializing array]
! CHECK:         br label %omp.reduction.neutral27

! CHECK:       omp.reduction.neutral20:                          ; preds = %omp.reduction.neutral
!                [source length was zero: finish initializing array]
! CHECK:         br label %omp.reduction.neutral22

! CHECK:       omp.private.copy17:                               ; preds = %omp.private.copy16
!                [source length was non-zero: call assign runtime]
! CHECK:         br label %omp.private.copy18

! CHECK:       omp.private.copy13:                               ; preds = %omp.private.copy12
!                [source length was non-zero: call assign runtime]
! CHECK:         br label %omp.private.copy14

! CHECK:       omp.private.init8:                               ; preds = %omp.private.init7
!                [var extent was non-zero: malloc a private array]
! CHECK:         br label %omp.private.init10

! CHECK:       omp.private.init3:                               ; preds = %omp.private.init2
!                [var extent was non-zero: malloc a private array]
! CHECK:         br label %omp.private.init5

! CHECK:       omp.par.exit.exitStub:                           ; preds = %omp.region.cont52
! CHECK-NEXT:    ret void
