27
27
#include " flang/Parser/parse-tree.h"
28
28
#include " flang/Semantics/openmp-directive-sets.h"
29
29
#include " flang/Semantics/tools.h"
30
+ #include " mlir/Dialect/ControlFlow/IR/ControlFlowOps.h"
30
31
#include " mlir/Dialect/OpenMP/OpenMPDialect.h"
31
32
#include " mlir/Dialect/SCF/IR/SCF.h"
32
33
#include " mlir/Transforms/RegionUtils.h"
@@ -385,7 +386,8 @@ void DataSharingProcessor::insertLastPrivateCompare(mlir::Operation *op) {
385
386
// construct
386
387
mlir::OpBuilder::InsertPoint unstructuredSectionsIP =
387
388
firOpBuilder.saveInsertionPoint ();
388
- firOpBuilder.setInsertionPointToStart (&op->getRegion (0 ).back ());
389
+ mlir::Operation *lastOper = op->getRegion (0 ).back ().getTerminator ();
390
+ firOpBuilder.setInsertionPoint (lastOper);
389
391
lastPrivIP = firOpBuilder.saveInsertionPoint ();
390
392
firOpBuilder.restoreInsertionPoint (unstructuredSectionsIP);
391
393
}
@@ -2206,15 +2208,6 @@ static mlir::Type getLoopVarType(Fortran::lower::AbstractConverter &converter,
2206
2208
return converter.getFirOpBuilder ().getIntegerType (loopVarTypeSize);
2207
2209
}
2208
2210
2209
- static void resetBeforeTerminator (fir::FirOpBuilder &firOpBuilder,
2210
- mlir::Operation *storeOp,
2211
- mlir::Block &block) {
2212
- if (storeOp)
2213
- firOpBuilder.setInsertionPointAfter (storeOp);
2214
- else
2215
- firOpBuilder.setInsertionPointToStart (&block);
2216
- }
2217
-
2218
2211
static mlir::Operation *
2219
2212
createAndSetPrivatizedLoopVar (Fortran::lower::AbstractConverter &converter,
2220
2213
mlir::Location loc, mlir::Value indexVal,
@@ -2257,11 +2250,17 @@ static void createBodyOfOp(
2257
2250
const llvm::SmallVector<const Fortran::semantics::Symbol *> &args = {},
2258
2251
bool outerCombined = false , DataSharingProcessor *dsp = nullptr ) {
2259
2252
fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder ();
2253
+
2254
+ auto insertMarker = [](fir::FirOpBuilder &builder) {
2255
+ mlir::Value undef = builder.create <fir::UndefOp>(builder.getUnknownLoc (),
2256
+ builder.getIndexType ());
2257
+ return undef.getDefiningOp ();
2258
+ };
2259
+
2260
2260
// If an argument for the region is provided then create the block with that
2261
2261
// argument. Also update the symbol's address with the mlir argument value.
2262
2262
// e.g. For loops the argument is the induction variable. And all further
2263
2263
// uses of the induction variable should use this mlir value.
2264
- mlir::Operation *storeOp = nullptr ;
2265
2264
if (args.size ()) {
2266
2265
std::size_t loopVarTypeSize = 0 ;
2267
2266
for (const Fortran::semantics::Symbol *arg : args)
@@ -2272,20 +2271,20 @@ static void createBodyOfOp(
2272
2271
firOpBuilder.createBlock (&op.getRegion (), {}, tiv, locs);
2273
2272
// The argument is not currently in memory, so make a temporary for the
2274
2273
// argument, and store it there, then bind that location to the argument.
2274
+ mlir::Operation *storeOp = nullptr ;
2275
2275
for (auto [argIndex, argSymbol] : llvm::enumerate (args)) {
2276
2276
mlir::Value indexVal =
2277
2277
fir::getBase (op.getRegion ().front ().getArgument (argIndex));
2278
2278
storeOp =
2279
2279
createAndSetPrivatizedLoopVar (converter, loc, indexVal, argSymbol);
2280
2280
}
2281
+ firOpBuilder.setInsertionPointAfter (storeOp);
2281
2282
} else {
2282
2283
firOpBuilder.createBlock (&op.getRegion ());
2283
2284
}
2284
- // Set the insert for the terminator operation to go at the end of the
2285
- // block - this is either empty or the block with the stores above,
2286
- // the end of the block works for both.
2287
- mlir::Block &block = op.getRegion ().back ();
2288
- firOpBuilder.setInsertionPointToEnd (&block);
2285
+
2286
+ // Mark the earliest insertion point.
2287
+ mlir::Operation *marker = insertMarker (firOpBuilder);
2289
2288
2290
2289
// If it is an unstructured region and is not the outer region of a combined
2291
2290
// construct, create empty blocks for all evaluations.
@@ -2294,37 +2293,100 @@ static void createBodyOfOp(
2294
2293
mlir::omp::YieldOp>(
2295
2294
firOpBuilder, eval.getNestedEvaluations ());
2296
2295
2297
- // Insert the terminator.
2298
- Fortran::lower::genOpenMPTerminator (firOpBuilder, op.getOperation (), loc);
2299
- // Reset the insert point to before the terminator.
2300
- resetBeforeTerminator (firOpBuilder, storeOp, block);
2296
+ // Start with privatization, so that the lowering of the nested
2297
+ // code will use the right symbols.
2298
+ constexpr bool isLoop = std::is_same_v<Op, mlir::omp::WsLoopOp> ||
2299
+ std::is_same_v<Op, mlir::omp::SimdLoopOp>;
2300
+ bool privatize = clauses && !outerCombined;
2301
2301
2302
- // Handle privatization. Do not privatize if this is the outer operation.
2303
- if (clauses && !outerCombined) {
2304
- constexpr bool isLoop = std::is_same_v<Op, mlir::omp::WsLoopOp> ||
2305
- std::is_same_v<Op, mlir::omp::SimdLoopOp>;
2302
+ firOpBuilder.setInsertionPoint (marker);
2303
+ std::optional<DataSharingProcessor> tempDsp;
2304
+ if (privatize) {
2306
2305
if (!dsp) {
2307
- DataSharingProcessor proc (converter, *clauses, eval);
2308
- proc.processStep1 ();
2309
- proc.processStep2 (op, isLoop);
2310
- } else {
2311
- if (isLoop && args.size () > 0 )
2312
- dsp->setLoopIV (converter.getSymbolAddress (*args[0 ]));
2313
- dsp->processStep2 (op, isLoop);
2306
+ tempDsp.emplace (converter, *clauses, eval);
2307
+ tempDsp->processStep1 ();
2314
2308
}
2315
-
2316
- if (storeOp)
2317
- firOpBuilder.setInsertionPointAfter (storeOp);
2318
2309
}
2319
2310
2320
2311
if constexpr (std::is_same_v<Op, mlir::omp::ParallelOp>) {
2321
2312
threadPrivatizeVars (converter, eval);
2322
- if (clauses)
2313
+ if (clauses) {
2314
+ firOpBuilder.setInsertionPoint (marker);
2323
2315
ClauseProcessor (converter, *clauses).processCopyin ();
2316
+ }
2324
2317
}
2325
2318
2326
- if (genNested)
2319
+ if (genNested) {
2320
+ // genFIR(Evaluation&) tries to patch up unterminated blocks, causing
2321
+ // a lot of trouble if the terminator generation is delayed past this
2322
+ // point. Insert a temporary terminator here, then delete it.
2323
+ firOpBuilder.setInsertionPointToEnd (&op.getRegion ().back ());
2324
+ auto *temp = Fortran::lower::genOpenMPTerminator (firOpBuilder,
2325
+ op.getOperation (), loc);
2326
+ firOpBuilder.setInsertionPointAfter (marker);
2327
2327
genNestedEvaluations (converter, eval);
2328
+ temp->erase ();
2329
+ }
2330
+
2331
+ // Get or create a unique exiting block from the given region, or
2332
+ // return nullptr if there is no exiting block.
2333
+ auto getUniqueExit = [&](mlir::Region ®ion) -> mlir::Block * {
2334
+ // Find the blocks where the OMP terminator should go. In simple cases
2335
+ // it is the single block in the operation's region. When the region
2336
+ // is more complicated, especially with unstructured control flow, there
2337
+ // may be multiple blocks, and some of them may have non-OMP terminators
2338
+ // resulting from lowering of the code contained within the operation.
2339
+ // All the remaining blocks are potential exit points from the op's region.
2340
+ //
2341
+ // Explicit control flow cannot exit any OpenMP region (other than via
2342
+ // STOP), and that is enforced by semantic checks prior to lowering. STOP
2343
+ // statements are lowered to a function call.
2344
+
2345
+ // Collect unterminated blocks.
2346
+ llvm::SmallVector<mlir::Block *> exits;
2347
+ for (mlir::Block &b : region) {
2348
+ if (b.empty () || !b.back ().hasTrait <mlir::OpTrait::IsTerminator>())
2349
+ exits.push_back (&b);
2350
+ }
2351
+
2352
+ if (exits.empty ())
2353
+ return nullptr ;
2354
+ // If there already is a unique exiting block, do not create another one.
2355
+ // Additionally, some ops (e.g. omp.sections) require only 1 block in
2356
+ // its region.
2357
+ if (exits.size () == 1 )
2358
+ return exits[0 ];
2359
+ mlir::Block *exit = firOpBuilder.createBlock (®ion);
2360
+ for (mlir::Block *b : exits) {
2361
+ firOpBuilder.setInsertionPointToEnd (b);
2362
+ firOpBuilder.create <mlir::cf::BranchOp>(loc, exit );
2363
+ }
2364
+ return exit ;
2365
+ };
2366
+
2367
+ if (auto *exitBlock = getUniqueExit (op.getRegion ())) {
2368
+ firOpBuilder.setInsertionPointToEnd (exitBlock);
2369
+ auto *term = Fortran::lower::genOpenMPTerminator (firOpBuilder,
2370
+ op.getOperation (), loc);
2371
+ // Only insert lastprivate code when there actually is an exit block.
2372
+ // Such a block may not exist if the nested code produced an infinite
2373
+ // loop (this may not make sense in production code, but a user could
2374
+ // write that and we should handle it).
2375
+ firOpBuilder.setInsertionPoint (term);
2376
+ if (privatize) {
2377
+ if (!dsp) {
2378
+ assert (tempDsp.has_value ());
2379
+ tempDsp->processStep2 (op, isLoop);
2380
+ } else {
2381
+ if (isLoop && args.size () > 0 )
2382
+ dsp->setLoopIV (converter.getSymbolAddress (*args[0 ]));
2383
+ dsp->processStep2 (op, isLoop);
2384
+ }
2385
+ }
2386
+ }
2387
+
2388
+ firOpBuilder.setInsertionPointAfter (marker);
2389
+ marker->erase ();
2328
2390
}
2329
2391
2330
2392
static void genBodyOfTargetDataOp (
@@ -3756,14 +3818,14 @@ genOMP(Fortran::lower::AbstractConverter &converter,
3756
3818
// Public functions
3757
3819
// ===----------------------------------------------------------------------===//
3758
3820
3759
- void Fortran::lower::genOpenMPTerminator (fir::FirOpBuilder &builder,
3760
- mlir::Operation *op,
3761
- mlir::Location loc) {
3821
+ mlir::Operation * Fortran::lower::genOpenMPTerminator (fir::FirOpBuilder &builder,
3822
+ mlir::Operation *op,
3823
+ mlir::Location loc) {
3762
3824
if (mlir::isa<mlir::omp::WsLoopOp, mlir::omp::ReductionDeclareOp,
3763
3825
mlir::omp::AtomicUpdateOp, mlir::omp::SimdLoopOp>(op))
3764
- builder.create <mlir::omp::YieldOp>(loc);
3826
+ return builder.create <mlir::omp::YieldOp>(loc);
3765
3827
else
3766
- builder.create <mlir::omp::TerminatorOp>(loc);
3828
+ return builder.create <mlir::omp::TerminatorOp>(loc);
3767
3829
}
3768
3830
3769
3831
void Fortran::lower::genOpenMPConstruct (
0 commit comments