Skip to content

Commit dfbf88d

Browse files
committed
[flang][OpenMP] WIP: Rewrite omp.loop to semantically equivalent ops
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.
1 parent d4f18a6 commit dfbf88d

File tree

9 files changed

+354
-96
lines changed

9 files changed

+354
-96
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::openmp::common {
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::openmp::common
67+
68+
#endif // FORTRAN_COMMON_OPENMP_UTILS_H_

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

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

53+
def GenericLoopConversionPass
54+
: Pass<"omp-generic-loop-conversion", "mlir::func::FuncOp"> {
55+
let summary = "Converts OpenMP generic `loop` direcitve to semantically "
56+
"equivalent OpenMP ops";
57+
let description = [{
58+
Rewrites `loop` ops to their semantically equivalent nest of ops. The
59+
rewrite depends on the nesting/combination structure of the `loop` op
60+
within its surrounding context as well as its `bind` clause value.
61+
}];
62+
let dependentDialects = ["mlir::omp::OpenMPDialect"];
63+
}
64+
5365
#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::openmp::common {
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::openmp::common

flang/lib/Lower/OpenMP/OpenMP.cpp

Lines changed: 9 additions & 96 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"
@@ -40,57 +41,12 @@
4041
#include "llvm/Frontend/OpenMP/OMPConstants.h"
4142

4243
using namespace Fortran::lower::omp;
44+
using namespace Fortran::openmp::common;
4345

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

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

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

921-
genEntryBlock(converter, args, dataOp.getRegion());
833+
genEntryBlock(converter.getFirOpBuilder(), args, dataOp.getRegion());
922834
bindEntryBlockArgs(converter, dataOp, args);
923835

924836
// Insert dummy instruction to remember the insertion position. The
@@ -995,7 +907,8 @@ static void genBodyOfTargetOp(
995907
auto argIface = llvm::cast<mlir::omp::BlockArgOpenMPOpInterface>(*targetOp);
996908

997909
mlir::Region &region = targetOp.getRegion();
998-
mlir::Block *entryBlock = genEntryBlock(converter, args, region);
910+
mlir::Block *entryBlock =
911+
genEntryBlock(converter.getFirOpBuilder(), args, region);
999912
bindEntryBlockArgs(converter, targetOp, args);
1000913

1001914
// Check if cloning the bounds introduced any dependency on the outer region.
@@ -1121,7 +1034,7 @@ static OpTy genWrapperOp(lower::AbstractConverter &converter,
11211034
auto op = firOpBuilder.create<OpTy>(loc, clauseOps);
11221035

11231036
// Create entry block with arguments.
1124-
genEntryBlock(converter, args, op.getRegion());
1037+
genEntryBlock(converter.getFirOpBuilder(), args, op.getRegion());
11251038

11261039
return op;
11271040
}
@@ -1544,7 +1457,7 @@ genParallelOp(lower::AbstractConverter &converter, lower::SymMap &symTable,
15441457
const EntryBlockArgs &args, DataSharingProcessor *dsp,
15451458
bool isComposite = false) {
15461459
auto genRegionEntryCB = [&](mlir::Operation *op) {
1547-
genEntryBlock(converter, args, op->getRegion(0));
1460+
genEntryBlock(converter.getFirOpBuilder(), args, op->getRegion(0));
15481461
bindEntryBlockArgs(
15491462
converter, llvm::cast<mlir::omp::BlockArgOpenMPOpInterface>(op), args);
15501463
return llvm::to_vector(args.getSyms());
@@ -1617,12 +1530,12 @@ genSectionsOp(lower::AbstractConverter &converter, lower::SymMap &symTable,
16171530
args.reduction.syms = reductionSyms;
16181531
args.reduction.vars = clauseOps.reductionVars;
16191532

1620-
genEntryBlock(converter, args, sectionsOp.getRegion());
1533+
genEntryBlock(converter.getFirOpBuilder(), args, sectionsOp.getRegion());
16211534
mlir::Operation *terminator =
16221535
lower::genOpenMPTerminator(builder, sectionsOp, loc);
16231536

16241537
auto genRegionEntryCB = [&](mlir::Operation *op) {
1625-
genEntryBlock(converter, args, op->getRegion(0));
1538+
genEntryBlock(converter.getFirOpBuilder(), args, op->getRegion(0));
16261539
bindEntryBlockArgs(
16271540
converter, llvm::cast<mlir::omp::BlockArgOpenMPOpInterface>(op), args);
16281541
return llvm::to_vector(args.getSyms());

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
@@ -24,4 +25,5 @@ add_flang_library(FlangOpenMPTransforms
2425
HLFIRDialect
2526
MLIRIR
2627
MLIRPass
28+
MLIRTransformUtils
2729
)

0 commit comments

Comments
 (0)