[flang-commits] [flang] b85c39d - [Flang][OpenMP] Initial lowering of the OpenMP worksharing loop
Kiran Chandramohan via flang-commits
flang-commits at lists.llvm.org
Fri May 6 05:00:41 PDT 2022
Author: Kiran Chandramohan
Date: 2022-05-06T11:46:02Z
New Revision: b85c39dd007858aac3edd915d802ff191bd58fe3
URL: https://github.com/llvm/llvm-project/commit/b85c39dd007858aac3edd915d802ff191bd58fe3
DIFF: https://github.com/llvm/llvm-project/commit/b85c39dd007858aac3edd915d802ff191bd58fe3.diff
LOG: [Flang][OpenMP] Initial lowering of the OpenMP worksharing loop
The OpenMP worksharing loop operation in the dialect is a proper loop
operation and not a container of a loop. So we have to lower the
parse-tree OpenMP loop construct and the do-loop inside the construct
to a omp.wsloop operation and there should not be a fir.do_loop inside
it. This is achieved by skipping fir.do_loop creation and calling genFIR
for the nested evaluations in the lowering of the do construct.
Note: Handling of more clauses, parallel do, storage of loop index variable etc will come in separate patches.
Part of the upstreaming effort to move LLVM Flang from fir-dev branch of
https://github.com/flang-compiler/f18-llvm-project to the LLVM Project.
Reviewed By: peixin
Differential Revision: https://reviews.llvm.org/D125024
Co-authored-by: Sourabh Singh Tomar <SourabhSingh.Tomar at amd.com>
Co-authored-by: Shraiysh Vaishay <Shraiysh.Vaishay at amd.com>
Added:
flang/test/Lower/OpenMP/omp-wsloop.f90
Modified:
flang/lib/Lower/Bridge.cpp
flang/lib/Lower/OpenMP.cpp
Removed:
################################################################################
diff --git a/flang/lib/Lower/Bridge.cpp b/flang/lib/Lower/Bridge.cpp
index e39a1fcbfe1aa..cb3b8ce7888b1 100644
--- a/flang/lib/Lower/Bridge.cpp
+++ b/flang/lib/Lower/Bridge.cpp
@@ -1407,7 +1407,15 @@ class FirConverter : public Fortran::lower::AbstractConverter {
localSymbols.pushScope();
Fortran::lower::genOpenMPConstruct(*this, getEval(), omp);
- for (Fortran::lower::pft::Evaluation &e : getEval().getNestedEvaluations())
+ // If loop is part of an OpenMP Construct then the OpenMP dialect
+ // workshare loop operation has already been created. Only the
+ // body needs to be created here and the do_loop can be skipped.
+ Fortran::lower::pft::Evaluation *curEval =
+ std::get_if<Fortran::parser::OpenMPLoopConstruct>(&omp.u)
+ ? &getEval().getFirstNestedEvaluation()
+ : &getEval();
+
+ for (Fortran::lower::pft::Evaluation &e : curEval->getNestedEvaluations())
genFIR(e);
localSymbols.popScope();
builder->restoreInsertionPoint(insertPt);
@@ -2299,10 +2307,7 @@ class FirConverter : public Fortran::lower::AbstractConverter {
void genFIR(const Fortran::parser::IfStmt &) {} // nop
void genFIR(const Fortran::parser::IfThenStmt &) {} // nop
void genFIR(const Fortran::parser::NonLabelDoStmt &) {} // nop
-
- void genFIR(const Fortran::parser::OmpEndLoopDirective &) {
- TODO(toLocation(), "OmpEndLoopDirective lowering");
- }
+ void genFIR(const Fortran::parser::OmpEndLoopDirective &) {} // nop
void genFIR(const Fortran::parser::NamelistStmt &) {
TODO(toLocation(), "NamelistStmt lowering");
diff --git a/flang/lib/Lower/OpenMP.cpp b/flang/lib/Lower/OpenMP.cpp
index c3740123437f1..8cc18bea602fc 100644
--- a/flang/lib/Lower/OpenMP.cpp
+++ b/flang/lib/Lower/OpenMP.cpp
@@ -113,13 +113,31 @@ static void
createBodyOfOp(Op &op, Fortran::lower::AbstractConverter &converter,
mlir::Location &loc,
const Fortran::parser::OmpClauseList *clauses = nullptr,
+ const Fortran::semantics::Symbol *arg = nullptr,
bool outerCombined = false) {
fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder();
- firOpBuilder.createBlock(&op.getRegion());
+ // If an argument for the region is provided then create the block with that
+ // argument. Also update the symbol's address with the mlir argument value.
+ // e.g. For loops the argument is the induction variable. And all further
+ // uses of the induction variable should use this mlir value.
+ if (arg) {
+ firOpBuilder.createBlock(&op.getRegion(), {}, {converter.genType(*arg)},
+ {loc});
+ converter.bindSymbol(*arg, op.getRegion().front().getArgument(0));
+ } else {
+ firOpBuilder.createBlock(&op.getRegion());
+ }
auto &block = op.getRegion().back();
firOpBuilder.setInsertionPointToStart(&block);
- // Ensure the block is well-formed.
- firOpBuilder.create<mlir::omp::TerminatorOp>(loc);
+
+ // Insert the terminator.
+ if constexpr (std::is_same_v<Op, omp::WsLoopOp>) {
+ mlir::ValueRange results;
+ firOpBuilder.create<mlir::omp::YieldOp>(loc, results);
+ } else {
+ firOpBuilder.create<mlir::omp::TerminatorOp>(loc);
+ }
+
// Reset the insertion point to the start of the first block.
firOpBuilder.setInsertionPointToStart(&block);
// Handle privatization. Do not privatize if this is the outer operation.
@@ -315,7 +333,7 @@ genOMP(Fortran::lower::AbstractConverter &converter,
allocateOperands, allocatorOperands, /*reduction_vars=*/ValueRange(),
/*reductions=*/nullptr, procBindKindAttr);
createBodyOfOp<omp::ParallelOp>(parallelOp, converter, currentLocation,
- &opClauseList, /*isCombined=*/false);
+ &opClauseList);
} else if (blockDirective.v == llvm::omp::OMPD_master) {
auto masterOp =
firOpBuilder.create<mlir::omp::MasterOp>(currentLocation, argTy);
@@ -333,6 +351,122 @@ genOMP(Fortran::lower::AbstractConverter &converter,
}
}
+static void genOMP(Fortran::lower::AbstractConverter &converter,
+ Fortran::lower::pft::Evaluation &eval,
+ const Fortran::parser::OpenMPLoopConstruct &loopConstruct) {
+
+ fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder();
+ mlir::Location currentLocation = converter.getCurrentLocation();
+ llvm::SmallVector<mlir::Value> lowerBound, upperBound, step, linearVars,
+ linearStepVars, reductionVars;
+ mlir::Value scheduleChunkClauseOperand;
+ mlir::Attribute scheduleClauseOperand, collapseClauseOperand,
+ noWaitClauseOperand, orderedClauseOperand, orderClauseOperand;
+ const auto &wsLoopOpClauseList = std::get<Fortran::parser::OmpClauseList>(
+ std::get<Fortran::parser::OmpBeginLoopDirective>(loopConstruct.t).t);
+ if (llvm::omp::OMPD_do !=
+ std::get<Fortran::parser::OmpLoopDirective>(
+ std::get<Fortran::parser::OmpBeginLoopDirective>(loopConstruct.t).t)
+ .v) {
+ TODO(converter.getCurrentLocation(), "Combined worksharing loop construct");
+ }
+
+ Fortran::lower::pft::Evaluation *doConstructEval =
+ &eval.getFirstNestedEvaluation();
+
+ Fortran::lower::pft::Evaluation *doLoop =
+ &doConstructEval->getFirstNestedEvaluation();
+ auto *doStmt = doLoop->getIf<Fortran::parser::NonLabelDoStmt>();
+ assert(doStmt && "Expected do loop to be in the nested evaluation");
+ const auto &loopControl =
+ std::get<std::optional<Fortran::parser::LoopControl>>(doStmt->t);
+ const Fortran::parser::LoopControl::Bounds *bounds =
+ std::get_if<Fortran::parser::LoopControl::Bounds>(&loopControl->u);
+ assert(bounds && "Expected bounds for worksharing do loop");
+ Fortran::semantics::Symbol *iv = nullptr;
+ Fortran::lower::StatementContext stmtCtx;
+ lowerBound.push_back(fir::getBase(converter.genExprValue(
+ *Fortran::semantics::GetExpr(bounds->lower), stmtCtx)));
+ upperBound.push_back(fir::getBase(converter.genExprValue(
+ *Fortran::semantics::GetExpr(bounds->upper), stmtCtx)));
+ if (bounds->step) {
+ step.push_back(fir::getBase(converter.genExprValue(
+ *Fortran::semantics::GetExpr(bounds->step), stmtCtx)));
+ } else { // If `step` is not present, assume it as `1`.
+ step.push_back(firOpBuilder.createIntegerConstant(
+ currentLocation, firOpBuilder.getIntegerType(32), 1));
+ }
+ iv = bounds->name.thing.symbol;
+
+ // FIXME: Add support for following clauses:
+ // 1. linear
+ // 2. order
+ // 3. collapse
+ // 4. schedule (with chunk)
+ auto wsLoopOp = firOpBuilder.create<mlir::omp::WsLoopOp>(
+ currentLocation, lowerBound, upperBound, step, linearVars, linearStepVars,
+ reductionVars, /*reductions=*/nullptr,
+ scheduleClauseOperand.dyn_cast_or_null<omp::ClauseScheduleKindAttr>(),
+ scheduleChunkClauseOperand, /*schedule_modifiers=*/nullptr,
+ /*simd_modifier=*/nullptr,
+ collapseClauseOperand.dyn_cast_or_null<IntegerAttr>(),
+ noWaitClauseOperand.dyn_cast_or_null<UnitAttr>(),
+ orderedClauseOperand.dyn_cast_or_null<IntegerAttr>(),
+ orderClauseOperand.dyn_cast_or_null<omp::ClauseOrderKindAttr>(),
+ /*inclusive=*/firOpBuilder.getUnitAttr());
+
+ // Handle attribute based clauses.
+ for (const Fortran::parser::OmpClause &clause : wsLoopOpClauseList.v) {
+ if (const auto &scheduleClause =
+ std::get_if<Fortran::parser::OmpClause::Schedule>(&clause.u)) {
+ mlir::MLIRContext *context = firOpBuilder.getContext();
+ const auto &scheduleType = scheduleClause->v;
+ const auto &scheduleKind =
+ std::get<Fortran::parser::OmpScheduleClause::ScheduleType>(
+ scheduleType.t);
+ switch (scheduleKind) {
+ case Fortran::parser::OmpScheduleClause::ScheduleType::Static:
+ wsLoopOp.schedule_valAttr(omp::ClauseScheduleKindAttr::get(
+ context, omp::ClauseScheduleKind::Static));
+ break;
+ case Fortran::parser::OmpScheduleClause::ScheduleType::Dynamic:
+ wsLoopOp.schedule_valAttr(omp::ClauseScheduleKindAttr::get(
+ context, omp::ClauseScheduleKind::Dynamic));
+ break;
+ case Fortran::parser::OmpScheduleClause::ScheduleType::Guided:
+ wsLoopOp.schedule_valAttr(omp::ClauseScheduleKindAttr::get(
+ context, omp::ClauseScheduleKind::Guided));
+ break;
+ case Fortran::parser::OmpScheduleClause::ScheduleType::Auto:
+ wsLoopOp.schedule_valAttr(omp::ClauseScheduleKindAttr::get(
+ context, omp::ClauseScheduleKind::Auto));
+ break;
+ case Fortran::parser::OmpScheduleClause::ScheduleType::Runtime:
+ wsLoopOp.schedule_valAttr(omp::ClauseScheduleKindAttr::get(
+ context, omp::ClauseScheduleKind::Runtime));
+ break;
+ }
+ }
+ }
+ // In FORTRAN `nowait` clause occur at the end of `omp do` directive.
+ // i.e
+ // !$omp do
+ // <...>
+ // !$omp end do nowait
+ if (const auto &endClauseList =
+ std::get<std::optional<Fortran::parser::OmpEndLoopDirective>>(
+ loopConstruct.t)) {
+ const auto &clauseList =
+ std::get<Fortran::parser::OmpClauseList>((*endClauseList).t);
+ for (const Fortran::parser::OmpClause &clause : clauseList.v)
+ if (std::get_if<Fortran::parser::OmpClause::Nowait>(&clause.u))
+ wsLoopOp.nowaitAttr(firOpBuilder.getUnitAttr());
+ }
+
+ createBodyOfOp<omp::WsLoopOp>(wsLoopOp, converter, currentLocation,
+ &wsLoopOpClauseList, iv);
+}
+
static void
genOMP(Fortran::lower::AbstractConverter &converter,
Fortran::lower::pft::Evaluation &eval,
@@ -612,7 +746,7 @@ void Fortran::lower::genOpenMPConstruct(
genOMP(converter, eval, sectionConstruct);
},
[&](const Fortran::parser::OpenMPLoopConstruct &loopConstruct) {
- TODO(converter.getCurrentLocation(), "OpenMPLoopConstruct");
+ genOMP(converter, eval, loopConstruct);
},
[&](const Fortran::parser::OpenMPDeclarativeAllocate
&execAllocConstruct) {
diff --git a/flang/test/Lower/OpenMP/omp-wsloop.f90 b/flang/test/Lower/OpenMP/omp-wsloop.f90
new file mode 100644
index 0000000000000..52de0a2a09c8d
--- /dev/null
+++ b/flang/test/Lower/OpenMP/omp-wsloop.f90
@@ -0,0 +1,63 @@
+! This test checks lowering of OpenMP DO Directive (Worksharing).
+
+! RUN: bbc -fopenmp -emit-fir %s -o - | FileCheck %s
+
+!CHECK-LABEL: func @_QPsimple_loop()
+subroutine simple_loop
+ integer :: i
+ ! CHECK: omp.parallel
+ !$OMP PARALLEL
+ ! CHECK: %[[WS_LB:.*]] = arith.constant 1 : i32
+ ! CHECK: %[[WS_UB:.*]] = arith.constant 9 : i32
+ ! CHECK: %[[WS_STEP:.*]] = arith.constant 1 : i32
+ ! CHECK: omp.wsloop for (%[[I:.*]]) : i32 = (%[[WS_LB]]) to (%[[WS_UB]]) inclusive step (%[[WS_STEP]])
+ !$OMP DO
+ do i=1, 9
+ ! CHECK: fir.call @_FortranAioOutputInteger32({{.*}}, %[[I]]) : (!fir.ref<i8>, i32) -> i1
+ print*, i
+ end do
+ ! CHECK: omp.yield
+ !$OMP END DO
+ ! CHECK: omp.terminator
+ !$OMP END PARALLEL
+end subroutine
+
+!CHECK-LABEL: func @_QPsimple_loop_with_step()
+subroutine simple_loop_with_step
+ integer :: i
+ ! CHECK: omp.parallel
+ !$OMP PARALLEL
+ ! CHECK: %[[WS_LB:.*]] = arith.constant 1 : i32
+ ! CHECK: %[[WS_UB:.*]] = arith.constant 9 : i32
+ ! CHECK: %[[WS_STEP:.*]] = arith.constant 2 : i32
+ ! CHECK: omp.wsloop for (%[[I:.*]]) : i32 = (%[[WS_LB]]) to (%[[WS_UB]]) inclusive step (%[[WS_STEP]])
+ !$OMP DO
+ do i=1, 9, 2
+ ! CHECK: fir.call @_FortranAioOutputInteger32({{.*}}, %[[I]]) : (!fir.ref<i8>, i32) -> i1
+ print*, i
+ end do
+ ! CHECK: omp.yield
+ !$OMP END DO
+ ! CHECK: omp.terminator
+ !$OMP END PARALLEL
+end subroutine
+
+!CHECK-LABEL: func @_QPloop_with_schedule_nowait()
+subroutine loop_with_schedule_nowait
+ integer :: i
+ ! CHECK: omp.parallel
+ !$OMP PARALLEL
+ ! CHECK: %[[WS_LB:.*]] = arith.constant 1 : i32
+ ! CHECK: %[[WS_UB:.*]] = arith.constant 9 : i32
+ ! CHECK: %[[WS_STEP:.*]] = arith.constant 1 : i32
+ ! CHECK: omp.wsloop schedule(runtime) nowait for (%[[I:.*]]) : i32 = (%[[WS_LB]]) to (%[[WS_UB]]) inclusive step (%[[WS_STEP]])
+ !$OMP DO SCHEDULE(runtime)
+ do i=1, 9
+ ! CHECK: fir.call @_FortranAioOutputInteger32({{.*}}, %[[I]]) : (!fir.ref<i8>, i32) -> i1
+ print*, i
+ end do
+ ! CHECK: omp.yield
+ !$OMP END DO NOWAIT
+ ! CHECK: omp.terminator
+ !$OMP END PARALLEL
+end subroutine
More information about the flang-commits
mailing list