[Mlir-commits] [flang] [llvm] [mlir] [flang][OpenMP] Add basic support to lower `loop` directive to MLIR (PR #114199)
Kareem Ergawy
llvmlistbot at llvm.org
Wed Oct 30 04:57:33 PDT 2024
https://github.com/ergawy updated https://github.com/llvm/llvm-project/pull/114199
>From 23f1f4e94f93e1d508b75ba8cb5113f9779ddbee Mon Sep 17 00:00:00 2001
From: ergawy <kareem.ergawy at amd.com>
Date: Fri, 25 Oct 2024 04:00:52 -0500
Subject: [PATCH 1/3] [flang][OpenMP] Parase `bind` clause for `loop`
direcitve.
Adds parsing for the `bind` clause. The clause was already part of the
`loop` direcitve's definition but parsing was still missing.
---
flang/include/flang/Parser/dump-parse-tree.h | 2 +
flang/include/flang/Parser/parse-tree.h | 7 ++++
.../flang/Semantics/openmp-directive-sets.h | 2 +
flang/lib/Parser/openmp-parsers.cpp | 9 +++++
flang/lib/Semantics/check-omp-structure.cpp | 39 ++++++++++++++++++-
flang/lib/Semantics/check-omp-structure.h | 1 +
flang/lib/Semantics/resolve-directives.cpp | 1 +
.../Parser/OpenMP/target-loop-unparse.f90 | 16 +++++++-
flang/test/Semantics/OpenMP/loop-bind.f90 | 33 ++++++++++++++++
.../Semantics/OpenMP/nested-distribute.f90 | 6 +--
flang/test/Semantics/OpenMP/nested-teams.f90 | 2 +-
llvm/include/llvm/Frontend/OpenMP/OMP.td | 1 +
12 files changed, 111 insertions(+), 8 deletions(-)
create mode 100644 flang/test/Semantics/OpenMP/loop-bind.f90
diff --git a/flang/include/flang/Parser/dump-parse-tree.h b/flang/include/flang/Parser/dump-parse-tree.h
index 31ad1b7c6ce5b5..ef6a774ec2c3d2 100644
--- a/flang/include/flang/Parser/dump-parse-tree.h
+++ b/flang/include/flang/Parser/dump-parse-tree.h
@@ -551,6 +551,8 @@ class ParseTreeDumper {
NODE_ENUM(OmpGrainsizeClause, Prescriptiveness)
NODE(parser, OmpNumTasksClause)
NODE_ENUM(OmpNumTasksClause, Prescriptiveness)
+ NODE(parser, OmpBindClause)
+ NODE_ENUM(OmpBindClause, Type)
NODE(parser, OmpProcBindClause)
NODE_ENUM(OmpProcBindClause, Type)
NODE_ENUM(OmpReductionClause, ReductionModifier)
diff --git a/flang/include/flang/Parser/parse-tree.h b/flang/include/flang/Parser/parse-tree.h
index 174f4c631e9d4c..d7d996ddf4239d 100644
--- a/flang/include/flang/Parser/parse-tree.h
+++ b/flang/include/flang/Parser/parse-tree.h
@@ -3716,6 +3716,13 @@ struct OmpNumTasksClause {
std::tuple<std::optional<Prescriptiveness>, ScalarIntExpr> t;
};
+// OMP 5.2 11.7.1 bind-clause ->
+// BIND( PARALLEL | TEAMS | THREAD )
+struct OmpBindClause {
+ ENUM_CLASS(Type, Parallel, Teams, Thread)
+ WRAPPER_CLASS_BOILERPLATE(OmpBindClause, Type);
+};
+
// OpenMP Clauses
struct OmpClause {
UNION_CLASS_BOILERPLATE(OmpClause);
diff --git a/flang/include/flang/Semantics/openmp-directive-sets.h b/flang/include/flang/Semantics/openmp-directive-sets.h
index 55ef1e0ca61b9f..5e51c5c7de0e82 100644
--- a/flang/include/flang/Semantics/openmp-directive-sets.h
+++ b/flang/include/flang/Semantics/openmp-directive-sets.h
@@ -169,6 +169,7 @@ static const OmpDirectiveSet topTeamsSet{
Directive::OMPD_teams_distribute_parallel_do,
Directive::OMPD_teams_distribute_parallel_do_simd,
Directive::OMPD_teams_distribute_simd,
+ Directive::OMPD_teams_loop,
};
static const OmpDirectiveSet allTeamsSet{
@@ -366,6 +367,7 @@ static const OmpDirectiveSet nestedTeamsAllowedSet{
Directive::OMPD_distribute_parallel_do,
Directive::OMPD_distribute_parallel_do_simd,
Directive::OMPD_distribute_simd,
+ Directive::OMPD_loop,
Directive::OMPD_parallel,
Directive::OMPD_parallel_do,
Directive::OMPD_parallel_do_simd,
diff --git a/flang/lib/Parser/openmp-parsers.cpp b/flang/lib/Parser/openmp-parsers.cpp
index 5276e1ec1dcadd..e4a0192f20a105 100644
--- a/flang/lib/Parser/openmp-parsers.cpp
+++ b/flang/lib/Parser/openmp-parsers.cpp
@@ -428,6 +428,12 @@ TYPE_PARSER(construct<OmpLastprivateClause>(
pure(OmpLastprivateClause::LastprivateModifier::Conditional) / ":"),
Parser<OmpObjectList>{}))
+// OMP 5.2 11.7.1 BIND ( PARALLEL | TEAMS | THREAD )
+TYPE_PARSER(construct<OmpBindClause>(
+ "PARALLEL" >> pure(OmpBindClause::Type::Parallel) ||
+ "TEAMS" >> pure(OmpBindClause::Type::Teams) ||
+ "THREAD" >> pure(OmpBindClause::Type::Thread)))
+
TYPE_PARSER(
"ACQUIRE" >> construct<OmpClause>(construct<OmpClause::Acquire>()) ||
"ACQ_REL" >> construct<OmpClause>(construct<OmpClause::AcqRel>()) ||
@@ -442,6 +448,8 @@ TYPE_PARSER(
"ATOMIC_DEFAULT_MEM_ORDER" >>
construct<OmpClause>(construct<OmpClause::AtomicDefaultMemOrder>(
parenthesized(Parser<OmpAtomicDefaultMemOrderClause>{}))) ||
+ "BIND" >> construct<OmpClause>(construct<OmpClause::Bind>(
+ parenthesized(Parser<OmpBindClause>{}))) ||
"COLLAPSE" >> construct<OmpClause>(construct<OmpClause::Collapse>(
parenthesized(scalarIntConstantExpr))) ||
"COPYIN" >> construct<OmpClause>(construct<OmpClause::Copyin>(
@@ -623,6 +631,7 @@ TYPE_PARSER(sourced(construct<OmpLoopDirective>(first(
"TEAMS DISTRIBUTE SIMD" >>
pure(llvm::omp::Directive::OMPD_teams_distribute_simd),
"TEAMS DISTRIBUTE" >> pure(llvm::omp::Directive::OMPD_teams_distribute),
+ "TEAMS LOOP" >> pure(llvm::omp::Directive::OMPD_teams_loop),
"TILE" >> pure(llvm::omp::Directive::OMPD_tile),
"UNROLL" >> pure(llvm::omp::Directive::OMPD_unroll)))))
diff --git a/flang/lib/Semantics/check-omp-structure.cpp b/flang/lib/Semantics/check-omp-structure.cpp
index 8f3eb9fefee678..f6ad1b996c9433 100644
--- a/flang/lib/Semantics/check-omp-structure.cpp
+++ b/flang/lib/Semantics/check-omp-structure.cpp
@@ -369,13 +369,47 @@ void OmpStructureChecker::HasInvalidDistributeNesting(
"region."_err_en_US);
}
}
+void OmpStructureChecker::HasInvalidLoopBinding(
+ const parser::OpenMPLoopConstruct &x) {
+ const auto &beginLoopDir{std::get<parser::OmpBeginLoopDirective>(x.t)};
+ const auto &beginDir{std::get<parser::OmpLoopDirective>(beginLoopDir.t)};
+
+ auto teamsBindingChecker = [&](parser::MessageFixedText msg) {
+ const auto &clauseList{std::get<parser::OmpClauseList>(beginLoopDir.t)};
+ for (const auto &clause : clauseList.v) {
+ if (const auto *bindClause{
+ std::get_if<parser::OmpClause::Bind>(&clause.u)}) {
+ if (bindClause->v.v != parser::OmpBindClause::Type::Teams) {
+ context_.Say(beginDir.source, msg);
+ }
+ }
+ }
+ };
+
+ if (llvm::omp::Directive::OMPD_loop == beginDir.v &&
+ CurrentDirectiveIsNested() &&
+ OmpDirectiveSet{llvm::omp::OMPD_teams, llvm::omp::OMPD_target_teams}.test(
+ GetContextParent().directive)) {
+ teamsBindingChecker(
+ "`BIND(TEAMS)` must be specified since the `LOOP` region is "
+ "strictly nested inside a `TEAMS` region."_err_en_US);
+ }
+
+ if (OmpDirectiveSet{
+ llvm::omp::OMPD_teams_loop, llvm::omp::OMPD_target_teams_loop}
+ .test(beginDir.v)) {
+ teamsBindingChecker(
+ "`BIND(TEAMS)` must be specified since the `LOOP` directive is "
+ "combined with a `TEAMS` construct."_err_en_US);
+ }
+}
void OmpStructureChecker::HasInvalidTeamsNesting(
const llvm::omp::Directive &dir, const parser::CharBlock &source) {
if (!llvm::omp::nestedTeamsAllowedSet.test(dir)) {
context_.Say(source,
- "Only `DISTRIBUTE` or `PARALLEL` regions are allowed to be strictly "
- "nested inside `TEAMS` region."_err_en_US);
+ "Only `DISTRIBUTE`, `PARALLEL`, or `LOOP` regions are allowed to be "
+ "strictly nested inside `TEAMS` region."_err_en_US);
}
}
@@ -538,6 +572,7 @@ void OmpStructureChecker::Enter(const parser::OpenMPLoopConstruct &x) {
CheckLoopItrVariableIsInt(x);
CheckAssociatedLoopConstraints(x);
HasInvalidDistributeNesting(x);
+ HasInvalidLoopBinding(x);
if (CurrentDirectiveIsNested() &&
llvm::omp::topTeamsSet.test(GetContextParent().directive)) {
HasInvalidTeamsNesting(beginDir.v, beginDir.source);
diff --git a/flang/lib/Semantics/check-omp-structure.h b/flang/lib/Semantics/check-omp-structure.h
index 237569bc40c483..b1f19c19bd2375 100644
--- a/flang/lib/Semantics/check-omp-structure.h
+++ b/flang/lib/Semantics/check-omp-structure.h
@@ -148,6 +148,7 @@ class OmpStructureChecker
void HasInvalidTeamsNesting(
const llvm::omp::Directive &dir, const parser::CharBlock &source);
void HasInvalidDistributeNesting(const parser::OpenMPLoopConstruct &x);
+ void HasInvalidLoopBinding(const parser::OpenMPLoopConstruct &x);
// specific clause related
bool ScheduleModifierHasType(const parser::OmpScheduleClause &,
const parser::OmpScheduleModifierType::ModType &);
diff --git a/flang/lib/Semantics/resolve-directives.cpp b/flang/lib/Semantics/resolve-directives.cpp
index 5e3ad5f3b4773d..20f050c71a426b 100644
--- a/flang/lib/Semantics/resolve-directives.cpp
+++ b/flang/lib/Semantics/resolve-directives.cpp
@@ -1663,6 +1663,7 @@ bool OmpAttributeVisitor::Pre(const parser::OpenMPLoopConstruct &x) {
case llvm::omp::Directive::OMPD_teams_distribute_parallel_do:
case llvm::omp::Directive::OMPD_teams_distribute_parallel_do_simd:
case llvm::omp::Directive::OMPD_teams_distribute_simd:
+ case llvm::omp::Directive::OMPD_teams_loop:
case llvm::omp::Directive::OMPD_tile:
case llvm::omp::Directive::OMPD_unroll:
PushContext(beginDir.source, beginDir.v);
diff --git a/flang/test/Parser/OpenMP/target-loop-unparse.f90 b/flang/test/Parser/OpenMP/target-loop-unparse.f90
index 3ee2fcef075a37..b2047070496527 100644
--- a/flang/test/Parser/OpenMP/target-loop-unparse.f90
+++ b/flang/test/Parser/OpenMP/target-loop-unparse.f90
@@ -1,6 +1,8 @@
+! RUN: %flang_fc1 -fdebug-unparse -fopenmp -fopenmp-version=50 %s | \
+! RUN: FileCheck --ignore-case %s
-! RUN: %flang_fc1 -fdebug-unparse -fopenmp %s | FileCheck --ignore-case %s
-! RUN: %flang_fc1 -fdebug-dump-parse-tree -fopenmp %s | FileCheck --check-prefix="PARSE-TREE" %s
+! RUN: %flang_fc1 -fdebug-dump-parse-tree -fopenmp -fopenmp-version=50 %s | \
+! RUN: FileCheck --check-prefix="PARSE-TREE" %s
! Check for parsing of loop directive
@@ -14,6 +16,16 @@ subroutine test_loop
j = j + 1
end do
!$omp end loop
+
+ !PARSE-TREE: OmpBeginLoopDirective
+ !PARSE-TREE-NEXT: OmpLoopDirective -> llvm::omp::Directive = loop
+ !PARSE-TREE-NEXT: OmpClauseList -> OmpClause -> Bind -> OmpBindClause -> Type = Thread
+ !CHECK: !$omp loop
+ !$omp loop bind(thread)
+ do i=1,10
+ j = j + 1
+ end do
+ !$omp end loop
end subroutine
subroutine test_target_loop
diff --git a/flang/test/Semantics/OpenMP/loop-bind.f90 b/flang/test/Semantics/OpenMP/loop-bind.f90
new file mode 100644
index 00000000000000..f3aa9d19fe989e
--- /dev/null
+++ b/flang/test/Semantics/OpenMP/loop-bind.f90
@@ -0,0 +1,33 @@
+! RUN: %python %S/../test_errors.py %s %flang -fopenmp -fopenmp-version=50
+
+! OpenMP Version 5.0
+! Check OpenMP construct validity for the following directives:
+! 11.7 Loop directive
+
+program main
+ integer :: i, x
+
+ !$omp teams
+ !ERROR: `BIND(TEAMS)` must be specified since the `LOOP` region is strictly nested inside a `TEAMS` region.
+ !$omp loop bind(thread)
+ do i = 1, 10
+ x = x + 1
+ end do
+ !$omp end loop
+ !$omp end teams
+
+ !ERROR: `BIND(TEAMS)` must be specified since the `LOOP` directive is combined with a `TEAMS` construct.
+ !$omp target teams loop bind(thread)
+ do i = 1, 10
+ x = x + 1
+ end do
+ !$omp end target teams loop
+
+ !ERROR: `BIND(TEAMS)` must be specified since the `LOOP` directive is combined with a `TEAMS` construct.
+ !$omp teams loop bind(thread)
+ do i = 1, 10
+ x = x + 1
+ end do
+ !$omp end teams loop
+
+end program main
diff --git a/flang/test/Semantics/OpenMP/nested-distribute.f90 b/flang/test/Semantics/OpenMP/nested-distribute.f90
index ba8c3bf04b3377..c212763cba1df8 100644
--- a/flang/test/Semantics/OpenMP/nested-distribute.f90
+++ b/flang/test/Semantics/OpenMP/nested-distribute.f90
@@ -21,7 +21,7 @@ program main
!$omp teams
do i = 1, N
- !ERROR: Only `DISTRIBUTE` or `PARALLEL` regions are allowed to be strictly nested inside `TEAMS` region.
+ !ERROR: Only `DISTRIBUTE`, `PARALLEL`, or `LOOP` regions are allowed to be strictly nested inside `TEAMS` region.
!$omp task
do k = 1, N
a = 3.14
@@ -50,7 +50,7 @@ program main
!$omp end parallel
!$omp teams
- !ERROR: Only `DISTRIBUTE` or `PARALLEL` regions are allowed to be strictly nested inside `TEAMS` region.
+ !ERROR: Only `DISTRIBUTE`, `PARALLEL`, or `LOOP` regions are allowed to be strictly nested inside `TEAMS` region.
!$omp target
!ERROR: `DISTRIBUTE` region has to be strictly nested inside `TEAMS` region.
!$omp distribute
@@ -82,7 +82,7 @@ program main
!$omp end target teams
!$omp teams
- !ERROR: Only `DISTRIBUTE` or `PARALLEL` regions are allowed to be strictly nested inside `TEAMS` region.
+ !ERROR: Only `DISTRIBUTE`, `PARALLEL`, or `LOOP` regions are allowed to be strictly nested inside `TEAMS` region.
!$omp task
do k = 1,10
print *, "hello"
diff --git a/flang/test/Semantics/OpenMP/nested-teams.f90 b/flang/test/Semantics/OpenMP/nested-teams.f90
index 06eea12aba5595..b1a7c92a6906b4 100644
--- a/flang/test/Semantics/OpenMP/nested-teams.f90
+++ b/flang/test/Semantics/OpenMP/nested-teams.f90
@@ -59,7 +59,7 @@ program main
!$omp target
!$omp teams
- !ERROR: Only `DISTRIBUTE` or `PARALLEL` regions are allowed to be strictly nested inside `TEAMS` region.
+ !ERROR: Only `DISTRIBUTE`, `PARALLEL`, or `LOOP` regions are allowed to be strictly nested inside `TEAMS` region.
!ERROR: TEAMS region can only be strictly nested within the implicit parallel region or TARGET region
!$omp teams
a = 3.14
diff --git a/llvm/include/llvm/Frontend/OpenMP/OMP.td b/llvm/include/llvm/Frontend/OpenMP/OMP.td
index 70179bab475779..feb8eb5c2abf49 100644
--- a/llvm/include/llvm/Frontend/OpenMP/OMP.td
+++ b/llvm/include/llvm/Frontend/OpenMP/OMP.td
@@ -73,6 +73,7 @@ def OMPC_AtomicDefaultMemOrder : Clause<"atomic_default_mem_order"> {
}
def OMPC_Bind : Clause<"bind"> {
let clangClass = "OMPBindClause";
+ let flangClass = "OmpBindClause";
}
def OMP_CANCELLATION_CONSTRUCT_Parallel : ClauseVal<"parallel", 1, 1> {}
def OMP_CANCELLATION_CONSTRUCT_Loop : ClauseVal<"loop", 2, 1> {}
>From 312ce293114a233b0455d368c6e5ec69e6aeb2ea Mon Sep 17 00:00:00 2001
From: ergawy <kareem.ergawy at amd.com>
Date: Mon, 28 Oct 2024 00:04:15 -0500
Subject: [PATCH 2/3] [flang][OpenMP][MLIR] Add MLIR op for `loop` directive
Adds MLIR op that corresponds to the `loop` directive.
---
llvm/include/llvm/Frontend/OpenMP/OMP.td | 11 +++++
.../mlir/Dialect/OpenMP/OpenMPClauses.td | 25 +++++++++++
mlir/include/mlir/Dialect/OpenMP/OpenMPOps.td | 44 +++++++++++++++++++
mlir/lib/Dialect/OpenMP/IR/OpenMPDialect.cpp | 23 ++++++++++
mlir/test/Dialect/OpenMP/invalid.mlir | 30 +++++++++++++
mlir/test/Dialect/OpenMP/ops.mlir | 40 +++++++++++++++++
6 files changed, 173 insertions(+)
diff --git a/llvm/include/llvm/Frontend/OpenMP/OMP.td b/llvm/include/llvm/Frontend/OpenMP/OMP.td
index feb8eb5c2abf49..9668ab1e90ecf3 100644
--- a/llvm/include/llvm/Frontend/OpenMP/OMP.td
+++ b/llvm/include/llvm/Frontend/OpenMP/OMP.td
@@ -71,10 +71,21 @@ def OMPC_AtomicDefaultMemOrder : Clause<"atomic_default_mem_order"> {
let clangClass = "OMPAtomicDefaultMemOrderClause";
let flangClass = "OmpAtomicDefaultMemOrderClause";
}
+
+def OMP_BIND_parallel : ClauseVal<"parallel",1,1> {}
+def OMP_BIND_teams : ClauseVal<"teams",2,1> {}
+def OMP_BIND_thread : ClauseVal<"thread",3,1> { let isDefault = true; }
def OMPC_Bind : Clause<"bind"> {
let clangClass = "OMPBindClause";
let flangClass = "OmpBindClause";
+ let enumClauseValue = "BindKind";
+ let allowedClauseValues = [
+ OMP_BIND_parallel,
+ OMP_BIND_teams,
+ OMP_BIND_thread
+ ];
}
+
def OMP_CANCELLATION_CONSTRUCT_Parallel : ClauseVal<"parallel", 1, 1> {}
def OMP_CANCELLATION_CONSTRUCT_Loop : ClauseVal<"loop", 2, 1> {}
def OMP_CANCELLATION_CONSTRUCT_Sections : ClauseVal<"sections", 3, 1> {}
diff --git a/mlir/include/mlir/Dialect/OpenMP/OpenMPClauses.td b/mlir/include/mlir/Dialect/OpenMP/OpenMPClauses.td
index 886554f66afffc..855deab94b2f16 100644
--- a/mlir/include/mlir/Dialect/OpenMP/OpenMPClauses.td
+++ b/mlir/include/mlir/Dialect/OpenMP/OpenMPClauses.td
@@ -107,6 +107,31 @@ class OpenMP_CancelDirectiveNameClauseSkip<
def OpenMP_CancelDirectiveNameClause : OpenMP_CancelDirectiveNameClauseSkip<>;
+//===----------------------------------------------------------------------===//
+// V5.2: [11.7.1] `bind` clause
+//===----------------------------------------------------------------------===//
+
+class OpenMP_BindClauseSkip<
+ bit traits = false, bit arguments = false, bit assemblyFormat = false,
+ bit description = false, bit extraClassDeclaration = false
+ > : OpenMP_Clause<traits, arguments, assemblyFormat, description,
+ extraClassDeclaration> {
+ let arguments = (ins
+ OptionalAttr<BindKindAttr>:$bind_kind
+ );
+
+ let optAssemblyFormat = [{
+ `bind` `(` custom<ClauseAttr>($bind_kind) `)`
+ }];
+
+ let description = [{
+ The `bind` clause specifies the binding region of the construct on which it
+ appears.
+ }];
+}
+
+def OpenMP_BindClause : OpenMP_BindClauseSkip<>;
+
//===----------------------------------------------------------------------===//
// V5.2: [5.7.2] `copyprivate` clause
//===----------------------------------------------------------------------===//
diff --git a/mlir/include/mlir/Dialect/OpenMP/OpenMPOps.td b/mlir/include/mlir/Dialect/OpenMP/OpenMPOps.td
index 626539cb7bde42..4f16108a7a585f 100644
--- a/mlir/include/mlir/Dialect/OpenMP/OpenMPOps.td
+++ b/mlir/include/mlir/Dialect/OpenMP/OpenMPOps.td
@@ -382,6 +382,50 @@ def LoopNestOp : OpenMP_Op<"loop_nest", traits = [
// 2.9.2 Workshare Loop Construct
//===----------------------------------------------------------------------===//
+def LoopOp : OpenMP_Op<"loop", traits = [
+ DeclareOpInterfaceMethods<LoopWrapperInterface>, NoTerminator, SingleBlock,
+ AttrSizedOperandSegments
+ ], clauses = [
+ OpenMP_BindClause, OpenMP_PrivateClause, OpenMP_OrderClause,
+ OpenMP_ReductionClause
+ ], singleRegion = true> {
+ let summary = "loop construct";
+ let description = [{
+ A loop construct specifies that the logical iterations of the associated loops
+ may execute concurrently and permits the encountering threads to execute the
+ loop accordingly. A loop construct is a worksharing construct if its binding
+ region is the innermost enclosing parallel region. Otherwise it is not a work-
+ sharing region. The directive asserts that the iterations of the associated
+ loops may execute in any order, including concurrently. Each logical iteration
+ is executed once per instance of the loop region that is encountered by exactly
+ one thread that is a member of the binding thread set.
+
+ The body region can only contain a single block which must contain a single
+ operation, this operation must be an `omp.loop_nest`.
+
+ ```
+ omp.loop <clauses> {
+ omp.loop_nest (%i1, %i2) : index = (%c0, %c0) to (%c10, %c10) step (%c1, %c1) {
+ %a = load %arrA[%i1, %i2] : memref<?x?xf32>
+ %b = load %arrB[%i1, %i2] : memref<?x?xf32>
+ %sum = arith.addf %a, %b : f32
+ store %sum, %arrC[%i1, %i2] : memref<?x?xf32>
+ omp.yield
+ }
+ }
+ ```
+ }] # clausesDescription;
+
+ let assemblyFormat = clausesAssemblyFormat # [{
+ custom<PrivateReductionRegion>($region, $private_vars, type($private_vars),
+ $private_syms, $reduction_vars, type($reduction_vars), $reduction_byref,
+ $reduction_syms) attr-dict
+ }];
+
+ let hasVerifier = 1;
+ let hasRegionVerifier = 1;
+}
+
def WsloopOp : OpenMP_Op<"wsloop", traits = [
AttrSizedOperandSegments, DeclareOpInterfaceMethods<ComposableOpInterface>,
DeclareOpInterfaceMethods<LoopWrapperInterface>, NoTerminator,
diff --git a/mlir/lib/Dialect/OpenMP/IR/OpenMPDialect.cpp b/mlir/lib/Dialect/OpenMP/IR/OpenMPDialect.cpp
index e1df647d6a3c71..39accbadc8e276 100644
--- a/mlir/lib/Dialect/OpenMP/IR/OpenMPDialect.cpp
+++ b/mlir/lib/Dialect/OpenMP/IR/OpenMPDialect.cpp
@@ -1948,6 +1948,29 @@ LogicalResult LoopWrapperInterface::verifyImpl() {
return success();
}
+//===----------------------------------------------------------------------===//
+// LoopOp
+//===----------------------------------------------------------------------===//
+
+LogicalResult LoopOp::verify() {
+ return verifyReductionVarList(*this, getReductionSyms(), getReductionVars(),
+ getReductionByref());
+}
+
+LogicalResult LoopOp::verifyRegions() {
+ Region ®ion = getRegion();
+
+ // Minimal amount of checks to verify the only nested op is an
+ // `omp.loop_nest`. A more extensive vierfication is done by the
+ // `LoopWrapperInterface` trait but the difference is that `omp.loop` cannot
+ // have another nested `LoopWrapperInterface`.
+ if (range_size(region.getOps()) != 1 || !isa<LoopNestOp>(*region.op_begin()))
+ return emitError() << "`omp.loop` expected to have a single nested "
+ "operation which is a `omp.loop_nest`";
+
+ return success();
+}
+
//===----------------------------------------------------------------------===//
// WsloopOp
//===----------------------------------------------------------------------===//
diff --git a/mlir/test/Dialect/OpenMP/invalid.mlir b/mlir/test/Dialect/OpenMP/invalid.mlir
index fd89ec31c64a60..194daf8c2edf34 100644
--- a/mlir/test/Dialect/OpenMP/invalid.mlir
+++ b/mlir/test/Dialect/OpenMP/invalid.mlir
@@ -2577,3 +2577,33 @@ func.func @omp_taskloop_invalid_composite(%lb: index, %ub: index, %step: index)
} {omp.composite}
return
}
+
+// -----
+
+func.func @omp_loop_invalid_nesting(%lb : index, %ub : index, %step : index) {
+
+ // expected-error @below {{`omp.loop` expected to have a single nested operation which is a `omp.loop_nest`}}
+ omp.loop {
+ omp.simd {
+ omp.loop_nest (%iv) : index = (%lb) to (%ub) step (%step) {
+ omp.yield
+ }
+ } {omp.composite}
+ }
+
+ return
+}
+
+// -----
+
+func.func @omp_loop_invalid_nesting(%lb : index, %ub : index, %step : index) {
+
+ // expected-error @below {{custom op 'omp.loop' invalid clause value: 'dummy_value'}}
+ omp.loop bind(dummy_value) {
+ omp.loop_nest (%iv) : index = (%lb) to (%ub) step (%step) {
+ omp.yield
+ }
+ }
+
+ return
+}
diff --git a/mlir/test/Dialect/OpenMP/ops.mlir b/mlir/test/Dialect/OpenMP/ops.mlir
index 6f11b451fa00a3..47b8890b851c10 100644
--- a/mlir/test/Dialect/OpenMP/ops.mlir
+++ b/mlir/test/Dialect/OpenMP/ops.mlir
@@ -2749,3 +2749,43 @@ func.func @omp_target_private(%map1: memref<?xi32>, %map2: memref<?xi32>, %priv_
return
}
+
+// CHECK-LABEL: omp_loop
+func.func @omp_loop(%lb : index, %ub : index, %step : index) {
+ // CHECK: omp.loop {
+ omp.loop {
+ // CHECK: omp.loop_nest {{.*}} {
+ omp.loop_nest (%iv) : index = (%lb) to (%ub) step (%step) {
+ // CHECK: omp.yield
+ omp.yield
+ // CHECK: }
+ }
+ // CHECK: }
+ }
+
+ // CHECK: omp.loop bind(teams) {
+ omp.loop bind(teams) {
+ omp.loop_nest (%iv) : index = (%lb) to (%ub) step (%step) {
+ omp.yield
+ }
+ // CHECK: }
+ }
+
+ // CHECK: omp.loop bind(parallel) {
+ omp.loop bind(parallel) {
+ omp.loop_nest (%iv) : index = (%lb) to (%ub) step (%step) {
+ omp.yield
+ }
+ // CHECK: }
+ }
+
+ // CHECK: omp.loop bind(thread) {
+ omp.loop bind(thread) {
+ omp.loop_nest (%iv) : index = (%lb) to (%ub) step (%step) {
+ omp.yield
+ }
+ // CHECK: }
+ }
+
+ return
+}
>From 860d1a00a58778924dea99ca5bee671e01fbd61c Mon Sep 17 00:00:00 2001
From: ergawy <kareem.ergawy at amd.com>
Date: Wed, 30 Oct 2024 04:40:14 -0500
Subject: [PATCH 3/3] [flang][OpenMP] Add basic support to lower `loop` to MLIR
Adds initial support for lowering the `loop` directive to MLIR.
The PR includes basic suport and testing for the following clauses:
* `collapse`
* `order`
* `private`
* `reduction`
---
flang/lib/Lower/OpenMP/OpenMP.cpp | 48 ++++++++++-
.../test/Lower/OpenMP/Todo/loop-directive.f90 | 15 ----
flang/test/Lower/OpenMP/loop-directive.f90 | 82 +++++++++++++++++++
mlir/include/mlir/Dialect/OpenMP/OpenMPOps.td | 4 +
.../Conversion/OpenMPToLLVM/OpenMPToLLVM.cpp | 3 +-
mlir/lib/Dialect/OpenMP/IR/OpenMPDialect.cpp | 11 +++
6 files changed, 146 insertions(+), 17 deletions(-)
delete mode 100644 flang/test/Lower/OpenMP/Todo/loop-directive.f90
create mode 100644 flang/test/Lower/OpenMP/loop-directive.f90
diff --git a/flang/lib/Lower/OpenMP/OpenMP.cpp b/flang/lib/Lower/OpenMP/OpenMP.cpp
index 01a40d6e2204ef..6dd1583624b1f1 100644
--- a/flang/lib/Lower/OpenMP/OpenMP.cpp
+++ b/flang/lib/Lower/OpenMP/OpenMP.cpp
@@ -2023,6 +2023,52 @@ static void genStandaloneDo(lower::AbstractConverter &converter,
llvm::omp::Directive::OMPD_do, dsp);
}
+static void genLoopClauses(
+ lower::AbstractConverter &converter, semantics::SemanticsContext &semaCtx,
+ const List<Clause> &clauses, mlir::Location loc,
+ mlir::omp::LoopOperands &clauseOps,
+ llvm::SmallVectorImpl<const semantics::Symbol *> &reductionSyms) {
+ ClauseProcessor cp(converter, semaCtx, clauses);
+ cp.processOrder(clauseOps);
+ cp.processReduction(loc, clauseOps, reductionSyms);
+ cp.processTODO<clause::Bind, clause::Lastprivate>(
+ loc, llvm::omp::Directive::OMPD_loop);
+}
+
+static void genLoopOp(lower::AbstractConverter &converter,
+ lower::SymMap &symTable,
+ semantics::SemanticsContext &semaCtx,
+ lower::pft::Evaluation &eval, mlir::Location loc,
+ const ConstructQueue &queue,
+ ConstructQueue::const_iterator item) {
+ mlir::omp::LoopOperands loopClauseOps;
+ llvm::SmallVector<const semantics::Symbol *> loopReductionSyms;
+ genLoopClauses(converter, semaCtx, item->clauses, loc, loopClauseOps,
+ loopReductionSyms);
+
+ DataSharingProcessor dsp(converter, semaCtx, item->clauses, eval,
+ /*shouldCollectPreDeterminedSymbols=*/true,
+ /*useDelayedPrivatization=*/true, &symTable);
+ dsp.processStep1(&loopClauseOps);
+
+ mlir::omp::LoopNestOperands loopNestClauseOps;
+ llvm::SmallVector<const semantics::Symbol *> iv;
+ genLoopNestClauses(converter, semaCtx, eval, item->clauses, loc,
+ loopNestClauseOps, iv);
+
+ EntryBlockArgs loopArgs;
+ loopArgs.priv.syms = dsp.getDelayedPrivSymbols();
+ loopArgs.priv.vars = loopClauseOps.privateVars;
+ loopArgs.reduction.syms = loopReductionSyms;
+ loopArgs.reduction.vars = loopClauseOps.reductionVars;
+
+ auto loopOp =
+ genWrapperOp<mlir::omp::LoopOp>(converter, loc, loopClauseOps, loopArgs);
+ genLoopNestOp(converter, symTable, semaCtx, eval, loc, queue, item,
+ loopNestClauseOps, iv, {{loopOp, loopArgs}},
+ llvm::omp::Directive::OMPD_loop, dsp);
+}
+
static void genStandaloneParallel(lower::AbstractConverter &converter,
lower::SymMap &symTable,
semantics::SemanticsContext &semaCtx,
@@ -2459,7 +2505,7 @@ static void genOMPDispatch(lower::AbstractConverter &converter,
genStandaloneDo(converter, symTable, semaCtx, eval, loc, queue, item);
break;
case llvm::omp::Directive::OMPD_loop:
- TODO(loc, "Unhandled directive " + llvm::omp::getOpenMPDirectiveName(dir));
+ genLoopOp(converter, symTable, semaCtx, eval, loc, queue, item);
break;
case llvm::omp::Directive::OMPD_masked:
genMaskedOp(converter, symTable, semaCtx, eval, loc, queue, item);
diff --git a/flang/test/Lower/OpenMP/Todo/loop-directive.f90 b/flang/test/Lower/OpenMP/Todo/loop-directive.f90
deleted file mode 100644
index f1aea70458aa6c..00000000000000
--- a/flang/test/Lower/OpenMP/Todo/loop-directive.f90
+++ /dev/null
@@ -1,15 +0,0 @@
-! This test checks lowering of OpenMP loop Directive.
-
-! RUN: %not_todo_cmd bbc -emit-fir -fopenmp -o - %s 2>&1 | FileCheck %s
-! RUN: %not_todo_cmd %flang_fc1 -emit-fir -fopenmp -o - %s 2>&1 | FileCheck %s
-
-! CHECK: not yet implemented: Unhandled directive loop
-subroutine test_loop()
- integer :: i, j = 1
- !$omp loop
- do i=1,10
- j = j + 1
- end do
- !$omp end loop
-end subroutine
-
diff --git a/flang/test/Lower/OpenMP/loop-directive.f90 b/flang/test/Lower/OpenMP/loop-directive.f90
new file mode 100644
index 00000000000000..cfef8f84c3668f
--- /dev/null
+++ b/flang/test/Lower/OpenMP/loop-directive.f90
@@ -0,0 +1,82 @@
+! This test checks lowering of OpenMP loop Directive.
+
+! RUN: bbc -emit-hlfir -fopenmp -fopenmp-version=50 -o - %s 2>&1 | FileCheck %s
+! RUN: %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=50 -o - %s 2>&1 | FileCheck %s
+
+! CHECK: omp.declare_reduction @[[RED:add_reduction_i32]] : i32
+! CHECK: omp.private {type = private} @[[DUMMY_PRIV:.*test_privateEdummy_private.*]] : !fir.ref<i32>
+! CHECK: omp.private {type = private} @[[I_PRIV:.*test_no_clausesEi.*]] : !fir.ref<i32>
+
+! CHECK-LABEL: func.func @_QPtest_no_clauses
+subroutine test_no_clauses()
+ integer :: i, j, dummy = 1
+
+ ! CHECK: omp.loop private(@[[I_PRIV]] %{{.*}}#0 -> %[[ARG:.*]] : !fir.ref<i32>) {
+ ! CHECK: omp.loop_nest (%[[IV:.*]]) : i32 = (%{{.*}}) to (%{{.*}}) {{.*}} {
+ ! CHECK: %[[ARG_DECL:.*]]:2 = hlfir.declare %[[ARG]]
+ ! CHECK: fir.store %[[IV]] to %[[ARG_DECL]]#1 : !fir.ref<i32>
+ ! CHECK: }
+ ! CHECK: }
+ !$omp loop
+ do i=1,10
+ dummy = dummy + 1
+ end do
+ !$omp end loop
+end subroutine
+
+! CHECK-LABEL: func.func @_QPtest_collapse
+subroutine test_collapse()
+ integer :: i, j, dummy = 1
+ ! CHECK: omp.loop private(@{{.*}} %{{.*}}#0 -> %{{.*}}, @{{.*}} %{{.*}}#0 -> %{{.*}} : {{.*}}) {
+ ! CHECK: omp.loop_nest (%{{.*}}, %{{.*}}) : i32 {{.*}} {
+ ! CHECK: }
+ ! CHECK: }
+ !$omp loop collapse(2)
+ do i=1,10
+ do j=2,20
+ dummy = dummy + 1
+ end do
+ end do
+ !$omp end loop
+end subroutine
+
+! CHECK-LABEL: func.func @_QPtest_private
+subroutine test_private()
+ integer :: i, dummy = 1
+ ! CHECK: omp.loop private(@[[DUMMY_PRIV]] %{{.*}}#0 -> %{{.*}}, @{{.*}} %{{.*}}#0 -> %{{.*}} : {{.*}}) {
+ ! CHECK: omp.loop_nest (%{{.*}}) : i32 = (%{{.*}}) to (%{{.*}}) {{.*}} {
+ ! CHECK: }
+ ! CHECK: }
+ !$omp loop private(dummy)
+ do i=1,10
+ dummy = dummy + 1
+ end do
+ !$omp end loop
+end subroutine
+
+
+! CHECK-LABEL: func.func @_QPtest_order
+subroutine test_order()
+ integer :: i, dummy = 1
+ ! CHECK: omp.loop order(reproducible:concurrent) private(@{{.*}} %{{.*}}#0 -> %{{.*}} : {{.*}}) {
+ ! CHECK: }
+ !$omp loop order(concurrent)
+ do i=1,10
+ dummy = dummy + 1
+ end do
+ !$omp end loop
+end subroutine
+
+! CHECK-LABEL: func.func @_QPtest_reduction
+subroutine test_reduction()
+ integer :: i, dummy = 1
+
+ ! CHECK: omp.loop private(@{{.*}} %{{.*}}#0 -> %{{.*}} : !{{.*}}) reduction
+ ! CHECK-SAME: (@[[RED]] %{{.*}}#0 -> %{{.*}} : !{{.*}}) {
+ ! CHECK: }
+ !$omp loop reduction(+:dummy)
+ do i=1,10
+ dummy = dummy + 1
+ end do
+ !$omp end loop
+end subroutine
diff --git a/mlir/include/mlir/Dialect/OpenMP/OpenMPOps.td b/mlir/include/mlir/Dialect/OpenMP/OpenMPOps.td
index 4f16108a7a585f..89c34e1410f6bd 100644
--- a/mlir/include/mlir/Dialect/OpenMP/OpenMPOps.td
+++ b/mlir/include/mlir/Dialect/OpenMP/OpenMPOps.td
@@ -422,6 +422,10 @@ def LoopOp : OpenMP_Op<"loop", traits = [
$reduction_syms) attr-dict
}];
+ let builders = [
+ OpBuilder<(ins CArg<"const LoopOperands &">:$clauses)>
+ ];
+
let hasVerifier = 1;
let hasRegionVerifier = 1;
}
diff --git a/mlir/lib/Conversion/OpenMPToLLVM/OpenMPToLLVM.cpp b/mlir/lib/Conversion/OpenMPToLLVM/OpenMPToLLVM.cpp
index aa824a95b1574a..32db173c9c34a9 100644
--- a/mlir/lib/Conversion/OpenMPToLLVM/OpenMPToLLVM.cpp
+++ b/mlir/lib/Conversion/OpenMPToLLVM/OpenMPToLLVM.cpp
@@ -238,7 +238,7 @@ void mlir::configureOpenMPToLLVMConversionLegality(
omp::ParallelOp, omp::PrivateClauseOp, omp::SectionOp, omp::SectionsOp,
omp::SimdOp, omp::SingleOp, omp::TargetDataOp, omp::TargetOp,
omp::TaskgroupOp, omp::TaskloopOp, omp::TaskOp, omp::TeamsOp,
- omp::WsloopOp>([&](Operation *op) {
+ omp::WsloopOp, omp::LoopOp>([&](Operation *op) {
return std::all_of(op->getRegions().begin(), op->getRegions().end(),
[&](Region ®ion) {
return typeConverter.isLegal(®ion);
@@ -284,6 +284,7 @@ void mlir::populateOpenMPToLLVMConversionPatterns(LLVMTypeConverter &converter,
RegionOpConversion<omp::TargetOp>, RegionOpConversion<omp::TaskgroupOp>,
RegionOpConversion<omp::TaskloopOp>, RegionOpConversion<omp::TaskOp>,
RegionOpConversion<omp::TeamsOp>, RegionOpConversion<omp::WsloopOp>,
+ RegionOpConversion<omp::LoopOp>,
RegionOpWithVarOperandsConversion<omp::AtomicUpdateOp>>(converter);
}
diff --git a/mlir/lib/Dialect/OpenMP/IR/OpenMPDialect.cpp b/mlir/lib/Dialect/OpenMP/IR/OpenMPDialect.cpp
index 39accbadc8e276..c2fc1b8aa554b6 100644
--- a/mlir/lib/Dialect/OpenMP/IR/OpenMPDialect.cpp
+++ b/mlir/lib/Dialect/OpenMP/IR/OpenMPDialect.cpp
@@ -1952,6 +1952,17 @@ LogicalResult LoopWrapperInterface::verifyImpl() {
// LoopOp
//===----------------------------------------------------------------------===//
+void LoopOp::build(OpBuilder &builder, OperationState &state,
+ const LoopOperands &clauses) {
+ MLIRContext *ctx = builder.getContext();
+
+ LoopOp::build(builder, state, clauses.bindKind, clauses.privateVars,
+ makeArrayAttr(ctx, clauses.privateSyms), clauses.order,
+ clauses.orderMod, clauses.reductionVars,
+ makeDenseBoolArrayAttr(ctx, clauses.reductionByref),
+ makeArrayAttr(ctx, clauses.reductionSyms));
+}
+
LogicalResult LoopOp::verify() {
return verifyReductionVarList(*this, getReductionSyms(), getReductionVars(),
getReductionByref());
More information about the Mlir-commits
mailing list