Index: flang/lib/Lower/OpenMP.cpp =================================================================== --- flang/lib/Lower/OpenMP.cpp +++ flang/lib/Lower/OpenMP.cpp @@ -96,9 +96,9 @@ hasFirstPrivateOp = true; } } - // TODO: Do not emit BarrierOp for single construct, either. // FIXME: Is BarrierOp needed for offloading constructs? - if constexpr (!std::is_same_v) + if constexpr (!std::is_same_v && + !std::is_same_v) if (hasFirstPrivateOp) firOpBuilder.create(converter.getCurrentLocation()); firOpBuilder.restoreInsertionPoint(insPt); @@ -659,7 +659,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); Index: flang/test/Lower/OpenMP/single.f90 =================================================================== --- flang/test/Lower/OpenMP/single.f90 +++ flang/test/Lower/OpenMP/single.f90 @@ -1,25 +1,25 @@ -!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.terminator !$omp end single - !OMPDialect: omp.terminator + !CHECK: omp.terminator !$omp end parallel end subroutine omp_single @@ -27,21 +27,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 +49,78 @@ ! 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.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.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-NOT: omp.barrier + !CHECK: fir.call @_QPbaz(%[[int_var_firstprivate]], %[[mytype_var_firstprivate]]) : (!fir.ref, !fir.ref>) -> () + call baz(int_var, mytype_var) + !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 @@ -245,7 +245,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