Skip to content

Commit 81f544d

Browse files
authored
[flang][OpenMP] Rewrite omp.loop to semantically equivalent ops (#115443)
Introduces a new conversion pass that rewrites `omp.loop` ops to their semantically equivalent op nests bases on the surrounding/binding context of the `loop` op. Not all forms of `omp.loop` are supported yet. See `isLoopConversionSupported` for more info on which forms are supported.
1 parent 3a11527 commit 81f544d

File tree

11 files changed

+514
-97
lines changed

11 files changed

+514
-97
lines changed
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
//===-- include/flang/Common/OpenMP-utils.h --------------------*- C++ -*-====//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#ifndef FORTRAN_COMMON_OPENMP_UTILS_H_
10+
#define FORTRAN_COMMON_OPENMP_UTILS_H_
11+
12+
#include "flang/Semantics/symbol.h"
13+
14+
#include "mlir/IR/Builders.h"
15+
#include "mlir/IR/Value.h"
16+
17+
#include "llvm/ADT/ArrayRef.h"
18+
19+
namespace Fortran::common::openmp {
20+
/// Structure holding the information needed to create and bind entry block
21+
/// arguments associated to a single clause.
22+
struct EntryBlockArgsEntry {
23+
llvm::ArrayRef<const Fortran::semantics::Symbol *> syms;
24+
llvm::ArrayRef<mlir::Value> vars;
25+
26+
bool isValid() const {
27+
// This check allows specifying a smaller number of symbols than values
28+
// because in some case cases a single symbol generates multiple block
29+
// arguments.
30+
return syms.size() <= vars.size();
31+
}
32+
};
33+
34+
/// Structure holding the information needed to create and bind entry block
35+
/// arguments associated to all clauses that can define them.
36+
struct EntryBlockArgs {
37+
EntryBlockArgsEntry inReduction;
38+
EntryBlockArgsEntry map;
39+
EntryBlockArgsEntry priv;
40+
EntryBlockArgsEntry reduction;
41+
EntryBlockArgsEntry taskReduction;
42+
EntryBlockArgsEntry useDeviceAddr;
43+
EntryBlockArgsEntry useDevicePtr;
44+
45+
bool isValid() const {
46+
return inReduction.isValid() && map.isValid() && priv.isValid() &&
47+
reduction.isValid() && taskReduction.isValid() &&
48+
useDeviceAddr.isValid() && useDevicePtr.isValid();
49+
}
50+
51+
auto getSyms() const {
52+
return llvm::concat<const Fortran::semantics::Symbol *const>(
53+
inReduction.syms, map.syms, priv.syms, reduction.syms,
54+
taskReduction.syms, useDeviceAddr.syms, useDevicePtr.syms);
55+
}
56+
57+
auto getVars() const {
58+
return llvm::concat<const mlir::Value>(inReduction.vars, map.vars,
59+
priv.vars, reduction.vars, taskReduction.vars, useDeviceAddr.vars,
60+
useDevicePtr.vars);
61+
}
62+
};
63+
64+
mlir::Block *genEntryBlock(
65+
mlir::OpBuilder &builder, const EntryBlockArgs &args, mlir::Region &region);
66+
} // namespace Fortran::common::openmp
67+
68+
#endif // FORTRAN_COMMON_OPENMP_UTILS_H_

flang/include/flang/Optimizer/OpenMP/Passes.td

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,9 +50,29 @@ def FunctionFilteringPass : Pass<"omp-function-filtering"> {
5050
];
5151
}
5252

53+
5354
// Needs to be scheduled on Module as we create functions in it
5455
def LowerWorkshare : Pass<"lower-workshare", "::mlir::ModuleOp"> {
5556
let summary = "Lower workshare construct";
5657
}
5758

59+
def GenericLoopConversionPass
60+
: Pass<"omp-generic-loop-conversion", "mlir::func::FuncOp"> {
61+
let summary = "Converts OpenMP generic `omp.loop` to semantically "
62+
"equivalent OpenMP ops";
63+
let description = [{
64+
Rewrites `omp.loop` ops to their semantically equivalent nest of ops. The
65+
rewrite depends on the nesting/combination structure of the `loop` op
66+
within its surrounding context as well as its `bind` clause value.
67+
68+
We assume for now that all `omp.loop` ops will occur inside `FuncOp`'s. This
69+
will most likely remain the case in the future; even if, for example, we
70+
need a loop in copying data for a `firstprivate` variable, this loop will
71+
be nested in a constructor, an overloaded operator, or a runtime function.
72+
}];
73+
let dependentDialects = [
74+
"mlir::omp::OpenMPDialect"
75+
];
76+
}
77+
5878
#endif //FORTRAN_OPTIMIZER_OPENMP_PASSES

