Index: flang/lib/Lower/OpenMP.cpp =================================================================== --- flang/lib/Lower/OpenMP.cpp +++ flang/lib/Lower/OpenMP.cpp @@ -504,14 +504,19 @@ if (eval.lowerAsUnstructured() && !outerCombined) createEmptyRegionBlocks(firOpBuilder, eval.getNestedEvaluations()); + if constexpr (std::is_same_v) { + // FIXME: Emit an implicit barrier at the end if no 'nowait' clause was + // specified and no 'copyprivate' clause. + if (!op.nowaitAttr()) + firOpBuilder.create(loc); + } + // Insert the terminator. if constexpr (std::is_same_v || - std::is_same_v) { - mlir::ValueRange results; - firOpBuilder.create(loc, results); - } else { + std::is_same_v) + firOpBuilder.create(loc, mlir::ValueRange()); + else firOpBuilder.create(loc); - } // Reset the insert point to before the terminator. resetBeforeTerminator(firOpBuilder, storeOp, block); @@ -824,7 +829,8 @@ } else if (blockDirective.v == llvm::omp::OMPD_single) { auto singleOp = firOpBuilder.create( currentLocation, allocateOperands, allocatorOperands, nowaitAttr); - createBodyOfOp(singleOp, converter, currentLocation, eval); + createBodyOfOp(singleOp, converter, currentLocation, eval, + &opClauseList); } else if (blockDirective.v == llvm::omp::OMPD_ordered) { auto orderedOp = firOpBuilder.create( currentLocation, /*simd=*/nullptr); @@ -838,7 +844,7 @@ allocatorOperands); createBodyOfOp(taskOp, converter, currentLocation, eval, &opClauseList); } else if (blockDirective.v == llvm::omp::OMPD_taskgroup) { - // TODO: Add task_reduction support + // TODO: Add task_reduction support auto taskGroupOp = firOpBuilder.create( currentLocation, /*task_reduction_vars=*/ValueRange(), /*task_reductions=*/nullptr, allocateOperands, allocatorOperands); Index: flang/test/Lower/OpenMP/default-clause.f90 =================================================================== --- flang/test/Lower/OpenMP/default-clause.f90 +++ flang/test/Lower/OpenMP/default-clause.f90 @@ -268,6 +268,7 @@ !CHECK: omp.single { !CHECK: %[[temp:.*]] = fir.load %[[PRIVATE_Y]] : !fir.ref !CHECK: fir.store %[[temp]] to %[[PRIVATE_X]] : !fir.ref +!CHECK: omp.barrier !CHECK: omp.terminator !CHECK: } !CHECK: omp.terminator Index: flang/test/Lower/OpenMP/single.f90 =================================================================== --- flang/test/Lower/OpenMP/single.f90 +++ flang/test/Lower/OpenMP/single.f90 @@ -1,25 +1,26 @@ -!RUN: %flang_fc1 -emit-fir -fopenmp %s -o - | FileCheck %s --check-prefixes="FIRDialect,OMPDialect" -!RUN: %flang_fc1 -emit-fir -fopenmp %s -o - | fir-opt --cfg-conversion | fir-opt --fir-to-llvm-ir | FileCheck %s --check-prefixes="LLVMDialect,OMPDialect" +!RUN: %flang_fc1 -emit-fir -fopenmp %s -o - | FileCheck %s +!RUN: bbc -emit-fir -fopenmp %s -o - | FileCheck %s !=============================================================================== ! Single construct !=============================================================================== -!FIRDialect-LABEL: func @_QPomp_single -!FIRDialect-SAME: (%[[x:.*]]: !fir.ref {fir.bindc_name = "x"}) +!CHECK-LABEL: func @_QPomp_single +!CHECK-SAME: (%[[x:.*]]: !fir.ref {fir.bindc_name = "x"}) subroutine omp_single(x) integer, intent(inout) :: x - !OMPDialect: omp.parallel + !CHECK: omp.parallel !$omp parallel - !OMPDialect: omp.single + !CHECK: omp.single !$omp single - !FIRDialect: %[[xval:.*]] = fir.load %[[x]] : !fir.ref - !FIRDialect: %[[res:.*]] = arith.addi %[[xval]], %{{.*}} : i32 - !FIRDialect: fir.store %[[res]] to %[[x]] : !fir.ref + !CHECK: %[[xval:.*]] = fir.load %[[x]] : !fir.ref + !CHECK: %[[res:.*]] = arith.addi %[[xval]], %{{.*}} : i32 + !CHECK: fir.store %[[res]] to %[[x]] : !fir.ref x = x + 12 - !OMPDialect: omp.terminator + !CHECK: omp.barrier + !CHECK: omp.terminator !$omp end single - !OMPDialect: omp.terminator + !CHECK: omp.terminator !$omp end parallel end subroutine omp_single @@ -27,21 +28,21 @@ ! Single construct with nowait !=============================================================================== -!FIRDialect-LABEL: func @_QPomp_single_nowait -!FIRDialect-SAME: (%[[x:.*]]: !fir.ref {fir.bindc_name = "x"}) +!CHECK-LABEL: func @_QPomp_single_nowait +!CHECK-SAME: (%[[x:.*]]: !fir.ref {fir.bindc_name = "x"}) subroutine omp_single_nowait(x) integer, intent(inout) :: x - !OMPDialect: omp.parallel + !CHECK: omp.parallel !$omp parallel - !OMPDialect: omp.single nowait + !CHECK: omp.single nowait !$omp single - !FIRDialect: %[[xval:.*]] = fir.load %[[x]] : !fir.ref - !FIRDialect: %[[res:.*]] = arith.addi %[[xval]], %{{.*}} : i32 - !FIRDialect: fir.store %[[res]] to %[[x]] : !fir.ref + !CHECK: %[[xval:.*]] = fir.load %[[x]] : !fir.ref + !CHECK: %[[res:.*]] = arith.addi %[[xval]], %{{.*}} : i32 + !CHECK: fir.store %[[res]] to %[[x]] : !fir.ref x = x + 12 - !OMPDialect: omp.terminator + !CHECK: omp.terminator !$omp end single nowait - !OMPDialect: omp.terminator + !CHECK: omp.terminator !$omp end parallel end subroutine omp_single_nowait @@ -49,21 +50,80 @@ ! Single construct with allocate !=============================================================================== -!FIRDialect-LABEL: func @_QPsingle_allocate +!CHECK-LABEL: func @_QPsingle_allocate subroutine single_allocate() use omp_lib integer :: x - !OMPDialect: omp.parallel { + !CHECK: omp.parallel { !$omp parallel - !OMPDialect: omp.single allocate( - !FIRDialect: %{{.+}} : i32 -> %{{.+}} : !fir.ref - !LLVMDialect: %{{.+}} : i32 -> %{{.+}} : !llvm.ptr - !OMPDialect: ) { + !CHECK: omp.single allocate(%{{.+}} : i32 -> %{{.+}} : !fir.ref) { !$omp single allocate(omp_high_bw_mem_alloc: x) private(x) - !FIRDialect: arith.addi + !CHECK: arith.addi x = x + 12 - !OMPDialect: omp.terminator + !CHECK: omp.barrier + !CHECK: omp.terminator !$omp end single - !OMPDialect: omp.terminator + !CHECK: omp.terminator !$omp end parallel end subroutine single_allocate + +!=============================================================================== +! Single construct with private +!=============================================================================== +!CHECK-LABEL: func @_QPsingle_private +subroutine single_private + type mytype + integer :: x + end type mytype + + !CHECK: %[[int_var:.+]] = fir.alloca i32 + !CHECK: %[[mytype_var:.+]] = fir.alloca !fir.type<_QFsingle_privateTmytype{x:i32}> + integer :: int_var + type(mytype) :: mytype_var + + !CHECK: fir.call @_QPbar(%[[int_var]], %[[mytype_var]]) : (!fir.ref, !fir.ref>) -> () + call bar(int_var, mytype_var) + + !CHECK: omp.single { + !$omp single private(int_var, mytype_var) + !CHECK: %[[int_var_private:.+]] = fir.alloca i32 + !CHECK: %[[mytype_var_private:.+]] = fir.alloca !fir.type<_QFsingle_privateTmytype{x:i32}> + + !CHECK: fir.call @_QPbar(%[[int_var_private]], %[[mytype_var_private]]) : (!fir.ref, !fir.ref>) -> () + call bar(int_var, mytype_var) + !CHECK: omp.barrier + !CHECK: omp.terminator + !$omp end single +end subroutine single_private + +!=============================================================================== +! Single construct with firstprivate +!=============================================================================== +!CHECK-LABEL: func @_QPsingle_firstprivate +subroutine single_firstprivate + type mytype + integer :: x + end type mytype + + !CHECK: %[[int_var:.+]] = fir.alloca i32 + !CHECK: %[[mytype_var:.+]] = fir.alloca !fir.type<_QFsingle_firstprivateTmytype{x:i32}> + integer :: int_var + type(mytype) :: mytype_var + + !CHECK: fir.call @_QPbaz(%[[int_var]], %[[mytype_var]]) : (!fir.ref, !fir.ref>) -> () + call baz(int_var, mytype_var) + + !CHECK: omp.single { + !$omp single firstprivate(int_var, mytype_var) + !CHECK: %[[int_var_firstprivate:.+]] = fir.alloca i32 + !CHECK: %[[int_var_load:.+]] = fir.load %[[int_var]] : !fir.ref + !CHECK: fir.store %[[int_var_load]] to %[[int_var_firstprivate]] : !fir.ref + !CHECK: %[[mytype_var_firstprivate:.+]] = fir.alloca !fir.type<_QFsingle_firstprivateTmytype{x:i32}> + !CHECK: %[[mytype_var_load:.+]] = fir.load %[[mytype_var]] : !fir.ref> + !CHECK: fir.store %[[mytype_var_load]] to %[[mytype_var_firstprivate]] + !CHECK: fir.call @_QPbaz(%[[int_var_firstprivate]], %[[mytype_var_firstprivate]]) : (!fir.ref, !fir.ref>) -> () + call baz(int_var, mytype_var) + !CHECK: omp.barrier + !CHECK: omp.terminator + !$omp end single +end subroutine single_firstprivate Index: mlir/include/mlir/Dialect/OpenMP/OpenMPOps.td =================================================================== --- mlir/include/mlir/Dialect/OpenMP/OpenMPOps.td +++ mlir/include/mlir/Dialect/OpenMP/OpenMPOps.td @@ -237,7 +237,8 @@ // 2.8.2 Single Construct //===----------------------------------------------------------------------===// -def SingleOp : OpenMP_Op<"single", [AttrSizedOperandSegments]> { +def SingleOp : OpenMP_Op<"single", [AttrSizedOperandSegments, + OutlineableOpenMPOpInterface]> { let summary = "single directive"; let description = [{ The single construct specifies that the associated structured block is