Skip to content

Commit 1b5524a

Browse files
committed
[Flang][OpenMP] Restructure recursive lowering in createBodyOfOp
This brings `createBodyOfOp` to its final intended form. First, input privatization is performed, then the recursive lowering takes place, and finally the output privatization (lastprivate) is done. This enables fixing a known issue with infinite loops inside of an OpenMP region, and the fix is included in this patch. Fixes #74348. Recursive lowering [5/5]
1 parent 883afd9 commit 1b5524a

File tree

5 files changed

+119
-49
lines changed

5 files changed

+119
-49
lines changed

flang/include/flang/Lower/OpenMP.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,8 @@ struct Variable;
5050
} // namespace pft
5151

5252
// Generate the OpenMP terminator for Operation at Location.
53-
void genOpenMPTerminator(fir::FirOpBuilder &, mlir::Operation *,
54-
mlir::Location);
53+
mlir::Operation *genOpenMPTerminator(fir::FirOpBuilder &, mlir::Operation *,
54+
mlir::Location);
5555

5656
void genOpenMPConstruct(AbstractConverter &, Fortran::lower::SymMap &,
5757
semantics::SemanticsContext &, pft::Evaluation &,

flang/lib/Lower/OpenMP.cpp

Lines changed: 92 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -383,7 +383,8 @@ void DataSharingProcessor::insertLastPrivateCompare(mlir::Operation *op) {
383383
// construct
384384
mlir::OpBuilder::InsertPoint unstructuredSectionsIP =
385385
firOpBuilder.saveInsertionPoint();
386-
firOpBuilder.setInsertionPointToStart(&op->getRegion(0).back());
386+
mlir::Operation *lastOper = op->getRegion(0).back().getTerminator();
387+
firOpBuilder.setInsertionPoint(lastOper);
387388
lastPrivIP = firOpBuilder.saveInsertionPoint();
388389
firOpBuilder.restoreInsertionPoint(unstructuredSectionsIP);
389390
}
@@ -2133,15 +2134,6 @@ static mlir::Type getLoopVarType(Fortran::lower::AbstractConverter &converter,
21332134
return converter.getFirOpBuilder().getIntegerType(loopVarTypeSize);
21342135
}
21352136

2136-
static void resetBeforeTerminator(fir::FirOpBuilder &firOpBuilder,
2137-
mlir::Operation *storeOp,
2138-
mlir::Block &block) {
2139-
if (storeOp)
2140-
firOpBuilder.setInsertionPointAfter(storeOp);
2141-
else
2142-
firOpBuilder.setInsertionPointToStart(&block);
2143-
}
2144-
21452137
static mlir::Operation *
21462138
createAndSetPrivatizedLoopVar(Fortran::lower::AbstractConverter &converter,
21472139
mlir::Location loc, mlir::Value indexVal,
@@ -2183,11 +2175,43 @@ static void createBodyOfOp(
21832175
const llvm::SmallVector<const Fortran::semantics::Symbol *> &args = {},
21842176
bool outerCombined = false, DataSharingProcessor *dsp = nullptr) {
21852177
fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder();
2178+
2179+
auto insertMarker = [](fir::FirOpBuilder &builder) {
2180+
mlir::Value undef = builder.create<fir::UndefOp>(builder.getUnknownLoc(),
2181+
builder.getIndexType());
2182+
return undef.getDefiningOp();
2183+
};
2184+
2185+
// Find the block where the OMP terminator should go. In simple cases
2186+
// it is the single block in the operation's region. When the region
2187+
// is more complicated, especially with unstructured control flow, there
2188+
// may be multiple blocks, and some of them may have non-OMP terminators
2189+
// resulting from lowering of the code contained within the operation.
2190+
// By OpenMP rules, there should be a single exit point from the region:
2191+
// here exit means transfering control to the code following the operation.
2192+
// STOP statement is allowed and does not count as exit for the purpose of
2193+
// inserting terminators.
2194+
auto findExitBlock = [&](mlir::Region &region) -> mlir::Block * {
2195+
auto isTerminated = [](mlir::Block &block) -> bool {
2196+
if (block.empty())
2197+
return false;
2198+
return block.back().hasTrait<mlir::OpTrait::IsTerminator>();
2199+
};
2200+
2201+
mlir::Block *exit = nullptr;
2202+
for (auto &block : region) {
2203+
if (!isTerminated(block)) {
2204+
assert(exit == nullptr && "Multiple exit block in OpenMP region");
2205+
exit = &block;
2206+
}
2207+
}
2208+
return exit;
2209+
};
2210+
21862211
// If an argument for the region is provided then create the block with that
21872212
// argument. Also update the symbol's address with the mlir argument value.
21882213
// e.g. For loops the argument is the induction variable. And all further
21892214
// uses of the induction variable should use this mlir value.
2190-
mlir::Operation *storeOp = nullptr;
21912215
if (args.size()) {
21922216
std::size_t loopVarTypeSize = 0;
21932217
for (const Fortran::semantics::Symbol *arg : args)
@@ -2198,20 +2222,20 @@ static void createBodyOfOp(
21982222
firOpBuilder.createBlock(&op.getRegion(), {}, tiv, locs);
21992223
// The argument is not currently in memory, so make a temporary for the
22002224
// argument, and store it there, then bind that location to the argument.
2225+
mlir::Operation *storeOp = nullptr;
22012226
for (auto [argIndex, argSymbol] : llvm::enumerate(args)) {
22022227
mlir::Value indexVal =
22032228
fir::getBase(op.getRegion().front().getArgument(argIndex));
22042229
storeOp =
22052230
createAndSetPrivatizedLoopVar(converter, loc, indexVal, argSymbol);
22062231
}
2232+
firOpBuilder.setInsertionPointAfter(storeOp);
22072233
} else {
22082234
firOpBuilder.createBlock(&op.getRegion());
22092235
}
2210-
// Set the insert for the terminator operation to go at the end of the
2211-
// block - this is either empty or the block with the stores above,
2212-
// the end of the block works for both.
2213-
mlir::Block &block = op.getRegion().back();
2214-
firOpBuilder.setInsertionPointToEnd(&block);
2236+
2237+
// Mark the earliest insertion point.
2238+
mlir::Operation *marker = insertMarker(firOpBuilder);
22152239

22162240
// If it is an unstructured region and is not the outer region of a combined
22172241
// construct, create empty blocks for all evaluations.
@@ -2220,37 +2244,64 @@ static void createBodyOfOp(
22202244
mlir::omp::YieldOp>(
22212245
firOpBuilder, eval.getNestedEvaluations());
22222246

2223-
// Insert the terminator.
2224-
Fortran::lower::genOpenMPTerminator(firOpBuilder, op.getOperation(), loc);
2225-
// Reset the insert point to before the terminator.
2226-
resetBeforeTerminator(firOpBuilder, storeOp, block);
2247+
// Start with privatization, so that the lowering of the nested
2248+
// code will use the right symbols.
2249+
constexpr bool isLoop = std::is_same_v<Op, mlir::omp::WsLoopOp> ||
2250+
std::is_same_v<Op, mlir::omp::SimdLoopOp>;
2251+
bool privatize = clauses && !outerCombined;
22272252

2228-
// Handle privatization. Do not privatize if this is the outer operation.
2229-
if (clauses && !outerCombined) {
2230-
constexpr bool isLoop = std::is_same_v<Op, mlir::omp::WsLoopOp> ||
2231-
std::is_same_v<Op, mlir::omp::SimdLoopOp>;
2253+
firOpBuilder.setInsertionPoint(marker);
2254+
std::optional<DataSharingProcessor> tempDsp;
2255+
if (privatize) {
22322256
if (!dsp) {
2233-
DataSharingProcessor proc(converter, *clauses, eval);
2234-
proc.processStep1();
2235-
proc.processStep2(op, isLoop);
2236-
} else {
2237-
if (isLoop && args.size() > 0)
2238-
dsp->setLoopIV(converter.getSymbolAddress(*args[0]));
2239-
dsp->processStep2(op, isLoop);
2257+
tempDsp.emplace(converter, *clauses, eval);
2258+
tempDsp->processStep1();
22402259
}
2241-
2242-
if (storeOp)
2243-
firOpBuilder.setInsertionPointAfter(storeOp);
22442260
}
22452261

22462262
if constexpr (std::is_same_v<Op, mlir::omp::ParallelOp>) {
22472263
threadPrivatizeVars(converter, eval);
2248-
if (clauses)
2264+
if (clauses) {
2265+
firOpBuilder.setInsertionPoint(marker);
22492266
ClauseProcessor(converter, *clauses).processCopyin();
2267+
}
22502268
}
22512269

2252-
if (genNested)
2270+
if (genNested) {
2271+
// genFIR(Evaluation&) tries to patch up unterminated blocks, causing
2272+
// a lot of trouble if the terminator generation is delayed past this
2273+
// point. Insert a temporary terminator here, then delete it.
2274+
firOpBuilder.setInsertionPointToEnd(&op.getRegion().back());
2275+
auto *temp = Fortran::lower::genOpenMPTerminator(firOpBuilder,
2276+
op.getOperation(), loc);
2277+
firOpBuilder.setInsertionPointAfter(marker);
22532278
genNestedEvaluations(converter, eval);
2279+
temp->erase();
2280+
}
2281+
2282+
if (auto *exitBlock = findExitBlock(op.getRegion())) {
2283+
firOpBuilder.setInsertionPointToEnd(exitBlock);
2284+
auto *term = Fortran::lower::genOpenMPTerminator(firOpBuilder,
2285+
op.getOperation(), loc);
2286+
// Only insert lastprivate code when there actually is an exit block.
2287+
// Such a block may not exist if the nested code produced an infinite
2288+
// loop (this may not make sense in production code, but a user could
2289+
// write that and we should handle it).
2290+
firOpBuilder.setInsertionPoint(term);
2291+
if (privatize) {
2292+
if (!dsp) {
2293+
assert(tempDsp.has_value());
2294+
tempDsp->processStep2(op, isLoop);
2295+
} else {
2296+
if (isLoop && args.size() > 0)
2297+
dsp->setLoopIV(converter.getSymbolAddress(*args[0]));
2298+
dsp->processStep2(op, isLoop);
2299+
}
2300+
}
2301+
}
2302+
2303+
firOpBuilder.setInsertionPointAfter(marker);
2304+
marker->erase();
22542305
}
22552306

22562307
static void genBodyOfTargetDataOp(
@@ -3664,14 +3715,14 @@ genOMP(Fortran::lower::AbstractConverter &converter,
36643715
// Public functions
36653716
//===----------------------------------------------------------------------===//
36663717

3667-
void Fortran::lower::genOpenMPTerminator(fir::FirOpBuilder &builder,
3668-
mlir::Operation *op,
3669-
mlir::Location loc) {
3718+
mlir::Operation *Fortran::lower::genOpenMPTerminator(fir::FirOpBuilder &builder,
3719+
mlir::Operation *op,
3720+
mlir::Location loc) {
36703721
if (mlir::isa<mlir::omp::WsLoopOp, mlir::omp::ReductionDeclareOp,
36713722
mlir::omp::AtomicUpdateOp, mlir::omp::SimdLoopOp>(op))
3672-
builder.create<mlir::omp::YieldOp>(loc);
3723+
return builder.create<mlir::omp::YieldOp>(loc);
36733724
else
3674-
builder.create<mlir::omp::TerminatorOp>(loc);
3725+
return builder.create<mlir::omp::TerminatorOp>(loc);
36753726
}
36763727

36773728
void Fortran::lower::genOpenMPConstruct(

flang/test/Lower/OpenMP/FIR/sections.f90

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -126,11 +126,11 @@ subroutine lastprivate()
126126
x = x * 10
127127
!CHECK: omp.section {
128128
!CHECK: %[[PRIVATE_X:.*]] = fir.alloca i32 {bindc_name = "x", pinned, uniq_name = "_QFlastprivateEx"}
129-
!CHECK: %[[true:.*]] = arith.constant true
130129
!CHECK: %[[temp:.*]] = fir.load %[[PRIVATE_X]] : !fir.ref<i32>
131130
!CHECK: %[[const:.*]] = arith.constant 1 : i32
132131
!CHECK: %[[result:.*]] = arith.addi %[[temp]], %[[const]] : i32
133132
!CHECK: fir.store %[[result]] to %[[PRIVATE_X]] : !fir.ref<i32>
133+
!CHECK: %[[true:.*]] = arith.constant true
134134
!CHECK: fir.if %[[true]] {
135135
!CHECK: %[[temp:.*]] = fir.load %[[PRIVATE_X]] : !fir.ref<i32>
136136
!CHECK: fir.store %[[temp]] to %[[X]] : !fir.ref<i32>
@@ -163,11 +163,11 @@ subroutine lastprivate()
163163
!CHECK: %[[temp:.*]] = fir.load %[[X]] : !fir.ref<i32>
164164
!CHECK: fir.store %[[temp]] to %[[PRIVATE_X]] : !fir.ref<i32>
165165
!CHECK: omp.barrier
166-
!CHECK: %[[true:.*]] = arith.constant true
167166
!CHECK: %[[temp:.*]] = fir.load %[[PRIVATE_X]] : !fir.ref<i32>
168167
!CHECK: %[[const:.*]] = arith.constant 1 : i32
169168
!CHECK: %[[result:.*]] = arith.addi %[[temp]], %[[const]] : i32
170169
!CHECK: fir.store %[[result]] to %[[PRIVATE_X]] : !fir.ref<i32>
170+
!CHECK: %[[true:.*]] = arith.constant true
171171
!CHECK: fir.if %true {
172172
!CHECK: %[[temp:.*]] = fir.load %[[PRIVATE_X]] : !fir.ref<i32>
173173
!CHECK: fir.store %[[temp]] to %[[X]] : !fir.ref<i32>
@@ -200,11 +200,11 @@ subroutine lastprivate()
200200
!CHECK: %[[temp:.*]] = fir.load %[[X]] : !fir.ref<i32>
201201
!CHECK: fir.store %[[temp]] to %[[PRIVATE_X]] : !fir.ref<i32>
202202
!CHECK: omp.barrier
203-
!CHECK: %[[true:.*]] = arith.constant true
204203
!CHECK: %[[temp:.*]] = fir.load %[[PRIVATE_X]] : !fir.ref<i32>
205204
!CHECK: %[[const:.*]] = arith.constant 1 : i32
206205
!CHECK: %[[result:.*]] = arith.addi %[[temp]], %[[const]] : i32
207206
!CHECK: fir.store %[[result]] to %[[PRIVATE_X]] : !fir.ref<i32>
207+
!CHECK: %[[true:.*]] = arith.constant true
208208
!CHECK: fir.if %true {
209209
!CHECK: %[[temp:.*]] = fir.load %[[PRIVATE_X]] : !fir.ref<i32>
210210
!CHECK: fir.store %[[temp]] to %[[X]] : !fir.ref<i32>
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
! RUN: bbc -fopenmp -o - %s | FileCheck %s
2+
3+
! Check that this test can be lowered successfully.
4+
! See https://github.com/llvm/llvm-project/issues/74348
5+
6+
! CHECK-LABEL: func.func @_QPsb
7+
! CHECK: omp.parallel
8+
subroutine sb(ninter, numnod)
9+
integer :: ninter, numnod
10+
integer, dimension(:), allocatable :: indx_nm
11+
12+
!$omp parallel
13+
if (ninter>0) then
14+
allocate(indx_nm(numnod))
15+
endif
16+
220 continue
17+
goto 220
18+
!$omp end parallel
19+
end subroutine

flang/test/Lower/OpenMP/sections.f90

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -141,11 +141,11 @@ subroutine lastprivate()
141141
!CHECK: omp.section {
142142
!CHECK: %[[PRIVATE_X:.*]] = fir.alloca i32 {bindc_name = "x", pinned, uniq_name = "_QFlastprivateEx"}
143143
!CHECK: %[[PRIVATE_X_DECL:.*]]:2 = hlfir.declare %[[PRIVATE_X]] {uniq_name = "_QFlastprivateEx"} : (!fir.ref<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
144-
!CHECK: %[[TRUE:.*]] = arith.constant true
145144
!CHECK: %[[TEMP:.*]] = fir.load %[[PRIVATE_X_DECL]]#0 : !fir.ref<i32>
146145
!CHECK: %[[CONST:.*]] = arith.constant 1 : i32
147146
!CHECK: %[[RESULT:.*]] = arith.addi %[[TEMP]], %[[CONST]] : i32
148147
!CHECK: hlfir.assign %[[RESULT]] to %[[PRIVATE_X_DECL]]#0 : i32, !fir.ref<i32>
148+
!CHECK: %[[TRUE:.*]] = arith.constant true
149149
!CHECK: fir.if %[[TRUE]] {
150150
!CHECK: %[[TEMP1:.*]] = fir.load %[[PRIVATE_X_DECL]]#0 : !fir.ref<i32>
151151
!CHECK: hlfir.assign %[[TEMP1]] to %[[X_DECL]]#0 temporary_lhs : i32, !fir.ref<i32>
@@ -180,11 +180,11 @@ subroutine lastprivate()
180180
!CHECK: %[[TEMP:.*]] = fir.load %[[X_DECL]]#0 : !fir.ref<i32>
181181
!CHECK: hlfir.assign %[[TEMP]] to %[[PRIVATE_X_DECL]]#0 temporary_lhs : i32, !fir.ref<i32>
182182
!CHECK: omp.barrier
183-
!CHECK: %[[TRUE:.*]] = arith.constant true
184183
!CHECK: %[[TEMP:.*]] = fir.load %[[PRIVATE_X_DECL]]#0 : !fir.ref<i32>
185184
!CHECK: %[[CONST:.*]] = arith.constant 1 : i32
186185
!CHECK: %[[RESULT:.*]] = arith.addi %[[TEMP]], %[[CONST]] : i32
187186
!CHECK: hlfir.assign %[[RESULT]] to %[[PRIVATE_X_DECL]]#0 : i32, !fir.ref<i32>
187+
!CHECK: %[[TRUE:.*]] = arith.constant true
188188
!CHECK: fir.if %[[TRUE]] {
189189
!CHECK: %[[TEMP:.*]] = fir.load %[[PRIVATE_X_DECL]]#0 : !fir.ref<i32>
190190
!CHECK: hlfir.assign %[[TEMP]] to %[[X_DECL]]#0 temporary_lhs : i32, !fir.ref<i32>
@@ -219,11 +219,11 @@ subroutine lastprivate()
219219
!CHECK: %[[TEMP:.*]] = fir.load %[[X_DECL]]#0 : !fir.ref<i32>
220220
!CHECK: hlfir.assign %[[TEMP]] to %[[PRIVATE_X_DECL]]#0 temporary_lhs : i32, !fir.ref<i32>
221221
!CHECK: omp.barrier
222-
!CHECK: %[[TRUE:.*]] = arith.constant true
223222
!CHECK: %[[TEMP:.*]] = fir.load %[[PRIVATE_X_DECL]]#0 : !fir.ref<i32>
224223
!CHECK: %[[CONST:.*]] = arith.constant 1 : i32
225224
!CHECK: %[[RESULT:.*]] = arith.addi %[[TEMP]], %[[CONST]] : i32
226225
!CHECK: hlfir.assign %[[RESULT]] to %[[PRIVATE_X_DECL]]#0 : i32, !fir.ref<i32>
226+
!CHECK: %[[TRUE:.*]] = arith.constant true
227227
!CHECK: fir.if %[[TRUE]] {
228228
!CHECK: %[[TEMP:.*]] = fir.load %[[PRIVATE_X_DECL]]#0 : !fir.ref<i32>
229229
!CHECK: hlfir.assign %[[TEMP]] to %[[X_DECL]]#0 temporary_lhs : i32, !fir.ref<i32>

0 commit comments

Comments
 (0)