flang/lib/Common/CMakeLists.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,9 +40,13 @@ add_flang_library(FortranCommon
4040
default-kinds.cpp
4141
idioms.cpp
4242
LangOptions.cpp
43+
OpenMP-utils.cpp
4344
Version.cpp
4445
${version_inc}
4546

4647
LINK_COMPONENTS
4748
Support
49+
50+
LINK_LIBS
51+
MLIRIR
4852
)

flang/lib/Common/OpenMP-utils.cpp

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
//===-- include/flang/Common/OpenMP-utils.cpp ------------------*- C++ -*-====//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#include "flang/Common/OpenMP-utils.h"
10+
11+
#include "mlir/IR/OpDefinition.h"
12+
13+
namespace Fortran::common::openmp {
14+
mlir::Block *genEntryBlock(mlir::OpBuilder &builder, const EntryBlockArgs &args,
15+
mlir::Region &region) {
16+
assert(args.isValid() && "invalid args");
17+
assert(region.empty() && "non-empty region");
18+
19+
llvm::SmallVector<mlir::Type> types;
20+
llvm::SmallVector<mlir::Location> locs;
21+
unsigned numVars = args.inReduction.vars.size() + args.map.vars.size() +
22+
args.priv.vars.size() + args.reduction.vars.size() +
23+
args.taskReduction.vars.size() + args.useDeviceAddr.vars.size() +
24+
args.useDevicePtr.vars.size();
25+
types.reserve(numVars);
26+
locs.reserve(numVars);
27+
28+
auto extractTypeLoc = [&types, &locs](llvm::ArrayRef<mlir::Value> vals) {
29+
llvm::transform(vals, std::back_inserter(types),
30+
[](mlir::Value v) { return v.getType(); });
31+
llvm::transform(vals, std::back_inserter(locs),
32+
[](mlir::Value v) { return v.getLoc(); });
33+
};
34+
35+
// Populate block arguments in clause name alphabetical order to match
36+
// expected order by the BlockArgOpenMPOpInterface.
37+
extractTypeLoc(args.inReduction.vars);
38+
extractTypeLoc(args.map.vars);
39+
extractTypeLoc(args.priv.vars);
40+
extractTypeLoc(args.reduction.vars);
41+
extractTypeLoc(args.taskReduction.vars);
42+
extractTypeLoc(args.useDeviceAddr.vars);
43+
extractTypeLoc(args.useDevicePtr.vars);
44+
45+
return builder.createBlock(&region, {}, types, locs);
46+
}
47+
} // namespace Fortran::common::openmp

flang/lib/Lower/OpenMP/OpenMP.cpp

Lines changed: 9 additions & 97 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include "DirectivesCommon.h"
2020
#include "ReductionProcessor.h"
2121
#include "Utils.h"
22+
#include "flang/Common/OpenMP-utils.h"
2223
#include "flang/Common/idioms.h"
2324
#include "flang/Lower/Bridge.h"
2425
#include "flang/Lower/ConvertExpr.h"
@@ -41,57 +42,12 @@
4142
#include "llvm/Frontend/OpenMP/OMPConstants.h"
4243

4344
using namespace Fortran::lower::omp;
45+
using namespace Fortran::common::openmp;
4446

4547
//===----------------------------------------------------------------------===//
4648
// Code generation helper functions
4749
//===----------------------------------------------------------------------===//
4850

