[llvm-branch-commits] [flang] [Flang][OpenMP] Add combined construct information (PR #198783)
Sergio Afonso via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Thu Jun 25 03:50:45 PDT 2026
https://github.com/skatrak updated https://github.com/llvm/llvm-project/pull/198783
>From db761229c7b697a9b9ec05ff2effa6a78d886daf Mon Sep 17 00:00:00 2001
From: Sergio Afonso <Sergio.AfonsoFumero at amd.com>
Date: Wed, 20 May 2026 13:06:54 +0100
Subject: [PATCH] [Flang][OpenMP] Add combined construct information
This patch adds the `omp.combined` attribute to OpenMP dialect
operations following changes to the `ComposableOpInterface`.
This attribute is added to operations representing non-innermost leaf
constructs of a combined construct and to standalone block-associated
constructs that can be combined with their parent construct.
Changes are made to the OpenMP lowering logic, as well as the
do-concurrent, workshare and workdistribute transformation passes.
---
flang/lib/Lower/OpenMP/OpenMP.cpp | 78 +-
.../OpenMP/DoConcurrentConversion.cpp | 8 +-
.../Optimizer/OpenMP/LowerWorkdistribute.cpp | 6 +-
flang/lib/Optimizer/OpenMP/LowerWorkshare.cpp | 7 +
.../OpenMP/workshare-array-array-assign.f90 | 3 +-
.../Integration/OpenMP/workshare-axpy.f90 | 3 +-
.../OpenMP/workshare-forall-sliced-array.f90 | 3 +-
.../OpenMP/workshare-scalar-array-assign.f90 | 3 +-
.../OpenMP/workshare-scalar-array-mul.f90 | 4 +
flang/test/Lower/OpenMP/compound.f90 | 1094 +++++++++++++++++
.../Lower/OpenMP/multiple-entry-points.f90 | 1 +
flang/test/Lower/OpenMP/workshare.f90 | 6 +-
.../Transforms/DoConcurrent/basic_device.f90 | 6 +-
.../Transforms/DoConcurrent/basic_host.f90 | 2 +-
.../Transforms/DoConcurrent/basic_host.mlir | 2 +-
.../Transforms/DoConcurrent/local_device.mlir | 10 +-
.../locality_specifiers_simple.mlir | 2 +-
.../DoConcurrent/non_const_bounds.f90 | 2 +-
.../Transforms/DoConcurrent/reduce_add.mlir | 2 +-
.../DoConcurrent/reduce_all_regions.mlir | 2 +-
.../DoConcurrent/reduce_device.mlir | 8 +-
.../DoConcurrent/reduce_device_min.f90 | 4 +-
.../Transforms/DoConcurrent/reduce_local.mlir | 2 +-
.../reduction_symbol_resultion.f90 | 4 +-
.../DoConcurrent/runtime_sized_array.f90 | 2 +-
.../DoConcurrent/skip_all_nested_loops.f90 | 2 +-
.../DoConcurrent/use_loop_bounds_in_body.f90 | 12 +-
.../OpenMP/lower-workdistribute-doloop.mlir | 2 +-
.../lower-workdistribute-fission-host.mlir | 4 +-
.../lower-workdistribute-fission-target.mlir | 4 +-
.../OpenMP/lower-workdistribute-fission.mlir | 2 +-
...-workdistribute-runtime-assign-scalar.mlir | 4 +-
.../omp-function-filtering-todo.mlir | 4 +-
33 files changed, 1227 insertions(+), 71 deletions(-)
create mode 100644 flang/test/Lower/OpenMP/compound.f90
diff --git a/flang/lib/Lower/OpenMP/OpenMP.cpp b/flang/lib/Lower/OpenMP/OpenMP.cpp
index 5df4c2f5e8e61..cd06bd5d33ca3 100644
--- a/flang/lib/Lower/OpenMP/OpenMP.cpp
+++ b/flang/lib/Lower/OpenMP/OpenMP.cpp
@@ -71,6 +71,25 @@ static void genOMPDispatch(lower::AbstractConverter &converter,
const ConstructQueue &queue,
ConstructQueue::const_iterator item);
+/// Return the directive that is immediately nested inside of the given
+/// \c parent evaluation, if it is its only non-end-statement nested evaluation
+/// and it represents an OpenMP construct.
+static lower::pft::Evaluation *
+extractOnlyOmpNestedEval(lower::pft::Evaluation &parent) {
+ if (!parent.hasNestedEvaluations())
+ return nullptr;
+
+ auto &nested = parent.getFirstNestedEvaluation();
+ if (!nested.isA<parser::OpenMPConstruct>())
+ return nullptr;
+
+ for (auto &sibling : parent.getNestedEvaluations())
+ if (&sibling != &nested && !sibling.isEndStmt())
+ return nullptr;
+
+ return &nested;
+}
+
static llvm::SmallVector<Object>
makeObjects(llvm::ArrayRef<const semantics::Symbol *> syms) {
llvm::SmallVector<Object> objects;
@@ -386,25 +405,6 @@ class DirectivePatternVisitor {
}
}
- /// Return the directive that is immediately nested inside of the given
- /// \c parent evaluation, if it is its only non-end-statement nested
- /// evaluation and it represents an OpenMP construct.
- lower::pft::Evaluation *
- extractOnlyOmpNestedEval(lower::pft::Evaluation &parent) {
- if (!parent.hasNestedEvaluations())
- return nullptr;
-
- auto &nested{parent.getFirstNestedEvaluation()};
- if (!nested.isA<parser::OpenMPConstruct>())
- return nullptr;
-
- for (auto &sibling : parent.getNestedEvaluations())
- if (&sibling != &nested && !sibling.isEndStmt())
- return nullptr;
-
- return &nested;
- }
-
protected:
semantics::SemanticsContext &semaCtx;
@@ -4307,7 +4307,7 @@ static void genOMPDispatch(lower::AbstractConverter &converter,
// Lowered in the enclosing genSectionsOp.
break;
case llvm::omp::Directive::OMPD_sections:
- genSectionsOp(converter, symTable, semaCtx, eval, loc, queue, item);
+ newOp = genSectionsOp(converter, symTable, semaCtx, eval, loc, queue, item);
break;
case llvm::omp::Directive::OMPD_simd:
newOp =
@@ -4394,6 +4394,44 @@ static void genOMPDispatch(lower::AbstractConverter &converter,
finalizeStmtCtx();
if (loopLeaf)
symTable.popScope();
+
+ // Add the omp.combined attribute to eligible ops, including non-innermost
+ // leafs of a combined construct and immediately nested block-associated
+ // combinable constructs. SECTIONS, WORKSHARE and WORKDISTRIBUTE are skipped
+ // due to only being able to appear as an innermost combined construct.
+ if (!loopLeaf &&
+ llvm::isa_and_present<mlir::omp::ComposableOpInterface>(newOp) &&
+ !llvm::isa<mlir::omp::SectionsOp, mlir::omp::WorkshareOp,
+ mlir::omp::WorkdistributeOp>(newOp)) {
+ bool isCombined = false;
+ if (std::next(item) != queue.end()) {
+ // Non-innermost leafs of a combined construct must always hold the
+ // attribute.
+ isCombined = true;
+ } else if (lower::pft::Evaluation *nestedEval =
+ extractOnlyOmpNestedEval(eval)) {
+ // Combinable constructs that are immediately nested with no other
+ // statements or directives preventing them from being combined need the
+ // attribute as well. Disallow block constructs that can only be outermost
+ // leafs and loop transformation constructs.
+ OmpDirectiveSet combinableDirs =
+ (llvm::omp::blockConstructSet &
+ ~OmpDirectiveSet{llvm::omp::Directive::OMPD_ordered,
+ llvm::omp::Directive::OMPD_scope,
+ llvm::omp::Directive::OMPD_taskgroup}) |
+ (llvm::omp::loopConstructSet & ~llvm::omp::loopTransformationSet);
+ const auto &ompEval = nestedEval->get<parser::OpenMPConstruct>();
+ llvm::omp::Directive nestedDir =
+ parser::omp::GetOmpDirectiveName(ompEval).v;
+ llvm::omp::Directive firstLeafDir =
+ llvm::omp::getLeafConstructsOrSelf(nestedDir).front();
+
+ if (combinableDirs.test(firstLeafDir))
+ isCombined = true;
+ }
+ if (isCombined)
+ llvm::cast<mlir::omp::ComposableOpInterface>(newOp).setCombined(true);
+ }
}
//===----------------------------------------------------------------------===//
diff --git a/flang/lib/Optimizer/OpenMP/DoConcurrentConversion.cpp b/flang/lib/Optimizer/OpenMP/DoConcurrentConversion.cpp
index 950a64fc13d57..945250daf1dbe 100644
--- a/flang/lib/Optimizer/OpenMP/DoConcurrentConversion.cpp
+++ b/flang/lib/Optimizer/OpenMP/DoConcurrentConversion.cpp
@@ -326,14 +326,18 @@ class DoConcurrentConversion
targetOp =
genTargetOp(doLoop.getLoc(), rewriter, mapper, loopNestLiveIns,
targetClauseOps, loopNestClauseOps, liveInShapeInfoMap);
- genTeamsOp(rewriter, loop, mapper);
+ auto teamsOp = genTeamsOp(rewriter, loop, mapper);
+ targetOp.setCombined(true);
+ teamsOp.setCombined(true);
}
mlir::omp::ParallelOp parallelOp =
genParallelOp(rewriter, loop, ivInfos, mapper);
- // Only set as composite when part of `distribute parallel do`.
+ // Only set as composite when part of `distribute parallel do`, and only set
+ // as combined when part of `parallel do`.
parallelOp.setComposite(mapToDevice);
+ parallelOp.setCombined(!mapToDevice);
if (!mapToDevice)
genLoopNestClauseOps(doLoop.getLoc(), rewriter, loop, loopNestClauseOps);
diff --git a/flang/lib/Optimizer/OpenMP/LowerWorkdistribute.cpp b/flang/lib/Optimizer/OpenMP/LowerWorkdistribute.cpp
index 3274e04179d33..c2de2499f7d10 100644
--- a/flang/lib/Optimizer/OpenMP/LowerWorkdistribute.cpp
+++ b/flang/lib/Optimizer/OpenMP/LowerWorkdistribute.cpp
@@ -274,8 +274,10 @@ fissionWorkdistribute(omp::WorkdistributeOp workdistribute) {
}
if (parallelize && hoisted.empty() &&
- parallelize->getNextNode() == terminator)
+ parallelize->getNextNode() == terminator) {
+ teams.setCombined(true);
break;
+ }
if (parallelize) {
auto newTeams = rewriter.cloneWithoutRegions(teams);
auto *newTeamsBlock = rewriter.createBlock(
@@ -290,6 +292,7 @@ fissionWorkdistribute(omp::WorkdistributeOp workdistribute) {
parallelize->replaceAllUsesWith(cloned);
parallelize->erase();
omp::TerminatorOp::create(rewriter, loc);
+ newTeams.setCombined(true);
changed = true;
}
}
@@ -1591,6 +1594,7 @@ genIsolatedTargetOp(omp::TargetOp targetOp, SmallVector<Value> &postMapOperands,
targetOp.getThreadLimitVars(), targetOp.getPrivateMapsAttr(),
omp::TargetExecModeAttr::get(targetOp->getContext(),
omp::TargetExecMode::spmd));
+ isolatedTargetOp.setCombined(true);
auto *isolatedTargetBlock =
rewriter.createBlock(&isolatedTargetOp.getRegion(),
isolatedTargetOp.getRegion().begin(), {}, {});
diff --git a/flang/lib/Optimizer/OpenMP/LowerWorkshare.cpp b/flang/lib/Optimizer/OpenMP/LowerWorkshare.cpp
index a41d8d8826501..b8231bc35c999 100644
--- a/flang/lib/Optimizer/OpenMP/LowerWorkshare.cpp
+++ b/flang/lib/Optimizer/OpenMP/LowerWorkshare.cpp
@@ -612,6 +612,13 @@ LogicalResult lowerWorkshare(mlir::omp::WorkshareOp wsOp, DominanceInfo &di) {
term->erase();
newOp->erase();
wsOp->erase();
+
+ // If this was part of a combined construct (e.g. 'parallel workshare'), the
+ // changes we just made to the region can be incompatible with a combined
+ // construct, such as containing multiple block-associated constructs in it.
+ if (auto parentOp =
+ dyn_cast<omp::ComposableOpInterface>(parentBlock->getParentOp()))
+ parentOp.setCombined(false);
} else {
// Otherwise just change the operation to an omp.single.
diff --git a/flang/test/Integration/OpenMP/workshare-array-array-assign.f90 b/flang/test/Integration/OpenMP/workshare-array-array-assign.f90
index e9ec5d9175beb..3ccb46ebeebea 100644
--- a/flang/test/Integration/OpenMP/workshare-array-array-assign.f90
+++ b/flang/test/Integration/OpenMP/workshare-array-array-assign.f90
@@ -23,7 +23,7 @@ subroutine sb1(x, y)
! HLFIR: omp.terminator
! HLFIR: }
! HLFIR: omp.terminator
-! HLFIR: }
+! HLFIR: } {omp.combined}
! FIR: omp.parallel {
! FIR: omp.wsloop nowait {
@@ -32,3 +32,4 @@ subroutine sb1(x, y)
! FIR: omp.barrier
! FIR: omp.terminator
! FIR: }
+! FIR-NOT: omp.combined
diff --git a/flang/test/Integration/OpenMP/workshare-axpy.f90 b/flang/test/Integration/OpenMP/workshare-axpy.f90
index 846bef5f5082c..3bbbb159de707 100644
--- a/flang/test/Integration/OpenMP/workshare-axpy.f90
+++ b/flang/test/Integration/OpenMP/workshare-axpy.f90
@@ -32,7 +32,7 @@ subroutine sb1(a, x, y, z)
! HLFIR: }
! HLFIR-NOT: omp.barrier
! HLFIR: omp.terminator
-! HLFIR: }
+! HLFIR: } {omp.combined}
! HLFIR: return
! HLFIR: }
! HLFIR:}
@@ -53,3 +53,4 @@ subroutine sb1(a, x, y, z)
! FIR: omp.barrier
! FIR: omp.terminator
! FIR: }
+! FIR-NOT:omp.combined
diff --git a/flang/test/Integration/OpenMP/workshare-forall-sliced-array.f90 b/flang/test/Integration/OpenMP/workshare-forall-sliced-array.f90
index 88d1062b091bf..e841213c2f1bf 100644
--- a/flang/test/Integration/OpenMP/workshare-forall-sliced-array.f90
+++ b/flang/test/Integration/OpenMP/workshare-forall-sliced-array.f90
@@ -36,7 +36,7 @@ subroutine workshare_forall_sliced(a1)
! HLFIR: omp.terminator
! HLFIR: }
! HLFIR: omp.terminator
-! HLFIR: }
+! HLFIR: } {omp.combined}
! After workshare lowering, the forall should be in omp.single (since it
! contains operations that are not safe to parallelize across threads).
@@ -51,6 +51,7 @@ subroutine workshare_forall_sliced(a1)
! FIR: omp.barrier
! FIR: omp.terminator
! FIR: }
+! FIR-NOT: omp.combined
! Verify LLVM IR is generated successfully (the original issue caused crashes)
! LLVM-LABEL: define {{.*}}workshare_forall_sliced
diff --git a/flang/test/Integration/OpenMP/workshare-scalar-array-assign.f90 b/flang/test/Integration/OpenMP/workshare-scalar-array-assign.f90
index 6c180cd639997..43e6cc4bef7b7 100644
--- a/flang/test/Integration/OpenMP/workshare-scalar-array-assign.f90
+++ b/flang/test/Integration/OpenMP/workshare-scalar-array-assign.f90
@@ -24,7 +24,7 @@ subroutine sb1(a, x)
! HLFIR: omp.terminator
! HLFIR: }
! HLFIR: omp.terminator
-! HLFIR: }
+! HLFIR: } {omp.combined}
! FIR: omp.parallel {
! FIR: %[[SCALAR_ALLOCA:.*]] = fir.alloca i32
@@ -43,3 +43,4 @@ subroutine sb1(a, x)
! FIR: }
! FIR: omp.barrier
! FIR: omp.terminator
+! FIR-NOT: omp.combined
diff --git a/flang/test/Integration/OpenMP/workshare-scalar-array-mul.f90 b/flang/test/Integration/OpenMP/workshare-scalar-array-mul.f90
index 9b8ef66b48f47..1a9c9a031d9c4 100644
--- a/flang/test/Integration/OpenMP/workshare-scalar-array-mul.f90
+++ b/flang/test/Integration/OpenMP/workshare-scalar-array-mul.f90
@@ -26,12 +26,14 @@ program test
! HLFIR-O3: hlfir.destroy
! HLFIR-O3: omp.terminator
! HLFIR-O3: omp.terminator
+! HLFIR-O3: omp.combined
! FIR-O3: omp.parallel {
! FIR-O3: omp.wsloop nowait {
! FIR-O3: omp.loop_nest
! FIR-O3: omp.barrier
! FIR-O3: omp.terminator
+! FIR-O3-NOT:omp.combined
! HLFIR-O0: omp.parallel {
! HLFIR-O0: omp.workshare {
@@ -40,6 +42,7 @@ program test
! HLFIR-O0: hlfir.destroy
! HLFIR-O0: omp.terminator
! HLFIR-O0: omp.terminator
+! HLFIR-O0: omp.combined
! Check the copyprivate copy function
! FIR-O0: func.func private @_workshare_copy_heap_{{.*}}(%[[DST:.*]]: {{.*}}, %[[SRC:.*]]: {{.*}})
@@ -63,3 +66,4 @@ program test
! FIR-O0: omp.terminator
! FIR-O0: omp.barrier
! FIR-O0: omp.terminator
+! FIR-O0-NOT:omp.combined
diff --git a/flang/test/Lower/OpenMP/compound.f90 b/flang/test/Lower/OpenMP/compound.f90
new file mode 100644
index 0000000000000..d61745345a640
--- /dev/null
+++ b/flang/test/Lower/OpenMP/compound.f90
@@ -0,0 +1,1094 @@
+! This test checks lowering of compound (combined and composite) constructs.
+! Specifically, it makes sure that the proper ComposableOpInterface attributes
+! are set.
+
+! RUN: bbc -fopenmp -fopenmp-version=60 -emit-hlfir %s -o - | FileCheck %s
+! RUN: %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=60 %s -o - | FileCheck %s
+
+! ------------------------------------------------------------------------------
+! COMPOSITE CONSTRUCTS
+! ------------------------------------------------------------------------------
+
+subroutine distribute_parallel_do()
+ implicit none
+ integer :: i
+
+ !$omp teams
+ !$omp distribute parallel do
+ do i=1, 10
+ end do
+ !$omp end teams
+end subroutine
+
+! CHECK-LABEL: func.func @_QPdistribute_parallel_do
+! CHECK: omp.parallel
+! CHECK: omp.distribute
+! CHECK-NEXT: omp.wsloop
+! CHECK-NEXT: omp.loop_nest
+! CHECK: omp.yield
+! CHECK-NEXT: }
+! CHECK-NEXT: } {{{.*}}omp.composite{{.*}}}
+! CHECK-NEXT: } {{{.*}}omp.composite{{.*}}}
+! CHECK: omp.terminator
+! CHECK-NEXT: } {{{.*}}omp.composite{{.*}}}
+
+subroutine distribute_parallel_do_simd()
+ implicit none
+ integer :: i
+
+ !$omp teams
+ !$omp distribute parallel do simd
+ do i=1, 10
+ end do
+ !$omp end teams
+end subroutine
+
+! CHECK-LABEL: func.func @_QPdistribute_parallel_do_simd
+! CHECK: omp.parallel
+! CHECK: omp.distribute
+! CHECK-NEXT: omp.wsloop
+! CHECK-NEXT: omp.simd
+! CHECK-NEXT: omp.loop_nest
+! CHECK: omp.yield
+! CHECK-NEXT: }
+! CHECK-NEXT: } {{{.*}}omp.composite{{.*}}}
+! CHECK-NEXT: } {{{.*}}omp.composite{{.*}}}
+! CHECK-NEXT: } {{{.*}}omp.composite{{.*}}}
+! CHECK: omp.terminator
+! CHECK-NEXT: } {{{.*}}omp.composite{{.*}}}
+
+subroutine distribute_simd()
+ implicit none
+ integer :: i
+
+ !$omp teams
+ !$omp distribute simd
+ do i=1, 10
+ end do
+ !$omp end teams
+end subroutine
+
+! CHECK-LABEL: func.func @_QPdistribute_simd
+! CHECK: omp.distribute
+! CHECK-NEXT: omp.simd
+! CHECK-NEXT: omp.loop_nest
+! CHECK: omp.yield
+! CHECK-NEXT: }
+! CHECK-NEXT: } {{{.*}}omp.composite{{.*}}}
+! CHECK-NEXT: } {{{.*}}omp.composite{{.*}}}
+
+subroutine do_simd()
+ implicit none
+ integer :: i
+
+ !$omp do simd
+ do i=1, 10
+ end do
+end subroutine
+
+! CHECK-LABEL: func.func @_QPdo_simd
+! CHECK: omp.wsloop
+! CHECK-NEXT: omp.simd
+! CHECK-NEXT: omp.loop_nest
+! CHECK: omp.yield
+! CHECK-NEXT: }
+! CHECK-NEXT: } {{{.*}}omp.composite{{.*}}}
+! CHECK-NEXT: } {{{.*}}omp.composite{{.*}}}
+
+! TODO: Add taskloop simd once supported by lowering.
+
+! ------------------------------------------------------------------------------
+! COMBINED CONSTRUCTS
+! ------------------------------------------------------------------------------
+
+subroutine masked_taskloop()
+ implicit none
+ integer :: i
+
+ !$omp masked taskloop
+ do i=1, 10
+ end do
+end subroutine
+
+! CHECK-LABEL: func.func @_QPmasked_taskloop
+! CHECK: omp.masked
+! CHECK: omp.taskloop.context
+! CHECK: omp.taskloop.wrapper
+! CHECK-NEXT: omp.loop_nest
+! CHECK: omp.yield
+! CHECK-NEXT: }
+! CHECK-NEXT: }
+! CHECK-NOT: omp.combined
+! CHECK: omp.terminator
+! CHECK-NEXT: } {{{.*}}omp.combined{{.*}}}
+! CHECK: omp.terminator
+! CHECK-NEXT: } {{{.*}}omp.combined{{.*}}}
+
+subroutine master_taskloop()
+ implicit none
+ integer :: i
+
+ !$omp master taskloop
+ do i=1, 10
+ end do
+end subroutine
+
+! CHECK-LABEL: func.func @_QPmaster_taskloop
+! CHECK: omp.master
+! CHECK: omp.taskloop.context
+! CHECK: omp.taskloop.wrapper
+! CHECK-NEXT: omp.loop_nest
+! CHECK: omp.yield
+! CHECK-NEXT: }
+! CHECK-NEXT: }
+! CHECK-NOT: omp.combined
+! CHECK: omp.terminator
+! CHECK-NEXT: } {{{.*}}omp.combined{{.*}}}
+! CHECK: omp.terminator
+! CHECK-NEXT: } {{{.*}}omp.combined{{.*}}}
+
+subroutine parallel_do()
+ implicit none
+ integer :: i
+
+ !$omp parallel do
+ do i=1, 10
+ end do
+end subroutine
+
+! CHECK-LABEL: func.func @_QPparallel_do
+! CHECK: omp.parallel
+! CHECK: omp.wsloop
+! CHECK-NEXT: omp.loop_nest
+! CHECK: omp.yield
+! CHECK-NEXT: }
+! CHECK-NEXT: }
+! CHECK-NOT: omp.combined
+! CHECK: omp.terminator
+! CHECK-NEXT: } {{{.*}}omp.combined{{.*}}}
+
+subroutine parallel_loop()
+ implicit none
+ integer :: i
+
+ !$omp parallel loop
+ do i=1, 10
+ end do
+end subroutine
+
+! CHECK-LABEL: func.func @_QPparallel_loop
+! CHECK: omp.parallel
+! CHECK: omp.wsloop
+! CHECK-NEXT: omp.loop_nest
+! CHECK: omp.yield
+! CHECK-NEXT: }
+! CHECK-NEXT: }
+! CHECK-NOT: omp.combined
+! CHECK: omp.terminator
+! CHECK-NEXT: } {{{.*}}omp.combined{{.*}}}
+
+subroutine parallel_masked()
+ implicit none
+
+ !$omp parallel masked
+ call foo()
+ !$omp end parallel masked
+end subroutine
+
+! CHECK-LABEL: func.func @_QPparallel_masked
+! CHECK: omp.parallel
+! CHECK: omp.masked
+! CHECK: omp.terminator
+! CHECK-NEXT: }
+! CHECK-NOT: omp.combined
+! CHECK: omp.terminator
+! CHECK-NEXT: } {{{.*}}omp.combined{{.*}}}
+
+subroutine parallel_master()
+ implicit none
+
+ !$omp parallel master
+ call foo()
+ !$omp end parallel master
+end subroutine
+
+! CHECK-LABEL: func.func @_QPparallel_master
+! CHECK: omp.parallel
+! CHECK: omp.master
+! CHECK: omp.terminator
+! CHECK-NEXT: }
+! CHECK-NOT: omp.combined
+! CHECK: omp.terminator
+! CHECK-NEXT: } {{{.*}}omp.combined{{.*}}}
+
+subroutine parallel_sections()
+ implicit none
+
+ !$omp parallel sections
+ call foo()
+ !$omp end parallel sections
+end subroutine
+
+! CHECK-LABEL: func.func @_QPparallel_sections
+! CHECK: omp.parallel
+! CHECK: omp.sections
+! CHECK: omp.section
+! CHECK: omp.terminator
+! CHECK-NEXT: }
+! CHECK: }
+! CHECK-NOT: omp.combined
+! CHECK: omp.terminator
+! CHECK-NEXT: } {{{.*}}omp.combined{{.*}}}
+
+subroutine parallel_workshare()
+ implicit none
+ integer :: x(10)
+
+ !$omp parallel workshare
+ x = 1
+ !$omp end parallel workshare
+end subroutine
+
+! CHECK-LABEL: func.func @_QPparallel_workshare
+! CHECK: omp.parallel
+! CHECK: omp.workshare
+! CHECK: omp.terminator
+! CHECK-NEXT: }
+! CHECK-NOT: omp.combined
+! CHECK: omp.terminator
+! CHECK-NEXT: } {{{.*}}omp.combined{{.*}}}
+
+subroutine target_loop()
+ implicit none
+ integer :: i
+
+ !$omp target loop
+ do i=1, 10
+ end do
+end subroutine
+
+! CHECK-LABEL: func.func @_QPtarget_loop
+! CHECK: omp.target
+! CHECK: omp.simd
+! CHECK-NEXT: omp.loop_nest
+! CHECK: omp.yield
+! CHECK-NEXT: }
+! CHECK-NEXT: }
+! CHECK-NOT: omp.combined
+! CHECK: omp.terminator
+! CHECK-NEXT: } {{{.*}}omp.combined{{.*}}}
+
+subroutine target_parallel()
+ implicit none
+
+ !$omp target parallel
+ call foo()
+ !$omp end target parallel
+end subroutine
+
+! CHECK-LABEL: func.func @_QPtarget_parallel
+! CHECK: omp.target
+! CHECK: omp.parallel
+! CHECK: omp.terminator
+! CHECK-NEXT: }
+! CHECK-NOT: omp.combined
+! CHECK: omp.terminator
+! CHECK-NEXT: } {{{.*}}omp.combined{{.*}}}
+
+subroutine target_simd()
+ implicit none
+ integer :: i
+
+ !$omp target simd
+ do i=1, 10
+ end do
+end subroutine
+
+! CHECK-LABEL: func.func @_QPtarget_simd
+! CHECK: omp.target
+! CHECK: omp.simd
+! CHECK-NEXT: omp.loop_nest
+! CHECK: omp.yield
+! CHECK-NEXT: }
+! CHECK-NEXT: }
+! CHECK-NOT: omp.combined
+! CHECK: omp.terminator
+! CHECK-NEXT: } {{{.*}}omp.combined{{.*}}}
+
+subroutine target_teams()
+ implicit none
+
+ !$omp target teams
+ call foo()
+ !$omp end target teams
+end subroutine
+
+! CHECK-LABEL: func.func @_QPtarget_teams
+! CHECK: omp.target
+! CHECK: omp.teams
+! CHECK: omp.terminator
+! CHECK-NEXT: }
+! CHECK-NOT: omp.combined
+! CHECK: omp.terminator
+! CHECK-NEXT: } {{{.*}}omp.combined{{.*}}}
+
+subroutine teams_distribute()
+ implicit none
+ integer :: i
+
+ !$omp teams distribute
+ do i=1, 10
+ end do
+end subroutine
+
+! CHECK-LABEL: func.func @_QPteams_distribute
+! CHECK: omp.teams
+! CHECK: omp.distribute
+! CHECK-NEXT: omp.loop_nest
+! CHECK: omp.yield
+! CHECK-NEXT: }
+! CHECK-NEXT: }
+! CHECK-NOT: omp.combined
+! CHECK: omp.terminator
+! CHECK-NEXT: } {{{.*}}omp.combined{{.*}}}
+
+subroutine teams_loop()
+ implicit none
+ integer :: i
+
+ !$omp teams loop
+ do i=1, 10
+ end do
+end subroutine
+
+! CHECK-LABEL: func.func @_QPteams_loop
+! CHECK: omp.teams
+! CHECK: omp.parallel
+! CHECK: omp.distribute
+! CHECK-NEXT: omp.wsloop
+! CHECK-NEXT: omp.loop_nest
+! CHECK: omp.yield
+! CHECK-NEXT: }
+! CHECK-NEXT: } {{{.*}}omp.composite{{.*}}}
+! CHECK-NEXT: } {{{.*}}omp.composite{{.*}}}
+! CHECK: omp.terminator
+! CHECK-NEXT: } {{{.*}}omp.composite{{.*}}}
+! CHECK: omp.terminator
+! CHECK-NEXT: } {{{.*}}omp.combined{{.*}}}
+
+subroutine teams_workdistribute()
+ implicit none
+ integer :: x
+
+ !$omp teams workdistribute
+ x = 1
+ !$omp end teams workdistribute
+end subroutine
+
+! CHECK-LABEL: func.func @_QPteams_workdistribute
+! CHECK: omp.teams
+! CHECK: omp.workdistribute
+! CHECK: omp.terminator
+! CHECK-NEXT: }
+! CHECK-NOT: omp.combined
+! CHECK: omp.terminator
+! CHECK-NEXT: } {{{.*}}omp.combined{{.*}}}
+
+! ------------------------------------------------------------------------------
+! COMBINED CONSTRUCTS (SPLIT)
+! ------------------------------------------------------------------------------
+
+subroutine masked_loop()
+ implicit none
+ integer :: i
+
+ !$omp masked
+ !$omp loop
+ do i=1, 10
+ end do
+ !$omp end masked
+end subroutine
+
+! CHECK-LABEL: func.func @_QPmasked_loop
+! CHECK: omp.masked
+! CHECK: omp.simd
+! CHECK-NEXT: omp.loop_nest
+! CHECK: omp.yield
+! CHECK-NEXT: }
+! CHECK-NEXT: }
+! CHECK-NOT: omp.combined
+! CHECK: omp.terminator
+! CHECK-NEXT: } {{{.*}}omp.combined{{.*}}}
+
+subroutine masked_parallel()
+ implicit none
+
+ !$omp masked
+ !$omp parallel
+ call foo()
+ !$omp end parallel
+ !$omp end masked
+end subroutine
+
+! CHECK-LABEL: func.func @_QPmasked_parallel
+! CHECK: omp.masked
+! CHECK: omp.parallel
+! CHECK: omp.terminator
+! CHECK-NEXT: }
+! CHECK-NOT: omp.combined
+! CHECK: omp.terminator
+! CHECK-NEXT: } {{{.*}}omp.combined{{.*}}}
+
+subroutine masked_simd()
+ implicit none
+ integer :: i
+
+ !$omp masked
+ !$omp simd
+ do i=1, 10
+ end do
+ !$omp end masked
+end subroutine
+
+! CHECK-LABEL: func.func @_QPmasked_simd
+! CHECK: omp.masked
+! CHECK: omp.simd
+! CHECK-NEXT: omp.loop_nest
+! CHECK: omp.yield
+! CHECK-NEXT: }
+! CHECK-NEXT: }
+! CHECK-NOT: omp.combined
+! CHECK: omp.terminator
+! CHECK-NEXT: } {{{.*}}omp.combined{{.*}}}
+
+subroutine masked_target()
+ implicit none
+
+ !$omp masked
+ !$omp target
+ call foo()
+ !$omp end target
+ !$omp end masked
+end subroutine
+
+! CHECK-LABEL: func.func @_QPmasked_target
+! CHECK: omp.masked
+! CHECK: omp.target
+! CHECK: omp.terminator
+! CHECK-NEXT: }
+! CHECK-NOT: omp.combined
+! CHECK: omp.terminator
+! CHECK-NEXT: } {{{.*}}omp.combined{{.*}}}
+
+subroutine masked_target_data()
+ implicit none
+ integer :: x(10)
+
+ !$omp masked
+ !$omp target_data map(tofrom: x)
+ call foo()
+ !$omp end target_data
+ !$omp end masked
+end subroutine
+
+! CHECK-LABEL: func.func @_QPmasked_target_data
+! CHECK: omp.masked
+! CHECK: omp.target_data
+! CHECK: omp.terminator
+! CHECK-NEXT: }
+! CHECK-NOT: omp.combined
+! CHECK: omp.terminator
+! CHECK-NEXT: } {{{.*}}omp.combined{{.*}}}
+
+subroutine masked_task()
+ implicit none
+
+ !$omp masked
+ !$omp task
+ call foo()
+ !$omp end task
+ !$omp end masked
+end subroutine
+
+! CHECK-LABEL: func.func @_QPmasked_task
+! CHECK: omp.masked
+! CHECK: omp.task
+! CHECK: omp.terminator
+! CHECK-NEXT: }
+! CHECK-NOT: omp.combined
+! CHECK: omp.terminator
+! CHECK-NEXT: } {{{.*}}omp.combined{{.*}}}
+
+subroutine master_loop()
+ implicit none
+ integer :: i
+
+ !$omp master
+ !$omp loop
+ do i=1, 10
+ end do
+ !$omp end master
+end subroutine
+
+! CHECK-LABEL: func.func @_QPmaster_loop
+! CHECK: omp.master
+! CHECK: omp.simd
+! CHECK-NEXT: omp.loop_nest
+! CHECK: omp.yield
+! CHECK-NEXT: }
+! CHECK-NEXT: }
+! CHECK-NOT: omp.combined
+! CHECK: omp.terminator
+! CHECK-NEXT: } {{{.*}}omp.combined{{.*}}}
+
+subroutine master_parallel()
+ implicit none
+
+ !$omp master
+ !$omp parallel
+ call foo()
+ !$omp end parallel
+ !$omp end master
+end subroutine
+
+! CHECK-LABEL: func.func @_QPmaster_parallel
+! CHECK: omp.master
+! CHECK: omp.parallel
+! CHECK: omp.terminator
+! CHECK-NEXT: }
+! CHECK-NOT: omp.combined
+! CHECK: omp.terminator
+! CHECK-NEXT: } {{{.*}}omp.combined{{.*}}}
+
+subroutine master_simd()
+ implicit none
+ integer :: i
+
+ !$omp master
+ !$omp simd
+ do i=1, 10
+ end do
+ !$omp end master
+end subroutine
+
+! CHECK-LABEL: func.func @_QPmaster_simd
+! CHECK: omp.master
+! CHECK: omp.simd
+! CHECK-NEXT: omp.loop_nest
+! CHECK: omp.yield
+! CHECK-NEXT: }
+! CHECK-NEXT: }
+! CHECK-NOT: omp.combined
+! CHECK: omp.terminator
+! CHECK-NEXT: } {{{.*}}omp.combined{{.*}}}
+
+subroutine master_target()
+ implicit none
+
+ !$omp master
+ !$omp target
+ call foo()
+ !$omp end target
+ !$omp end master
+end subroutine
+
+! CHECK-LABEL: func.func @_QPmaster_target
+! CHECK: omp.master
+! CHECK: omp.target
+! CHECK: omp.terminator
+! CHECK-NEXT: }
+! CHECK-NOT: omp.combined
+! CHECK: omp.terminator
+! CHECK-NEXT: } {{{.*}}omp.combined{{.*}}}
+
+subroutine master_target_data()
+ implicit none
+ integer :: x(10)
+
+ !$omp master
+ !$omp target_data map(tofrom: x)
+ call foo()
+ !$omp end target_data
+ !$omp end master
+end subroutine
+
+! CHECK-LABEL: func.func @_QPmaster_target_data
+! CHECK: omp.master
+! CHECK: omp.target_data
+! CHECK: omp.terminator
+! CHECK-NEXT: }
+! CHECK-NOT: omp.combined
+! CHECK: omp.terminator
+! CHECK-NEXT: } {{{.*}}omp.combined{{.*}}}
+
+subroutine master_task()
+ implicit none
+
+ !$omp master
+ !$omp task
+ call foo()
+ !$omp end task
+ !$omp end master
+end subroutine
+
+! CHECK-LABEL: func.func @_QPmaster_task
+! CHECK: omp.master
+! CHECK: omp.task
+! CHECK: omp.terminator
+! CHECK-NEXT: }
+! CHECK-NOT: omp.combined
+! CHECK: omp.terminator
+! CHECK-NEXT: } {{{.*}}omp.combined{{.*}}}
+
+subroutine parallel_simd()
+ implicit none
+ integer :: i
+
+ !$omp parallel
+ !$omp simd
+ do i=1, 10
+ end do
+ !$omp end parallel
+end subroutine
+
+! CHECK-LABEL: func.func @_QPparallel_simd
+! CHECK: omp.parallel
+! CHECK: omp.simd
+! CHECK-NEXT: omp.loop_nest
+! CHECK: omp.yield
+! CHECK-NEXT: }
+! CHECK-NEXT: }
+! CHECK-NOT: omp.combined
+! CHECK: omp.terminator
+! CHECK-NEXT: } {{{.*}}omp.combined{{.*}}}
+
+subroutine parallel_single()
+ implicit none
+
+ !$omp parallel
+ !$omp single
+ call foo()
+ !$omp end single
+ !$omp end parallel
+end subroutine
+
+! CHECK-LABEL: func.func @_QPparallel_single
+! CHECK: omp.parallel
+! CHECK: omp.single
+! CHECK: omp.terminator
+! CHECK-NEXT: }
+! CHECK-NOT: omp.combined
+! CHECK: omp.terminator
+! CHECK-NEXT: } {{{.*}}omp.combined{{.*}}}
+
+subroutine parallel_target()
+ implicit none
+
+ !$omp parallel
+ !$omp target
+ call foo()
+ !$omp end target
+ !$omp end parallel
+end subroutine
+
+! CHECK-LABEL: func.func @_QPparallel_target
+! CHECK: omp.parallel
+! CHECK: omp.target
+! CHECK: omp.terminator
+! CHECK-NEXT: }
+! CHECK-NOT: omp.combined
+! CHECK: omp.terminator
+! CHECK-NEXT: } {{{.*}}omp.combined{{.*}}}
+
+subroutine parallel_target_data()
+ implicit none
+ integer :: x(10)
+
+ !$omp parallel
+ !$omp target_data map(tofrom: x)
+ call foo()
+ !$omp end target_data
+ !$omp end parallel
+end subroutine
+
+! CHECK-LABEL: func.func @_QPparallel_target_data
+! CHECK: omp.parallel
+! CHECK: omp.target_data
+! CHECK: omp.terminator
+! CHECK-NEXT: }
+! CHECK-NOT: omp.combined
+! CHECK: omp.terminator
+! CHECK-NEXT: } {{{.*}}omp.combined{{.*}}}
+
+subroutine parallel_task()
+ implicit none
+
+ !$omp parallel
+ !$omp task
+ call foo()
+ !$omp end task
+ !$omp end parallel
+end subroutine
+
+! CHECK-LABEL: func.func @_QPparallel_task
+! CHECK: omp.parallel
+! CHECK: omp.task
+! CHECK: omp.terminator
+! CHECK-NEXT: }
+! CHECK-NOT: omp.combined
+! CHECK: omp.terminator
+! CHECK-NEXT: } {{{.*}}omp.combined{{.*}}}
+
+subroutine parallel_taskloop()
+ implicit none
+ integer :: i
+
+ !$omp parallel
+ !$omp taskloop
+ do i=1, 10
+ end do
+ !$omp end parallel
+end subroutine
+
+! CHECK-LABEL: func.func @_QPparallel_taskloop
+! CHECK: omp.parallel
+! CHECK: omp.taskloop.context
+! CHECK: omp.taskloop.wrapper
+! CHECK-NEXT: omp.loop_nest
+! CHECK: omp.yield
+! CHECK-NEXT: }
+! CHECK-NEXT: }
+! CHECK-NOT: omp.combined
+! CHECK: omp.terminator
+! CHECK-NEXT: } {{{.*}}omp.combined{{.*}}}
+! CHECK: omp.terminator
+! CHECK-NEXT: } {{{.*}}omp.combined{{.*}}}
+
+subroutine single_loop()
+ implicit none
+ integer :: i
+
+ !$omp single
+ !$omp loop
+ do i=1, 10
+ end do
+ !$omp end single
+end subroutine
+
+! CHECK-LABEL: func.func @_QPsingle_loop
+! CHECK: omp.single
+! CHECK: omp.simd
+! CHECK-NEXT: omp.loop_nest
+! CHECK: omp.yield
+! CHECK-NEXT: }
+! CHECK-NEXT: }
+! CHECK-NOT: omp.combined
+! CHECK: omp.terminator
+! CHECK-NEXT: } {{{.*}}omp.combined{{.*}}}
+
+subroutine single_parallel()
+ implicit none
+
+ !$omp single
+ !$omp parallel
+ call foo()
+ !$omp end parallel
+ !$omp end single
+end subroutine
+
+! CHECK-LABEL: func.func @_QPsingle_parallel
+! CHECK: omp.single
+! CHECK: omp.parallel
+! CHECK: omp.terminator
+! CHECK-NEXT: }
+! CHECK-NOT: omp.combined
+! CHECK: omp.terminator
+! CHECK-NEXT: } {{{.*}}omp.combined{{.*}}}
+
+subroutine single_simd()
+ implicit none
+ integer :: i
+
+ !$omp single
+ !$omp simd
+ do i=1, 10
+ end do
+ !$omp end single
+end subroutine
+
+! CHECK-LABEL: func.func @_QPsingle_simd
+! CHECK: omp.single
+! CHECK: omp.simd
+! CHECK-NEXT: omp.loop_nest
+! CHECK: omp.yield
+! CHECK-NEXT: }
+! CHECK-NEXT: }
+! CHECK-NOT: omp.combined
+! CHECK: omp.terminator
+! CHECK-NEXT: } {{{.*}}omp.combined{{.*}}}
+
+subroutine single_target()
+ implicit none
+
+ !$omp single
+ !$omp target
+ call foo()
+ !$omp end target
+ !$omp end single
+end subroutine
+
+! CHECK-LABEL: func.func @_QPsingle_target
+! CHECK: omp.single
+! CHECK: omp.target
+! CHECK: omp.terminator
+! CHECK-NEXT: }
+! CHECK-NOT: omp.combined
+! CHECK: omp.terminator
+! CHECK-NEXT: } {{{.*}}omp.combined{{.*}}}
+
+subroutine single_target_data()
+ implicit none
+ integer :: x(10)
+
+ !$omp single
+ !$omp target_data map(tofrom: x)
+ call foo()
+ !$omp end target_data
+ !$omp end single
+end subroutine
+
+! CHECK-LABEL: func.func @_QPsingle_target_data
+! CHECK: omp.single
+! CHECK: omp.target_data
+! CHECK: omp.terminator
+! CHECK-NEXT: }
+! CHECK-NOT: omp.combined
+! CHECK: omp.terminator
+! CHECK-NEXT: } {{{.*}}omp.combined{{.*}}}
+
+subroutine single_task()
+ implicit none
+
+ !$omp single
+ !$omp task
+ call foo()
+ !$omp end task
+ !$omp end single
+end subroutine
+
+! CHECK-LABEL: func.func @_QPsingle_task
+! CHECK: omp.single
+! CHECK: omp.task
+! CHECK: omp.terminator
+! CHECK-NEXT: }
+! CHECK-NOT: omp.combined
+! CHECK: omp.terminator
+! CHECK-NEXT: } {{{.*}}omp.combined{{.*}}}
+
+subroutine single_taskloop()
+ implicit none
+ integer :: i
+
+ !$omp single
+ !$omp taskloop
+ do i=1, 10
+ end do
+ !$omp end single
+end subroutine
+
+! CHECK-LABEL: func.func @_QPsingle_taskloop
+! CHECK: omp.single
+! CHECK: omp.taskloop.context
+! CHECK: omp.taskloop.wrapper
+! CHECK-NEXT: omp.loop_nest
+! CHECK: omp.yield
+! CHECK-NEXT: }
+! CHECK-NEXT: }
+! CHECK-NOT: omp.combined
+! CHECK: omp.terminator
+! CHECK-NEXT: } {{{.*}}omp.combined{{.*}}}
+! CHECK: omp.terminator
+! CHECK-NEXT: } {{{.*}}omp.combined{{.*}}}
+
+subroutine target_task()
+ implicit none
+
+ !$omp target
+ !$omp task
+ call foo()
+ !$omp end task
+ !$omp end target
+end subroutine
+
+! CHECK-LABEL: func.func @_QPtarget_task
+! CHECK: omp.target
+! CHECK: omp.task
+! CHECK: omp.terminator
+! CHECK-NEXT: }
+! CHECK-NOT: omp.combined
+! CHECK: omp.terminator
+! CHECK-NEXT: } {{{.*}}omp.combined{{.*}}}
+
+subroutine target_taskloop()
+ implicit none
+ integer :: i
+
+ !$omp target
+ !$omp taskloop
+ do i=1, 10
+ end do
+ !$omp end target
+end subroutine
+
+! CHECK-LABEL: func.func @_QPtarget_taskloop
+! CHECK: omp.target
+! CHECK: omp.taskloop.context
+! CHECK: omp.taskloop.wrapper
+! CHECK-NEXT: omp.loop_nest
+! CHECK: omp.yield
+! CHECK-NEXT: }
+! CHECK-NEXT: }
+! CHECK-NOT: omp.combined
+! CHECK: omp.terminator
+! CHECK-NEXT: } {{{.*}}omp.combined{{.*}}}
+! CHECK: omp.terminator
+! CHECK-NEXT: } {{{.*}}omp.combined{{.*}}}
+
+subroutine target_data_loop()
+ implicit none
+ integer :: i
+ integer :: x(10)
+
+ !$omp target_data map(tofrom: x)
+ !$omp loop
+ do i=1, 10
+ end do
+ !$omp end target_data
+end subroutine
+
+! CHECK-LABEL: func.func @_QPtarget_data_loop
+! CHECK: omp.target_data
+! CHECK: omp.simd
+! CHECK-NEXT: omp.loop_nest
+! CHECK: omp.yield
+! CHECK-NEXT: }
+! CHECK-NEXT: }
+! CHECK-NOT: omp.combined
+! CHECK: omp.terminator
+! CHECK-NEXT: } {{{.*}}omp.combined{{.*}}}
+
+subroutine target_data_parallel()
+ implicit none
+ integer :: x(10)
+
+ !$omp target_data map(tofrom: x)
+ !$omp parallel
+ call foo()
+ !$omp end parallel
+ !$omp end target_data
+end subroutine
+
+! CHECK-LABEL: func.func @_QPtarget_data_parallel
+! CHECK: omp.target_data
+! CHECK: omp.parallel
+! CHECK: omp.terminator
+! CHECK-NEXT: }
+! CHECK-NOT: omp.combined
+! CHECK: omp.terminator
+! CHECK-NEXT: } {{{.*}}omp.combined{{.*}}}
+
+subroutine target_data_simd()
+ implicit none
+ integer :: i
+ integer :: x(10)
+
+ !$omp target_data map(tofrom: x)
+ !$omp simd
+ do i=1, 10
+ end do
+ !$omp end target_data
+end subroutine
+
+! CHECK-LABEL: func.func @_QPtarget_data_simd
+! CHECK: omp.target_data
+! CHECK: omp.simd
+! CHECK-NEXT: omp.loop_nest
+! CHECK: omp.yield
+! CHECK-NEXT: }
+! CHECK-NEXT: }
+! CHECK-NOT: omp.combined
+! CHECK: omp.terminator
+! CHECK-NEXT: } {{{.*}}omp.combined{{.*}}}
+
+subroutine task_loop()
+ implicit none
+ integer :: i
+
+ !$omp task
+ !$omp loop
+ do i=1, 10
+ end do
+ !$omp end task
+end subroutine
+
+! CHECK-LABEL: func.func @_QPtask_loop
+! CHECK: omp.task
+! CHECK: omp.simd
+! CHECK-NEXT: omp.loop_nest
+! CHECK: omp.yield
+! CHECK-NEXT: }
+! CHECK-NEXT: }
+! CHECK-NOT: omp.combined
+! CHECK: omp.terminator
+! CHECK-NEXT: } {{{.*}}omp.combined{{.*}}}
+
+subroutine task_parallel()
+ implicit none
+
+ !$omp task
+ !$omp parallel
+ call foo()
+ !$omp end parallel
+ !$omp end task
+end subroutine
+
+! CHECK-LABEL: func.func @_QPtask_parallel
+! CHECK: omp.task
+! CHECK: omp.parallel
+! CHECK: omp.terminator
+! CHECK-NEXT: }
+! CHECK-NOT: omp.combined
+! CHECK: omp.terminator
+! CHECK-NEXT: } {{{.*}}omp.combined{{.*}}}
+
+subroutine task_simd()
+ implicit none
+ integer :: i
+
+ !$omp task
+ !$omp simd
+ do i=1, 10
+ end do
+ !$omp end task
+end subroutine
+
+! CHECK-LABEL: func.func @_QPtask_simd
+! CHECK: omp.task
+! CHECK: omp.simd
+! CHECK-NEXT: omp.loop_nest
+! CHECK: omp.yield
+! CHECK-NEXT: }
+! CHECK-NEXT: }
+! CHECK-NOT: omp.combined
+! CHECK: omp.terminator
+! CHECK-NEXT: } {{{.*}}omp.combined{{.*}}}
+
+subroutine teams_parallel()
+ implicit none
+
+ !$omp teams
+ !$omp parallel
+ call foo()
+ !$omp end parallel
+ !$omp end teams
+end subroutine
diff --git a/flang/test/Lower/OpenMP/multiple-entry-points.f90 b/flang/test/Lower/OpenMP/multiple-entry-points.f90
index 604b9cda4af3d..2cea046928cff 100644
--- a/flang/test/Lower/OpenMP/multiple-entry-points.f90
+++ b/flang/test/Lower/OpenMP/multiple-entry-points.f90
@@ -8,6 +8,7 @@
!CHECK: %[[V1:[a-z_0-9]+]] = arith.constant 2.000000e+00 : f32
!CHECK: = arith.mulf %[[V0]], %[[V1]] fastmath<contract> : f32
!CHECK: omp.terminator
+!CHECK: {omp.combined}
!CHECK-NOT: omp
!CHECK: return
diff --git a/flang/test/Lower/OpenMP/workshare.f90 b/flang/test/Lower/OpenMP/workshare.f90
index 8e771952f5b6d..bf7cec1f99769 100644
--- a/flang/test/Lower/OpenMP/workshare.f90
+++ b/flang/test/Lower/OpenMP/workshare.f90
@@ -12,7 +12,7 @@ subroutine sb1(arr)
!$omp end workshare
!CHECK: }
!$omp end parallel
-!CHECK: }
+!CHECK: } {omp.combined}
end subroutine
!CHECK-LABEL: func @_QPsb2
@@ -26,7 +26,7 @@ subroutine sb2(arr)
!$omp end workshare nowait
!CHECK: }
!$omp end parallel
-!CHECK: }
+!CHECK: } {omp.combined}
end subroutine
!CHECK-LABEL: func @_QPsb3
@@ -38,5 +38,5 @@ subroutine sb3(arr)
arr = 0
!$omp end parallel workshare
!CHECK: }
-!CHECK: }
+!CHECK: } {omp.combined}
end subroutine
diff --git a/flang/test/Transforms/DoConcurrent/basic_device.f90 b/flang/test/Transforms/DoConcurrent/basic_device.f90
index fd13f9c6babe0..8404e5e3a2cde 100644
--- a/flang/test/Transforms/DoConcurrent/basic_device.f90
+++ b/flang/test/Transforms/DoConcurrent/basic_device.f90
@@ -37,7 +37,7 @@ program do_concurrent_basic
! CHECK: %[[A_MAP_INFO:.*]] = omp.map.info var_ptr(%[[A_ORIG_DECL]]#1 : {{[^(]+}})
! CHECK-SAME: map_clauses(implicit, tofrom) capture(ByRef) bounds(%[[A_BOUNDS]])
- ! CHECK: omp.target
+ ! CHECK: omp.target kernel_type(spmd)
! CHECK-SAME: host_eval(%[[HOST_LB]] -> %[[LB:[[:alnum:]]+]], %[[HOST_UB]] -> %[[UB:[[:alnum:]]+]], %[[HOST_STEP]] -> %[[STEP:[[:alnum:]]+]] : index, index, index)
! CHECK-SAME: map_entries(
! CHECK-SAME: %{{[[:alnum:]]+}} -> %{{[^,]+}},
@@ -72,9 +72,9 @@ program do_concurrent_basic
! CHECK-NEXT: omp.terminator
! CHECK-NEXT: } {omp.composite}
! CHECK-NEXT: omp.terminator
- ! CHECK-NEXT: }
+ ! CHECK-NEXT: } {omp.combined}
! CHECK-NEXT: omp.terminator
- ! CHECK-NEXT: }
+ ! CHECK-NEXT: } {omp.combined}
do concurrent (i=1:10)
a(i) = i
end do
diff --git a/flang/test/Transforms/DoConcurrent/basic_host.f90 b/flang/test/Transforms/DoConcurrent/basic_host.f90
index b4eb15837d0a5..7d0f70add2815 100644
--- a/flang/test/Transforms/DoConcurrent/basic_host.f90
+++ b/flang/test/Transforms/DoConcurrent/basic_host.f90
@@ -40,7 +40,7 @@ program do_concurrent_basic
! CHECK-NEXT: }
! CHECK-NEXT: omp.terminator
- ! CHECK-NEXT: }
+ ! CHECK-NEXT: } {omp.combined}
do concurrent (i=1:10)
a(i) = i
end do
diff --git a/flang/test/Transforms/DoConcurrent/basic_host.mlir b/flang/test/Transforms/DoConcurrent/basic_host.mlir
index 5425829404d7b..ac7859bc20bc9 100644
--- a/flang/test/Transforms/DoConcurrent/basic_host.mlir
+++ b/flang/test/Transforms/DoConcurrent/basic_host.mlir
@@ -43,7 +43,7 @@ func.func @do_concurrent_basic() attributes {fir.bindc_name = "do_concurrent_bas
// CHECK-NEXT: }
// CHECK-NEXT: omp.terminator
- // CHECK-NEXT: }
+ // CHECK-NEXT: } {omp.combined}
fir.do_concurrent {
%0 = fir.alloca i32 {bindc_name = "i"}
%1:2 = hlfir.declare %0 {uniq_name = "_QFEi"} : (!fir.ref<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
diff --git a/flang/test/Transforms/DoConcurrent/local_device.mlir b/flang/test/Transforms/DoConcurrent/local_device.mlir
index 6da3db7f51e0b..0c74f4d745e4f 100644
--- a/flang/test/Transforms/DoConcurrent/local_device.mlir
+++ b/flang/test/Transforms/DoConcurrent/local_device.mlir
@@ -42,8 +42,8 @@ func.func @_QPfoo() {
// CHECK: hlfir.assign %{{.*}} to %[[LOCAL_LOOP_DECL]]#0
// CHECK: omp.yield
// CHECK: }
-// CHECK: }
-// CHECK: }
-// CHECK: }
-// CHECK: }
-// CHECK: }
+// CHECK: } {omp.composite}
+// CHECK: } {omp.composite}
+// CHECK: } {omp.composite}
+// CHECK: } {omp.combined}
+// CHECK: } {omp.combined}
diff --git a/flang/test/Transforms/DoConcurrent/locality_specifiers_simple.mlir b/flang/test/Transforms/DoConcurrent/locality_specifiers_simple.mlir
index 160c1df040680..e5064f20835f8 100644
--- a/flang/test/Transforms/DoConcurrent/locality_specifiers_simple.mlir
+++ b/flang/test/Transforms/DoConcurrent/locality_specifiers_simple.mlir
@@ -45,4 +45,4 @@ func.func @_QPlocal_spec_translation() {
// CHECK: }
// CHECK: }
// CHECK: omp.terminator
-// CHECK: }
+// CHECK: } {omp.combined}
diff --git a/flang/test/Transforms/DoConcurrent/non_const_bounds.f90 b/flang/test/Transforms/DoConcurrent/non_const_bounds.f90
index cd1bd4f98a3f5..05a3aa7650269 100644
--- a/flang/test/Transforms/DoConcurrent/non_const_bounds.f90
+++ b/flang/test/Transforms/DoConcurrent/non_const_bounds.f90
@@ -41,5 +41,5 @@ end program main
! CHECK: }
! CHECK: }
! CHECK: omp.terminator
-! CHECK: }
+! CHECK: } {omp.combined}
diff --git a/flang/test/Transforms/DoConcurrent/reduce_add.mlir b/flang/test/Transforms/DoConcurrent/reduce_add.mlir
index 1ea3e3e527335..9b70da0131164 100644
--- a/flang/test/Transforms/DoConcurrent/reduce_add.mlir
+++ b/flang/test/Transforms/DoConcurrent/reduce_add.mlir
@@ -66,7 +66,7 @@ func.func @_QPdo_concurrent_reduce() {
// CHECK: }
// CHECK: }
// CHECK: omp.terminator
-// CHECK: }
+// CHECK: } {omp.combined}
// CHECK: return
// CHECK: }
diff --git a/flang/test/Transforms/DoConcurrent/reduce_all_regions.mlir b/flang/test/Transforms/DoConcurrent/reduce_all_regions.mlir
index 3d5b8bf22af75..cfd2c78ca91d1 100644
--- a/flang/test/Transforms/DoConcurrent/reduce_all_regions.mlir
+++ b/flang/test/Transforms/DoConcurrent/reduce_all_regions.mlir
@@ -65,6 +65,6 @@ func.func @_QPdo_concurrent_reduce() {
// CHECK: }
// CHECK: }
// CHECK: omp.terminator
-// CHECK: }
+// CHECK: } {omp.combined}
// CHECK: return
// CHECK: }
diff --git a/flang/test/Transforms/DoConcurrent/reduce_device.mlir b/flang/test/Transforms/DoConcurrent/reduce_device.mlir
index a66243375297e..81cb680b58cf6 100644
--- a/flang/test/Transforms/DoConcurrent/reduce_device.mlir
+++ b/flang/test/Transforms/DoConcurrent/reduce_device.mlir
@@ -48,7 +48,7 @@ func.func @_QPfoo() {
// CHECK: %[[S_VAL:.*]] = fir.load %[[S_WS_DECL]]#0
// CHECK: %[[RED_RES:.*]] = arith.addf %[[S_VAL]], %{{.*}} fastmath<contract> : f32
// CHECK: hlfir.assign %[[RED_RES]] to %[[S_WS_DECL]]#0
-// CHECK: }
-// CHECK: }
-// CHECK: }
-// CHECK: }
+// CHECK: } {omp.composite}
+// CHECK: } {omp.composite}
+// CHECK: } {omp.composite}
+// CHECK: } {omp.combined}
diff --git a/flang/test/Transforms/DoConcurrent/reduce_device_min.f90 b/flang/test/Transforms/DoConcurrent/reduce_device_min.f90
index 509207c1db2a8..b3fa5636a3077 100644
--- a/flang/test/Transforms/DoConcurrent/reduce_device_min.f90
+++ b/flang/test/Transforms/DoConcurrent/reduce_device_min.f90
@@ -30,7 +30,7 @@ end subroutine min_reduce
! CHECK-SAME: map_clauses(implicit, tofrom) capture(ByRef)
! CHECK-SAME: -> !fir.ref<f32> {name = "_QFmin_reduceEmin_val"}
-! CHECK: omp.target
+! CHECK: omp.target kernel_type(spmd)
! CHECK-SAME: map_entries({{.*}}%[[MIN_VAL_MAP]] -> %[[MIN_VAL_ARG:[[:alnum:]]+]]{{.*}})
! CHECK: %[[MIN_VAL_DEV:.*]]:2 = hlfir.declare %[[MIN_VAL_ARG]] {{.*}} "_QFmin_reduceEmin_val"
@@ -42,4 +42,4 @@ end subroutine min_reduce
! CHECK: } {omp.composite}
! CHECK: } {omp.composite}
! CHECK: } {omp.composite}
-! CHECK: }
+! CHECK: } {omp.combined}
diff --git a/flang/test/Transforms/DoConcurrent/reduce_local.mlir b/flang/test/Transforms/DoConcurrent/reduce_local.mlir
index 0f667109e6e83..8089637f54029 100644
--- a/flang/test/Transforms/DoConcurrent/reduce_local.mlir
+++ b/flang/test/Transforms/DoConcurrent/reduce_local.mlir
@@ -77,7 +77,7 @@ fir.declare_reduction @add_reduction_i32 : i32 init {
// CHECK: }
// CHECK: }
// CHECK: omp.terminator
-// CHECK: }
+// CHECK: } {omp.combined}
// CHECK: return
// CHECK: }
diff --git a/flang/test/Transforms/DoConcurrent/reduction_symbol_resultion.f90 b/flang/test/Transforms/DoConcurrent/reduction_symbol_resultion.f90
index ab56a4f6c7e70..7278a68b73260 100644
--- a/flang/test/Transforms/DoConcurrent/reduction_symbol_resultion.f90
+++ b/flang/test/Transforms/DoConcurrent/reduction_symbol_resultion.f90
@@ -23,10 +23,10 @@ end subroutine test2
! CHECK: omp.parallel {
! CHECK: omp.wsloop reduction(@[[RED_SYM]] {{.*}} : !fir.ref<f32>) {
! CHECK: }
-! CHECK: }
+! CHECK: } {omp.combined}
! CHECK-LABEL: func.func @_QPtest2
! CHECK: omp.parallel {
! CHECK: omp.wsloop reduction(@[[RED_SYM]] {{.*}} : !fir.ref<f32>) {
! CHECK: }
-! CHECK: }
+! CHECK: } {omp.combined}
diff --git a/flang/test/Transforms/DoConcurrent/runtime_sized_array.f90 b/flang/test/Transforms/DoConcurrent/runtime_sized_array.f90
index e38474a68747f..9cf49ce3d4a2f 100644
--- a/flang/test/Transforms/DoConcurrent/runtime_sized_array.f90
+++ b/flang/test/Transforms/DoConcurrent/runtime_sized_array.f90
@@ -24,7 +24,7 @@ subroutine foo(n)
! CHECK-DAG: %[[A_MAP:.*]] = omp.map.info var_ptr(%[[A_DECL]]#1 : {{.*}}) {{.*}} {name = "_QFfooEa"}
! CHECK-DAG: %[[N_MAP:.*]] = omp.map.info var_ptr(%{{.*}} : {{.*}}) {{.*}} {name = "_QFfooEa.extent.dim0"}
-! CHECK: omp.target
+! CHECK: omp.target kernel_type(spmd)
! CHECK-SAME: map_entries(
! CHECK-SAME: %{{[[:alnum:]]+}} -> %{{[^,]+}},
! CHECK-SAME: %{{[[:alnum:]]+}} -> %{{[^,]+}},
diff --git a/flang/test/Transforms/DoConcurrent/skip_all_nested_loops.f90 b/flang/test/Transforms/DoConcurrent/skip_all_nested_loops.f90
index 2dada05396ad6..ebacd0353b90b 100644
--- a/flang/test/Transforms/DoConcurrent/skip_all_nested_loops.f90
+++ b/flang/test/Transforms/DoConcurrent/skip_all_nested_loops.f90
@@ -26,7 +26,7 @@ program main
! HOST: %[[ORIG_J_ALLOC:.*]] = fir.alloca i32 {bindc_name = "j", {{.*}}}
! HOST: %[[ORIG_J_DECL:.*]]:2 = hlfir.declare %[[ORIG_J_ALLOC]]
-! DEVICE: omp.target {{.*}}map_entries(
+! DEVICE: omp.target kernel_type(spmd) {{.*}}map_entries(
! DEVICE-SAME: %{{[[:alnum:]]+}} -> %{{[^,]+}},
! DEVICE-SAME: %{{[[:alnum:]]+}} -> %{{[^,]+}},
! DEVICE-SAME: %{{[[:alnum:]]+}} -> %{{[^,]+}},
diff --git a/flang/test/Transforms/DoConcurrent/use_loop_bounds_in_body.f90 b/flang/test/Transforms/DoConcurrent/use_loop_bounds_in_body.f90
index 07a3b5b62b5a5..a86f4759b02b9 100644
--- a/flang/test/Transforms/DoConcurrent/use_loop_bounds_in_body.f90
+++ b/flang/test/Transforms/DoConcurrent/use_loop_bounds_in_body.f90
@@ -17,7 +17,7 @@ subroutine foo(a, n)
end subroutine
! CHECK-LABEL: func.func @_QPfoo
-! CHECK: omp.target
+! CHECK: omp.target kernel_type(spmd)
! CHECK-SAME: host_eval(%{{.*}} -> %{{.*}}, %{{.*}} -> %[[N_HOST_EVAL:.*]], %{{.*}} -> %{{.*}} : index, index, index)
! CHECK-SAME: map_entries({{[^[:space:]]*}} -> {{[^[:space:]]*}},
! CHECK-SAME: {{[^[:space:]]*}} -> {{[^[:space:]]*}}, {{[^[:space:]]*}} -> {{[^[:space:]]*}},
@@ -33,8 +33,8 @@ subroutine foo(a, n)
! CHECK: hlfir.assign %[[N_VAL_CVT]] to {{.*}}
! CHECK-NEXT: omp.yield
! CHECK: }
-! CHECK: }
-! CHECK: }
-! CHECK: }
-! CHECK: }
-! CHECK: }
+! CHECK: } {omp.composite}
+! CHECK: } {omp.composite}
+! CHECK: } {omp.composite}
+! CHECK: } {omp.combined}
+! CHECK: } {omp.combined}
diff --git a/flang/test/Transforms/OpenMP/lower-workdistribute-doloop.mlir b/flang/test/Transforms/OpenMP/lower-workdistribute-doloop.mlir
index 00d10d6264ec9..320124ebab21d 100644
--- a/flang/test/Transforms/OpenMP/lower-workdistribute-doloop.mlir
+++ b/flang/test/Transforms/OpenMP/lower-workdistribute-doloop.mlir
@@ -15,7 +15,7 @@
// CHECK: omp.terminator
// CHECK: } {omp.composite}
// CHECK: omp.terminator
-// CHECK: }
+// CHECK: } {omp.combined}
// CHECK: return
// CHECK: }
func.func @x(%lb : index, %ub : index, %step : index, %b : i1, %addr : !fir.ref<index>) {
diff --git a/flang/test/Transforms/OpenMP/lower-workdistribute-fission-host.mlir b/flang/test/Transforms/OpenMP/lower-workdistribute-fission-host.mlir
index 936a487d27249..5ae81c31ba4f8 100644
--- a/flang/test/Transforms/OpenMP/lower-workdistribute-fission-host.mlir
+++ b/flang/test/Transforms/OpenMP/lower-workdistribute-fission-host.mlir
@@ -60,9 +60,9 @@
// CHECK: omp.terminator
// CHECK: } {omp.composite}
// CHECK: omp.terminator
-// CHECK: }
+// CHECK: } {omp.combined}
// CHECK: omp.terminator
-// CHECK: }
+// CHECK: } {omp.combined}
// CHECK: %[[VAL_48:.*]] = llvm.mlir.constant(0 : i32) : i32
// CHECK: %[[VAL_49:.*]] = fir.load %[[VAL_11]] : !fir.ref<index>
// CHECK: %[[VAL_50:.*]] = fir.load %[[VAL_14]] : !fir.ref<index>
diff --git a/flang/test/Transforms/OpenMP/lower-workdistribute-fission-target.mlir b/flang/test/Transforms/OpenMP/lower-workdistribute-fission-target.mlir
index 832fec201bca3..95722f101efd7 100644
--- a/flang/test/Transforms/OpenMP/lower-workdistribute-fission-target.mlir
+++ b/flang/test/Transforms/OpenMP/lower-workdistribute-fission-target.mlir
@@ -60,9 +60,9 @@
// CHECK: omp.terminator
// CHECK: } {omp.composite}
// CHECK: omp.terminator
-// CHECK: }
+// CHECK: } {omp.combined}
// CHECK: omp.terminator
-// CHECK: }
+// CHECK: } {omp.combined}
// CHECK: %[[VAL_45:.*]] = llvm.mlir.constant(0 : i32) : i32
// CHECK: %[[VAL_46:.*]] = fir.load %[[VAL_11]] : !fir.ref<index>
// CHECK: %[[VAL_47:.*]] = fir.load %[[VAL_14]] : !fir.ref<index>
diff --git a/flang/test/Transforms/OpenMP/lower-workdistribute-fission.mlir b/flang/test/Transforms/OpenMP/lower-workdistribute-fission.mlir
index c562b7009664d..e1c08bbf8bb82 100644
--- a/flang/test/Transforms/OpenMP/lower-workdistribute-fission.mlir
+++ b/flang/test/Transforms/OpenMP/lower-workdistribute-fission.mlir
@@ -22,7 +22,7 @@
// CHECK: omp.terminator
// CHECK: } {omp.composite}
// CHECK: omp.terminator
-// CHECK: }
+// CHECK: } {omp.combined}
// CHECK: fir.call @regular_side_effect_func(%[[ARG2:.*]]) : (!fir.ref<f32>) -> ()
// CHECK: fir.call @my_fir_parallel_runtime_func(%[[ARG3:.*]]) : (!fir.ref<f32>) -> ()
// CHECK: fir.do_loop %[[VAL_8:.*]] = %[[VAL_0]] to %[[VAL_2]] step %[[VAL_1]] {
diff --git a/flang/test/Transforms/OpenMP/lower-workdistribute-runtime-assign-scalar.mlir b/flang/test/Transforms/OpenMP/lower-workdistribute-runtime-assign-scalar.mlir
index 04e95c27e3c5c..ea48fcc7183ca 100644
--- a/flang/test/Transforms/OpenMP/lower-workdistribute-runtime-assign-scalar.mlir
+++ b/flang/test/Transforms/OpenMP/lower-workdistribute-runtime-assign-scalar.mlir
@@ -35,9 +35,9 @@
// CHECK: omp.terminator
// CHECK: } {omp.composite}
// CHECK: omp.terminator
-// CHECK: }
+// CHECK: } {omp.combined}
// CHECK: omp.terminator
-// CHECK: }
+// CHECK: } {omp.combined}
// CHECK: omp.terminator
// CHECK: }
// CHECK: return
diff --git a/flang/test/Transforms/omp-function-filtering-todo.mlir b/flang/test/Transforms/omp-function-filtering-todo.mlir
index cd45de88edfa9..280671ff774bc 100644
--- a/flang/test/Transforms/omp-function-filtering-todo.mlir
+++ b/flang/test/Transforms/omp-function-filtering-todo.mlir
@@ -25,9 +25,9 @@ module attributes {omp.is_gpu = true, omp.is_target_device = true} {
}
}
omp.terminator
- }
+ } {omp.combined}
omp.terminator
- }
+ } {omp.combined}
return
}
}
More information about the llvm-branch-commits
mailing list