@@ -383,7 +383,8 @@ void DataSharingProcessor::insertLastPrivateCompare(mlir::Operation *op) {
383
383
// construct
384
384
mlir::OpBuilder::InsertPoint unstructuredSectionsIP =
385
385
firOpBuilder.saveInsertionPoint ();
386
- firOpBuilder.setInsertionPointToStart (&op->getRegion (0 ).back ());
386
+ mlir::Operation *lastOper = op->getRegion (0 ).back ().getTerminator ();
387
+ firOpBuilder.setInsertionPoint (lastOper);
387
388
lastPrivIP = firOpBuilder.saveInsertionPoint ();
388
389
firOpBuilder.restoreInsertionPoint (unstructuredSectionsIP);
389
390
}
@@ -2133,15 +2134,6 @@ static mlir::Type getLoopVarType(Fortran::lower::AbstractConverter &converter,
2133
2134
return converter.getFirOpBuilder ().getIntegerType (loopVarTypeSize);
2134
2135
}
2135
2136
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
-
2145
2137
static mlir::Operation *
2146
2138
createAndSetPrivatizedLoopVar (Fortran::lower::AbstractConverter &converter,
2147
2139
mlir::Location loc, mlir::Value indexVal,
@@ -2183,11 +2175,43 @@ static void createBodyOfOp(
2183
2175
const llvm::SmallVector<const Fortran::semantics::Symbol *> &args = {},
2184
2176
bool outerCombined = false , DataSharingProcessor *dsp = nullptr ) {
2185
2177
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 ®ion) -> 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 = █
2206
+ }
2207
+ }
2208
+ return exit;
2209
+ };
2210
+
2186
2211
// If an argument for the region is provided then create the block with that
2187
2212
// argument. Also update the symbol's address with the mlir argument value.
2188
2213
// e.g. For loops the argument is the induction variable. And all further
2189
2214
// uses of the induction variable should use this mlir value.
2190
- mlir::Operation *storeOp = nullptr ;
2191
2215
if (args.size ()) {
2192
2216
std::size_t loopVarTypeSize = 0 ;
2193
2217
for (const Fortran::semantics::Symbol *arg : args)
@@ -2198,20 +2222,20 @@ static void createBodyOfOp(
2198
2222
firOpBuilder.createBlock (&op.getRegion (), {}, tiv, locs);
2199
2223
// The argument is not currently in memory, so make a temporary for the
2200
2224
// argument, and store it there, then bind that location to the argument.
2225
+ mlir::Operation *storeOp = nullptr ;
2201
2226
for (auto [argIndex, argSymbol] : llvm::enumerate (args)) {
2202
2227
mlir::Value indexVal =
2203
2228
fir::getBase (op.getRegion ().front ().getArgument (argIndex));
2204
2229
storeOp =
2205
2230
createAndSetPrivatizedLoopVar (converter, loc, indexVal, argSymbol);
2206
2231
}
2232
+ firOpBuilder.setInsertionPointAfter (storeOp);
2207
2233
} else {
2208
2234
firOpBuilder.createBlock (&op.getRegion ());
2209
2235
}
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);
2215
2239
2216
2240
// If it is an unstructured region and is not the outer region of a combined
2217
2241
// construct, create empty blocks for all evaluations.
@@ -2220,37 +2244,64 @@ static void createBodyOfOp(
2220
2244
mlir::omp::YieldOp>(
2221
2245
firOpBuilder, eval.getNestedEvaluations ());
2222
2246
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;
2227
2252
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) {
2232
2256
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 ();
2240
2259
}
2241
-
2242
- if (storeOp)
2243
- firOpBuilder.setInsertionPointAfter (storeOp);
2244
2260
}
2245
2261
2246
2262
if constexpr (std::is_same_v<Op, mlir::omp::ParallelOp>) {
2247
2263
threadPrivatizeVars (converter, eval);
2248
- if (clauses)
2264
+ if (clauses) {
2265
+ firOpBuilder.setInsertionPoint (marker);
2249
2266
ClauseProcessor (converter, *clauses).processCopyin ();
2267
+ }
2250
2268
}
2251
2269
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);
2253
2278
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 ();
2254
2305
}
2255
2306
2256
2307
static void genBodyOfTargetDataOp (
@@ -3664,14 +3715,14 @@ genOMP(Fortran::lower::AbstractConverter &converter,
3664
3715
// Public functions
3665
3716
// ===----------------------------------------------------------------------===//
3666
3717
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) {
3670
3721
if (mlir::isa<mlir::omp::WsLoopOp, mlir::omp::ReductionDeclareOp,
3671
3722
mlir::omp::AtomicUpdateOp, mlir::omp::SimdLoopOp>(op))
3672
- builder.create <mlir::omp::YieldOp>(loc);
3723
+ return builder.create <mlir::omp::YieldOp>(loc);
3673
3724
else
3674
- builder.create <mlir::omp::TerminatorOp>(loc);
3725
+ return builder.create <mlir::omp::TerminatorOp>(loc);
3675
3726
}
3676
3727
3677
3728
void Fortran::lower::genOpenMPConstruct (
0 commit comments