168d69237SJean Perier //===-- RewriteLoop.cpp ---------------------------------------------------===//
268d69237SJean Perier //
368d69237SJean Perier // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
468d69237SJean Perier // See https://llvm.org/LICENSE.txt for license information.
568d69237SJean Perier // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
668d69237SJean Perier //
768d69237SJean Perier //===----------------------------------------------------------------------===//
868d69237SJean Perier 
968d69237SJean Perier #include "PassDetail.h"
1068d69237SJean Perier #include "flang/Optimizer/Dialect/FIRDialect.h"
1168d69237SJean Perier #include "flang/Optimizer/Dialect/FIROps.h"
1268d69237SJean Perier #include "flang/Optimizer/Transforms/Passes.h"
1368d69237SJean Perier #include "mlir/Dialect/Affine/IR/AffineOps.h"
1468d69237SJean Perier #include "mlir/Dialect/StandardOps/IR/Ops.h"
1568d69237SJean Perier #include "mlir/Pass/Pass.h"
1668d69237SJean Perier #include "mlir/Transforms/DialectConversion.h"
1768d69237SJean Perier #include "llvm/Support/CommandLine.h"
1868d69237SJean Perier 
1968d69237SJean Perier using namespace fir;
2068d69237SJean Perier 
2168d69237SJean Perier namespace {
2268d69237SJean Perier 
2368d69237SJean Perier // Conversion of fir control ops to more primitive control-flow.
2468d69237SJean Perier //
2568d69237SJean Perier // FIR loops that cannot be converted to the affine dialect will remain as
2668d69237SJean Perier // `fir.do_loop` operations.  These can be converted to control-flow operations.
2768d69237SJean Perier 
2868d69237SJean Perier /// Convert `fir.do_loop` to CFG
2968d69237SJean Perier class CfgLoopConv : public mlir::OpRewritePattern<fir::DoLoopOp> {
3068d69237SJean Perier public:
3168d69237SJean Perier   using OpRewritePattern::OpRewritePattern;
3268d69237SJean Perier 
3368d69237SJean Perier   CfgLoopConv(mlir::MLIRContext *ctx, bool forceLoopToExecuteOnce)
3468d69237SJean Perier       : mlir::OpRewritePattern<fir::DoLoopOp>(ctx),
3568d69237SJean Perier         forceLoopToExecuteOnce(forceLoopToExecuteOnce) {}
3668d69237SJean Perier 
3768d69237SJean Perier   mlir::LogicalResult
3868d69237SJean Perier   matchAndRewrite(DoLoopOp loop,
3968d69237SJean Perier                   mlir::PatternRewriter &rewriter) const override {
4068d69237SJean Perier     auto loc = loop.getLoc();
4168d69237SJean Perier 
4268d69237SJean Perier     // Create the start and end blocks that will wrap the DoLoopOp with an
4368d69237SJean Perier     // initalizer and an end point
4468d69237SJean Perier     auto *initBlock = rewriter.getInsertionBlock();
4568d69237SJean Perier     auto initPos = rewriter.getInsertionPoint();
4668d69237SJean Perier     auto *endBlock = rewriter.splitBlock(initBlock, initPos);
4768d69237SJean Perier 
4868d69237SJean Perier     // Split the first DoLoopOp block in two parts. The part before will be the
4968d69237SJean Perier     // conditional block since it already has the induction variable and
5068d69237SJean Perier     // loop-carried values as arguments.
5168d69237SJean Perier     auto *conditionalBlock = &loop.region().front();
52e084679fSRiver Riddle     conditionalBlock->addArgument(rewriter.getIndexType(), loc);
5368d69237SJean Perier     auto *firstBlock =
5468d69237SJean Perier         rewriter.splitBlock(conditionalBlock, conditionalBlock->begin());
5568d69237SJean Perier     auto *lastBlock = &loop.region().back();
5668d69237SJean Perier 
5768d69237SJean Perier     // Move the blocks from the DoLoopOp between initBlock and endBlock
5868d69237SJean Perier     rewriter.inlineRegionBefore(loop.region(), endBlock);
5968d69237SJean Perier 
6068d69237SJean Perier     // Get loop values from the DoLoopOp
6168d69237SJean Perier     auto low = loop.lowerBound();
6268d69237SJean Perier     auto high = loop.upperBound();
6368d69237SJean Perier     assert(low && high && "must be a Value");
6468d69237SJean Perier     auto step = loop.step();
6568d69237SJean Perier 
6668d69237SJean Perier     // Initalization block
6768d69237SJean Perier     rewriter.setInsertionPointToEnd(initBlock);
68a54f4eaeSMogball     auto diff = rewriter.create<mlir::arith::SubIOp>(loc, high, low);
69a54f4eaeSMogball     auto distance = rewriter.create<mlir::arith::AddIOp>(loc, diff, step);
7068d69237SJean Perier     mlir::Value iters =
71a54f4eaeSMogball         rewriter.create<mlir::arith::DivSIOp>(loc, distance, step);
7268d69237SJean Perier 
7368d69237SJean Perier     if (forceLoopToExecuteOnce) {
74a54f4eaeSMogball       auto zero = rewriter.create<mlir::arith::ConstantIndexOp>(loc, 0);
75a54f4eaeSMogball       auto cond = rewriter.create<mlir::arith::CmpIOp>(
76a54f4eaeSMogball           loc, arith::CmpIPredicate::sle, iters, zero);
77a54f4eaeSMogball       auto one = rewriter.create<mlir::arith::ConstantIndexOp>(loc, 1);
78*dec8af70SRiver Riddle       iters = rewriter.create<mlir::arith::SelectOp>(loc, cond, one, iters);
7968d69237SJean Perier     }
8068d69237SJean Perier 
8168d69237SJean Perier     llvm::SmallVector<mlir::Value> loopOperands;
8268d69237SJean Perier     loopOperands.push_back(low);
8368d69237SJean Perier     auto operands = loop.getIterOperands();
8468d69237SJean Perier     loopOperands.append(operands.begin(), operands.end());
8568d69237SJean Perier     loopOperands.push_back(iters);
8668d69237SJean Perier 
8768d69237SJean Perier     rewriter.create<mlir::BranchOp>(loc, conditionalBlock, loopOperands);
8868d69237SJean Perier 
8968d69237SJean Perier     // Last loop block
9068d69237SJean Perier     auto *terminator = lastBlock->getTerminator();
9168d69237SJean Perier     rewriter.setInsertionPointToEnd(lastBlock);
9268d69237SJean Perier     auto iv = conditionalBlock->getArgument(0);
93a54f4eaeSMogball     mlir::Value steppedIndex =
94a54f4eaeSMogball         rewriter.create<mlir::arith::AddIOp>(loc, iv, step);
9568d69237SJean Perier     assert(steppedIndex && "must be a Value");
9668d69237SJean Perier     auto lastArg = conditionalBlock->getNumArguments() - 1;
9768d69237SJean Perier     auto itersLeft = conditionalBlock->getArgument(lastArg);
98a54f4eaeSMogball     auto one = rewriter.create<mlir::arith::ConstantIndexOp>(loc, 1);
9968d69237SJean Perier     mlir::Value itersMinusOne =
100a54f4eaeSMogball         rewriter.create<mlir::arith::SubIOp>(loc, itersLeft, one);
10168d69237SJean Perier 
10268d69237SJean Perier     llvm::SmallVector<mlir::Value> loopCarried;
10368d69237SJean Perier     loopCarried.push_back(steppedIndex);
10468d69237SJean Perier     auto begin = loop.finalValue() ? std::next(terminator->operand_begin())
10568d69237SJean Perier                                    : terminator->operand_begin();
10668d69237SJean Perier     loopCarried.append(begin, terminator->operand_end());
10768d69237SJean Perier     loopCarried.push_back(itersMinusOne);
10868d69237SJean Perier     rewriter.create<mlir::BranchOp>(loc, conditionalBlock, loopCarried);
10968d69237SJean Perier     rewriter.eraseOp(terminator);
11068d69237SJean Perier 
11168d69237SJean Perier     // Conditional block
11268d69237SJean Perier     rewriter.setInsertionPointToEnd(conditionalBlock);
113a54f4eaeSMogball     auto zero = rewriter.create<mlir::arith::ConstantIndexOp>(loc, 0);
114a54f4eaeSMogball     auto comparison = rewriter.create<mlir::arith::CmpIOp>(
115a54f4eaeSMogball         loc, arith::CmpIPredicate::sgt, itersLeft, zero);
11668d69237SJean Perier 
11768d69237SJean Perier     rewriter.create<mlir::CondBranchOp>(loc, comparison, firstBlock,
11868d69237SJean Perier                                         llvm::ArrayRef<mlir::Value>(), endBlock,
11968d69237SJean Perier                                         llvm::ArrayRef<mlir::Value>());
12068d69237SJean Perier 
12168d69237SJean Perier     // The result of the loop operation is the values of the condition block
12268d69237SJean Perier     // arguments except the induction variable on the last iteration.
12368d69237SJean Perier     auto args = loop.finalValue()
12468d69237SJean Perier                     ? conditionalBlock->getArguments()
12568d69237SJean Perier                     : conditionalBlock->getArguments().drop_front();
12668d69237SJean Perier     rewriter.replaceOp(loop, args.drop_back());
12768d69237SJean Perier     return success();
12868d69237SJean Perier   }
12968d69237SJean Perier 
13068d69237SJean Perier private:
13168d69237SJean Perier   bool forceLoopToExecuteOnce;
13268d69237SJean Perier };
13368d69237SJean Perier 
13468d69237SJean Perier /// Convert `fir.if` to control-flow
13568d69237SJean Perier class CfgIfConv : public mlir::OpRewritePattern<fir::IfOp> {
13668d69237SJean Perier public:
13768d69237SJean Perier   using OpRewritePattern::OpRewritePattern;
13868d69237SJean Perier 
13968d69237SJean Perier   CfgIfConv(mlir::MLIRContext *ctx, bool forceLoopToExecuteOnce)
140665970d4SValentin Clement       : mlir::OpRewritePattern<fir::IfOp>(ctx) {}
14168d69237SJean Perier 
14268d69237SJean Perier   mlir::LogicalResult
14368d69237SJean Perier   matchAndRewrite(IfOp ifOp, mlir::PatternRewriter &rewriter) const override {
14468d69237SJean Perier     auto loc = ifOp.getLoc();
14568d69237SJean Perier 
14668d69237SJean Perier     // Split the block containing the 'fir.if' into two parts.  The part before
14768d69237SJean Perier     // will contain the condition, the part after will be the continuation
14868d69237SJean Perier     // point.
14968d69237SJean Perier     auto *condBlock = rewriter.getInsertionBlock();
15068d69237SJean Perier     auto opPosition = rewriter.getInsertionPoint();
15168d69237SJean Perier     auto *remainingOpsBlock = rewriter.splitBlock(condBlock, opPosition);
15268d69237SJean Perier     mlir::Block *continueBlock;
15368d69237SJean Perier     if (ifOp.getNumResults() == 0) {
15468d69237SJean Perier       continueBlock = remainingOpsBlock;
15568d69237SJean Perier     } else {
15668d69237SJean Perier       continueBlock =
15768d69237SJean Perier           rewriter.createBlock(remainingOpsBlock, ifOp.getResultTypes());
15868d69237SJean Perier       rewriter.create<mlir::BranchOp>(loc, remainingOpsBlock);
15968d69237SJean Perier     }
16068d69237SJean Perier 
16168d69237SJean Perier     // Move blocks from the "then" region to the region containing 'fir.if',
16268d69237SJean Perier     // place it before the continuation block, and branch to it.
16368d69237SJean Perier     auto &ifOpRegion = ifOp.thenRegion();
16468d69237SJean Perier     auto *ifOpBlock = &ifOpRegion.front();
16568d69237SJean Perier     auto *ifOpTerminator = ifOpRegion.back().getTerminator();
16668d69237SJean Perier     auto ifOpTerminatorOperands = ifOpTerminator->getOperands();
16768d69237SJean Perier     rewriter.setInsertionPointToEnd(&ifOpRegion.back());
16868d69237SJean Perier     rewriter.create<mlir::BranchOp>(loc, continueBlock, ifOpTerminatorOperands);
16968d69237SJean Perier     rewriter.eraseOp(ifOpTerminator);
17068d69237SJean Perier     rewriter.inlineRegionBefore(ifOpRegion, continueBlock);
17168d69237SJean Perier 
17268d69237SJean Perier     // Move blocks from the "else" region (if present) to the region containing
17368d69237SJean Perier     // 'fir.if', place it before the continuation block and branch to it.  It
17468d69237SJean Perier     // will be placed after the "then" regions.
17568d69237SJean Perier     auto *otherwiseBlock = continueBlock;
17668d69237SJean Perier     auto &otherwiseRegion = ifOp.elseRegion();
17768d69237SJean Perier     if (!otherwiseRegion.empty()) {
17868d69237SJean Perier       otherwiseBlock = &otherwiseRegion.front();
17968d69237SJean Perier       auto *otherwiseTerm = otherwiseRegion.back().getTerminator();
18068d69237SJean Perier       auto otherwiseTermOperands = otherwiseTerm->getOperands();
18168d69237SJean Perier       rewriter.setInsertionPointToEnd(&otherwiseRegion.back());
18268d69237SJean Perier       rewriter.create<mlir::BranchOp>(loc, continueBlock,
18368d69237SJean Perier                                       otherwiseTermOperands);
18468d69237SJean Perier       rewriter.eraseOp(otherwiseTerm);
18568d69237SJean Perier       rewriter.inlineRegionBefore(otherwiseRegion, continueBlock);
18668d69237SJean Perier     }
18768d69237SJean Perier 
18868d69237SJean Perier     rewriter.setInsertionPointToEnd(condBlock);
18968d69237SJean Perier     rewriter.create<mlir::CondBranchOp>(
19068d69237SJean Perier         loc, ifOp.condition(), ifOpBlock, llvm::ArrayRef<mlir::Value>(),
19168d69237SJean Perier         otherwiseBlock, llvm::ArrayRef<mlir::Value>());
19268d69237SJean Perier     rewriter.replaceOp(ifOp, continueBlock->getArguments());
19368d69237SJean Perier     return success();
19468d69237SJean Perier   }
19568d69237SJean Perier };
19668d69237SJean Perier 
19768d69237SJean Perier /// Convert `fir.iter_while` to control-flow.
19868d69237SJean Perier class CfgIterWhileConv : public mlir::OpRewritePattern<fir::IterWhileOp> {
19968d69237SJean Perier public:
20068d69237SJean Perier   using OpRewritePattern::OpRewritePattern;
20168d69237SJean Perier 
20268d69237SJean Perier   CfgIterWhileConv(mlir::MLIRContext *ctx, bool forceLoopToExecuteOnce)
203665970d4SValentin Clement       : mlir::OpRewritePattern<fir::IterWhileOp>(ctx) {}
20468d69237SJean Perier 
20568d69237SJean Perier   mlir::LogicalResult
20668d69237SJean Perier   matchAndRewrite(fir::IterWhileOp whileOp,
20768d69237SJean Perier                   mlir::PatternRewriter &rewriter) const override {
20868d69237SJean Perier     auto loc = whileOp.getLoc();
20968d69237SJean Perier 
21068d69237SJean Perier     // Start by splitting the block containing the 'fir.do_loop' into two parts.
21168d69237SJean Perier     // The part before will get the init code, the part after will be the end
21268d69237SJean Perier     // point.
21368d69237SJean Perier     auto *initBlock = rewriter.getInsertionBlock();
21468d69237SJean Perier     auto initPosition = rewriter.getInsertionPoint();
21568d69237SJean Perier     auto *endBlock = rewriter.splitBlock(initBlock, initPosition);
21668d69237SJean Perier 
21768d69237SJean Perier     // Use the first block of the loop body as the condition block since it is
21868d69237SJean Perier     // the block that has the induction variable and loop-carried values as
21968d69237SJean Perier     // arguments. Split out all operations from the first block into a new
22068d69237SJean Perier     // block. Move all body blocks from the loop body region to the region
22168d69237SJean Perier     // containing the loop.
22268d69237SJean Perier     auto *conditionBlock = &whileOp.region().front();
22368d69237SJean Perier     auto *firstBodyBlock =
22468d69237SJean Perier         rewriter.splitBlock(conditionBlock, conditionBlock->begin());
22568d69237SJean Perier     auto *lastBodyBlock = &whileOp.region().back();
22668d69237SJean Perier     rewriter.inlineRegionBefore(whileOp.region(), endBlock);
22768d69237SJean Perier     auto iv = conditionBlock->getArgument(0);
22868d69237SJean Perier     auto iterateVar = conditionBlock->getArgument(1);
22968d69237SJean Perier 
23068d69237SJean Perier     // Append the induction variable stepping logic to the last body block and
23168d69237SJean Perier     // branch back to the condition block. Loop-carried values are taken from
23268d69237SJean Perier     // operands of the loop terminator.
23368d69237SJean Perier     auto *terminator = lastBodyBlock->getTerminator();
23468d69237SJean Perier     rewriter.setInsertionPointToEnd(lastBodyBlock);
23568d69237SJean Perier     auto step = whileOp.step();
236a54f4eaeSMogball     mlir::Value stepped = rewriter.create<mlir::arith::AddIOp>(loc, iv, step);
23768d69237SJean Perier     assert(stepped && "must be a Value");
23868d69237SJean Perier 
23968d69237SJean Perier     llvm::SmallVector<mlir::Value> loopCarried;
24068d69237SJean Perier     loopCarried.push_back(stepped);
24168d69237SJean Perier     auto begin = whileOp.finalValue() ? std::next(terminator->operand_begin())
24268d69237SJean Perier                                       : terminator->operand_begin();
24368d69237SJean Perier     loopCarried.append(begin, terminator->operand_end());
24468d69237SJean Perier     rewriter.create<mlir::BranchOp>(loc, conditionBlock, loopCarried);
24568d69237SJean Perier     rewriter.eraseOp(terminator);
24668d69237SJean Perier 
24768d69237SJean Perier     // Compute loop bounds before branching to the condition.
24868d69237SJean Perier     rewriter.setInsertionPointToEnd(initBlock);
24968d69237SJean Perier     auto lowerBound = whileOp.lowerBound();
25068d69237SJean Perier     auto upperBound = whileOp.upperBound();
25168d69237SJean Perier     assert(lowerBound && upperBound && "must be a Value");
25268d69237SJean Perier 
25368d69237SJean Perier     // The initial values of loop-carried values is obtained from the operands
25468d69237SJean Perier     // of the loop operation.
25568d69237SJean Perier     llvm::SmallVector<mlir::Value> destOperands;
25668d69237SJean Perier     destOperands.push_back(lowerBound);
25768d69237SJean Perier     auto iterOperands = whileOp.getIterOperands();
25868d69237SJean Perier     destOperands.append(iterOperands.begin(), iterOperands.end());
25968d69237SJean Perier     rewriter.create<mlir::BranchOp>(loc, conditionBlock, destOperands);
26068d69237SJean Perier 
26168d69237SJean Perier     // With the body block done, we can fill in the condition block.
26268d69237SJean Perier     rewriter.setInsertionPointToEnd(conditionBlock);
26368d69237SJean Perier     // The comparison depends on the sign of the step value. We fully expect
26468d69237SJean Perier     // this expression to be folded by the optimizer or LLVM. This expression
26568d69237SJean Perier     // is written this way so that `step == 0` always returns `false`.
266a54f4eaeSMogball     auto zero = rewriter.create<mlir::arith::ConstantIndexOp>(loc, 0);
267a54f4eaeSMogball     auto compl0 = rewriter.create<mlir::arith::CmpIOp>(
268a54f4eaeSMogball         loc, arith::CmpIPredicate::slt, zero, step);
269a54f4eaeSMogball     auto compl1 = rewriter.create<mlir::arith::CmpIOp>(
270a54f4eaeSMogball         loc, arith::CmpIPredicate::sle, iv, upperBound);
271a54f4eaeSMogball     auto compl2 = rewriter.create<mlir::arith::CmpIOp>(
272a54f4eaeSMogball         loc, arith::CmpIPredicate::slt, step, zero);
273a54f4eaeSMogball     auto compl3 = rewriter.create<mlir::arith::CmpIOp>(
274a54f4eaeSMogball         loc, arith::CmpIPredicate::sle, upperBound, iv);
275a54f4eaeSMogball     auto cmp0 = rewriter.create<mlir::arith::AndIOp>(loc, compl0, compl1);
276a54f4eaeSMogball     auto cmp1 = rewriter.create<mlir::arith::AndIOp>(loc, compl2, compl3);
277a54f4eaeSMogball     auto cmp2 = rewriter.create<mlir::arith::OrIOp>(loc, cmp0, cmp1);
27868d69237SJean Perier     // Remember to AND in the early-exit bool.
279a54f4eaeSMogball     auto comparison =
280a54f4eaeSMogball         rewriter.create<mlir::arith::AndIOp>(loc, iterateVar, cmp2);
28168d69237SJean Perier     rewriter.create<mlir::CondBranchOp>(loc, comparison, firstBodyBlock,
28268d69237SJean Perier                                         llvm::ArrayRef<mlir::Value>(), endBlock,
28368d69237SJean Perier                                         llvm::ArrayRef<mlir::Value>());
28468d69237SJean Perier     // The result of the loop operation is the values of the condition block
28568d69237SJean Perier     // arguments except the induction variable on the last iteration.
28668d69237SJean Perier     auto args = whileOp.finalValue()
28768d69237SJean Perier                     ? conditionBlock->getArguments()
28868d69237SJean Perier                     : conditionBlock->getArguments().drop_front();
28968d69237SJean Perier     rewriter.replaceOp(whileOp, args);
29068d69237SJean Perier     return success();
29168d69237SJean Perier   }
29268d69237SJean Perier };
29368d69237SJean Perier 
29468d69237SJean Perier /// Convert FIR structured control flow ops to CFG ops.
29568d69237SJean Perier class CfgConversion : public CFGConversionBase<CfgConversion> {
29668d69237SJean Perier public:
297196c4279SRiver Riddle   void runOnOperation() override {
29868d69237SJean Perier     auto *context = &getContext();
2999f85c198SRiver Riddle     mlir::RewritePatternSet patterns(context);
30068d69237SJean Perier     patterns.insert<CfgLoopConv, CfgIfConv, CfgIterWhileConv>(
30168d69237SJean Perier         context, forceLoopToExecuteOnce);
30268d69237SJean Perier     mlir::ConversionTarget target(*context);
30368d69237SJean Perier     target.addLegalDialect<mlir::AffineDialect, FIROpsDialect,
30468d69237SJean Perier                            mlir::StandardOpsDialect>();
30568d69237SJean Perier 
30668d69237SJean Perier     // apply the patterns
30768d69237SJean Perier     target.addIllegalOp<ResultOp, DoLoopOp, IfOp, IterWhileOp>();
30868d69237SJean Perier     target.markUnknownOpDynamicallyLegal([](Operation *) { return true; });
309196c4279SRiver Riddle     if (mlir::failed(mlir::applyPartialConversion(getOperation(), target,
31068d69237SJean Perier                                                   std::move(patterns)))) {
31168d69237SJean Perier       mlir::emitError(mlir::UnknownLoc::get(context),
31268d69237SJean Perier                       "error in converting to CFG\n");
31368d69237SJean Perier       signalPassFailure();
31468d69237SJean Perier     }
31568d69237SJean Perier   }
31668d69237SJean Perier };
31768d69237SJean Perier } // namespace
31868d69237SJean Perier 
31968d69237SJean Perier /// Convert FIR's structured control flow ops to CFG ops.  This
32068d69237SJean Perier /// conversion enables the `createLowerToCFGPass` to transform these to CFG
32168d69237SJean Perier /// form.
32268d69237SJean Perier std::unique_ptr<mlir::Pass> fir::createFirToCfgPass() {
32368d69237SJean Perier   return std::make_unique<CfgConversion>();
32468d69237SJean Perier }
325