49-
namespace {
50-
/// Structure holding the information needed to create and bind entry block
51-
/// arguments associated to a single clause.
52-
struct EntryBlockArgsEntry {
53-
llvm::ArrayRef<const semantics::Symbol *> syms;
54-
llvm::ArrayRef<mlir::Value> vars;
55-
56-
bool isValid() const {
57-
// This check allows specifying a smaller number of symbols than values
58-
// because in some case cases a single symbol generates multiple block
59-
// arguments.
60-
return syms.size() <= vars.size();
61-
}
62-
};
63-
64-
/// Structure holding the information needed to create and bind entry block
65-
/// arguments associated to all clauses that can define them.
66-
struct EntryBlockArgs {
67-
EntryBlockArgsEntry inReduction;
68-
EntryBlockArgsEntry map;
69-
EntryBlockArgsEntry priv;
70-
EntryBlockArgsEntry reduction;
71-
EntryBlockArgsEntry taskReduction;
72-
EntryBlockArgsEntry useDeviceAddr;
73-
EntryBlockArgsEntry useDevicePtr;
74-
75-
bool isValid() const {
76-
return inReduction.isValid() && map.isValid() && priv.isValid() &&
77-
reduction.isValid() && taskReduction.isValid() &&
78-
useDeviceAddr.isValid() && useDevicePtr.isValid();
79-
}
80-
81-
auto getSyms() const {
82-
return llvm::concat<const semantics::Symbol *const>(
83-
inReduction.syms, map.syms, priv.syms, reduction.syms,
84-
taskReduction.syms, useDeviceAddr.syms, useDevicePtr.syms);
85-
}
86-
87-
auto getVars() const {
88-
return llvm::concat<const mlir::Value>(
89-
inReduction.vars, map.vars, priv.vars, reduction.vars,
90-
taskReduction.vars, useDeviceAddr.vars, useDevicePtr.vars);
91-
}
92-
};
93-
} // namespace
94-
9551
static void genOMPDispatch(lower::AbstractConverter &converter,
9652
lower::SymMap &symTable,
9753
semantics::SemanticsContext &semaCtx,
@@ -625,50 +581,6 @@ static void genLoopVars(
625581
firOpBuilder.setInsertionPointAfter(storeOp);
626582
}
627583

628-
/// Create an entry block for the given region, including the clause-defined
629-
/// arguments specified.
630-
///
631-
/// \param [in] converter - PFT to MLIR conversion interface.
632-
/// \param [in] args - entry block arguments information for the given
633-
/// operation.
634-
/// \param [in] region - Empty region in which to create the entry block.
635-
static mlir::Block *genEntryBlock(lower::AbstractConverter &converter,
636-
const EntryBlockArgs &args,
637-
mlir::Region &region) {
638-
assert(args.isValid() && "invalid args");
639-
assert(region.empty() && "non-empty region");
640-
fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder();
641-
642-
llvm::SmallVector<mlir::Type> types;
643-
llvm::SmallVector<mlir::Location> locs;
644-
unsigned numVars = args.inReduction.vars.size() + args.map.vars.size() +
645-
args.priv.vars.size() + args.reduction.vars.size() +
646-
args.taskReduction.vars.size() +
647-
args.useDeviceAddr.vars.size() +
648-
args.useDevicePtr.vars.size();
649-
types.reserve(numVars);
650-
locs.reserve(numVars);
651-
652-
auto extractTypeLoc = [&types, &locs](llvm::ArrayRef<mlir::Value> vals) {
653-
llvm::transform(vals, std::back_inserter(types),
654-
[](mlir::Value v) { return v.getType(); });
655-
llvm::transform(vals, std::back_inserter(locs),
656-
[](mlir::Value v) { return v.getLoc(); });
657-
};
658-
659-
// Populate block arguments in clause name alphabetical order to match
660-
// expected order by the BlockArgOpenMPOpInterface.
661-
extractTypeLoc(args.inReduction.vars);
662-
extractTypeLoc(args.map.vars);
663-
extractTypeLoc(args.priv.vars);
664-
extractTypeLoc(args.reduction.vars);
665-
extractTypeLoc(args.taskReduction.vars);
666-
extractTypeLoc(args.useDeviceAddr.vars);
667-
extractTypeLoc(args.useDevicePtr.vars);
668-
669-
return firOpBuilder.createBlock(&region, {}, types, locs);
670-
}
671-
672584
static void
673585
markDeclareTarget(mlir::Operation *op, lower::AbstractConverter &converter,
674586
mlir::omp::DeclareTargetCaptureClause captureClause,
@@ -921,7 +833,7 @@ static void genBodyOfTargetDataOp(
921833
ConstructQueue::const_iterator item) {
922834
fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder();
923835

924-
genEntryBlock(converter, args, dataOp.getRegion());
836+
genEntryBlock(firOpBuilder, args, dataOp.getRegion());
925837
bindEntryBlockArgs(converter, dataOp, args);
926838

927839
// Insert dummy instruction to remember the insertion position. The
@@ -998,7 +910,7 @@ static void genBodyOfTargetOp(
998910
auto argIface = llvm::cast<mlir::omp::BlockArgOpenMPOpInterface>(*targetOp);
999911

1000912
mlir::Region &region = targetOp.getRegion();
1001-
mlir::Block *entryBlock = genEntryBlock(converter, args, region);
913+
mlir::Block *entryBlock = genEntryBlock(firOpBuilder, args, region);
1002914
bindEntryBlockArgs(converter, targetOp, args);
1003915

1004916
// Check if cloning the bounds introduced any dependency on the outer region.
@@ -1124,7 +1036,7 @@ static OpTy genWrapperOp(lower::AbstractConverter &converter,
11241036
auto op = firOpBuilder.create<OpTy>(loc, clauseOps);
11251037

11261038
// Create entry block with arguments.
1127-
genEntryBlock(converter, args, op.getRegion());
1039+
genEntryBlock(firOpBuilder, args, op.getRegion());
11281040

11291041
return op;
11301042
}
@@ -1590,7 +1502,7 @@ genParallelOp(lower::AbstractConverter &converter, lower::SymMap &symTable,
15901502
const EntryBlockArgs &args, DataSharingProcessor *dsp,
15911503
bool isComposite = false) {
15921504
auto genRegionEntryCB = [&](mlir::Operation *op) {
1593-
genEntryBlock(converter, args, op->getRegion(0));
1505+
genEntryBlock(converter.getFirOpBuilder(), args, op->getRegion(0));
15941506
bindEntryBlockArgs(
15951507
converter, llvm::cast<mlir::omp::BlockArgOpenMPOpInterface>(op), args);
15961508
return llvm::to_vector(args.getSyms());
@@ -1663,12 +1575,12 @@ genSectionsOp(lower::AbstractConverter &converter, lower::SymMap &symTable,
16631575
args.reduction.syms = reductionSyms;
16641576
args.reduction.vars = clauseOps.reductionVars;
16651577

1666-
genEntryBlock(converter, args, sectionsOp.getRegion());
1578+
genEntryBlock(builder, args, sectionsOp.getRegion());
16671579
mlir::Operation *terminator =
16681580
lower::genOpenMPTerminator(builder, sectionsOp, loc);
16691581

16701582
auto genRegionEntryCB = [&](mlir::Operation *op) {
1671-
genEntryBlock(converter, args, op->getRegion(0));
1583+
genEntryBlock(builder, args, op->getRegion(0));
16721584
bindEntryBlockArgs(
16731585
converter, llvm::cast<mlir::omp::BlockArgOpenMPOpInterface>(op), args);
16741586
return llvm::to_vector(args.getSyms());
@@ -1991,7 +1903,7 @@ genTaskOp(lower::AbstractConverter &converter, lower::SymMap &symTable,
19911903
taskArgs.priv.vars = clauseOps.privateVars;
19921904

19931905
auto genRegionEntryCB = [&](mlir::Operation *op) {
1994-
genEntryBlock(converter, taskArgs, op->getRegion(0));
1906+
genEntryBlock(converter.getFirOpBuilder(), taskArgs, op->getRegion(0));
19951907
bindEntryBlockArgs(converter,
19961908
llvm::cast<mlir::omp::BlockArgOpenMPOpInterface>(op),
19971909
taskArgs);

flang/lib/Optimizer/OpenMP/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ get_property(dialect_libs GLOBAL PROPERTY MLIR_DIALECT_LIBS)
22

33
add_flang_library(FlangOpenMPTransforms
44
FunctionFiltering.cpp
5+
GenericLoopConversion.cpp
56
MapsForPrivatizedSymbols.cpp
67
MapInfoFinalization.cpp
78
MarkDeclareTarget.cpp
@@ -25,4 +26,5 @@ add_flang_library(FlangOpenMPTransforms
2526
HLFIRDialect
2627
MLIRIR
2728
MLIRPass
29+
MLIRTransformUtils
2830
)

0 commit comments

Comments
 (0)