15#include "mlir/Dialect/Func/IR/FuncOps.h"
16#include "mlir/IR/Block.h"
17#include "mlir/IR/Builders.h"
18#include "mlir/IR/PatternMatch.h"
19#include "mlir/Support/LogicalResult.h"
20#include "mlir/Transforms/DialectConversion.h"
21#include "mlir/Transforms/GreedyPatternRewriteDriver.h"
32void lowerTerminator(mlir::Operation *op, mlir::Block *dest,
33 mlir::PatternRewriter &rewriter) {
34 assert(op->hasTrait<mlir::OpTrait::IsTerminator>() &&
"not a terminator");
35 mlir::OpBuilder::InsertionGuard guard(rewriter);
36 rewriter.setInsertionPoint(op);
37 rewriter.replaceOpWithNewOp<cir::BrOp>(op, dest);
42template <
typename... Ops>
43void walkRegionSkipping(
45 mlir::function_ref<mlir::WalkResult(mlir::Operation *)> callback) {
46 region.walk<mlir::WalkOrder::PreOrder>([&](mlir::Operation *op) {
48 return mlir::WalkResult::skip();
53struct CIRFlattenCFGPass :
public CIRFlattenCFGBase<CIRFlattenCFGPass> {
55 CIRFlattenCFGPass() =
default;
56 void runOnOperation()
override;
59struct CIRIfFlattening :
public mlir::OpRewritePattern<cir::IfOp> {
60 using OpRewritePattern<IfOp>::OpRewritePattern;
63 matchAndRewrite(cir::IfOp ifOp,
64 mlir::PatternRewriter &rewriter)
const override {
65 mlir::OpBuilder::InsertionGuard guard(rewriter);
66 mlir::Location loc = ifOp.getLoc();
67 bool emptyElse = ifOp.getElseRegion().empty();
68 mlir::Block *currentBlock = rewriter.getInsertionBlock();
69 mlir::Block *remainingOpsBlock =
70 rewriter.splitBlock(currentBlock, rewriter.getInsertionPoint());
71 mlir::Block *continueBlock;
72 if (ifOp->getResults().empty())
73 continueBlock = remainingOpsBlock;
75 llvm_unreachable(
"NYI");
78 mlir::Block *thenBeforeBody = &ifOp.getThenRegion().front();
79 mlir::Block *thenAfterBody = &ifOp.getThenRegion().back();
80 rewriter.inlineRegionBefore(ifOp.getThenRegion(), continueBlock);
82 rewriter.setInsertionPointToEnd(thenAfterBody);
83 if (
auto thenYieldOp =
84 dyn_cast<cir::YieldOp>(thenAfterBody->getTerminator())) {
85 rewriter.replaceOpWithNewOp<cir::BrOp>(thenYieldOp, thenYieldOp.getArgs(),
89 rewriter.setInsertionPointToEnd(continueBlock);
92 mlir::Block *elseBeforeBody =
nullptr;
93 mlir::Block *elseAfterBody =
nullptr;
95 elseBeforeBody = &ifOp.getElseRegion().front();
96 elseAfterBody = &ifOp.getElseRegion().back();
97 rewriter.inlineRegionBefore(ifOp.getElseRegion(), continueBlock);
99 elseBeforeBody = elseAfterBody = continueBlock;
102 rewriter.setInsertionPointToEnd(currentBlock);
103 cir::BrCondOp::create(rewriter, loc, ifOp.getCondition(), thenBeforeBody,
107 rewriter.setInsertionPointToEnd(elseAfterBody);
108 if (
auto elseYieldOP =
109 dyn_cast<cir::YieldOp>(elseAfterBody->getTerminator())) {
110 rewriter.replaceOpWithNewOp<cir::BrOp>(
111 elseYieldOP, elseYieldOP.getArgs(), continueBlock);
115 rewriter.replaceOp(ifOp, continueBlock->getArguments());
116 return mlir::success();
120class CIRScopeOpFlattening :
public mlir::OpRewritePattern<cir::ScopeOp> {
122 using OpRewritePattern<cir::ScopeOp>::OpRewritePattern;
125 matchAndRewrite(cir::ScopeOp scopeOp,
126 mlir::PatternRewriter &rewriter)
const override {
127 mlir::OpBuilder::InsertionGuard guard(rewriter);
128 mlir::Location loc = scopeOp.getLoc();
136 if (scopeOp.isEmpty()) {
137 rewriter.eraseOp(scopeOp);
138 return mlir::success();
143 mlir::Block *currentBlock = rewriter.getInsertionBlock();
144 mlir::Block *continueBlock =
145 rewriter.splitBlock(currentBlock, rewriter.getInsertionPoint());
146 if (scopeOp.getNumResults() > 0)
147 continueBlock->addArguments(scopeOp.getResultTypes(), loc);
150 mlir::Block *beforeBody = &scopeOp.getScopeRegion().front();
151 mlir::Block *afterBody = &scopeOp.getScopeRegion().back();
152 rewriter.inlineRegionBefore(scopeOp.getScopeRegion(), continueBlock);
155 rewriter.setInsertionPointToEnd(currentBlock);
157 cir::BrOp::create(rewriter, loc, mlir::ValueRange(), beforeBody);
161 rewriter.setInsertionPointToEnd(afterBody);
162 if (
auto yieldOp = dyn_cast<cir::YieldOp>(afterBody->getTerminator())) {
163 rewriter.replaceOpWithNewOp<cir::BrOp>(yieldOp, yieldOp.getArgs(),
168 rewriter.replaceOp(scopeOp, continueBlock->getArguments());
170 return mlir::success();
174class CIRSwitchOpFlattening :
public mlir::OpRewritePattern<cir::SwitchOp> {
176 using OpRewritePattern<cir::SwitchOp>::OpRewritePattern;
178 inline void rewriteYieldOp(mlir::PatternRewriter &rewriter,
179 cir::YieldOp yieldOp,
180 mlir::Block *destination)
const {
181 rewriter.setInsertionPoint(yieldOp);
182 rewriter.replaceOpWithNewOp<cir::BrOp>(yieldOp, yieldOp.getOperands(),
187 Block *condBrToRangeDestination(cir::SwitchOp op,
188 mlir::PatternRewriter &rewriter,
189 mlir::Block *rangeDestination,
190 mlir::Block *defaultDestination,
191 const APInt &lowerBound,
192 const APInt &upperBound)
const {
193 assert(lowerBound.sle(upperBound) &&
"Invalid range");
194 mlir::Block *resBlock = rewriter.createBlock(defaultDestination);
195 cir::IntType sIntType = cir::IntType::get(op.getContext(), 32,
true);
196 cir::IntType uIntType = cir::IntType::get(op.getContext(), 32,
false);
198 cir::ConstantOp rangeLength = cir::ConstantOp::create(
199 rewriter, op.getLoc(),
200 cir::IntAttr::get(sIntType, upperBound - lowerBound));
202 cir::ConstantOp lowerBoundValue = cir::ConstantOp::create(
203 rewriter, op.getLoc(), cir::IntAttr::get(sIntType, lowerBound));
204 cir::BinOp diffValue =
205 cir::BinOp::create(rewriter, op.getLoc(), sIntType, cir::BinOpKind::Sub,
206 op.getCondition(), lowerBoundValue);
209 cir::CastOp uDiffValue = cir::CastOp::create(
210 rewriter, op.getLoc(), uIntType, CastKind::integral, diffValue);
211 cir::CastOp uRangeLength = cir::CastOp::create(
212 rewriter, op.getLoc(), uIntType, CastKind::integral, rangeLength);
214 cir::CmpOp cmpResult = cir::CmpOp::create(
215 rewriter, op.getLoc(), cir::BoolType::get(op.getContext()),
216 cir::CmpOpKind::le, uDiffValue, uRangeLength);
217 cir::BrCondOp::create(rewriter, op.getLoc(), cmpResult, rangeDestination,
223 matchAndRewrite(cir::SwitchOp op,
224 mlir::PatternRewriter &rewriter)
const override {
225 llvm::SmallVector<CaseOp> cases;
226 op.collectCases(cases);
230 rewriter.eraseOp(op);
231 return mlir::success();
235 mlir::Block *exitBlock = rewriter.splitBlock(
236 rewriter.getBlock(), op->getNextNode()->getIterator());
249 cir::YieldOp switchYield =
nullptr;
251 for (mlir::Block &block :
252 llvm::make_early_inc_range(op.getBody().getBlocks()))
253 if (
auto yieldOp = dyn_cast<cir::YieldOp>(block.getTerminator()))
254 switchYield = yieldOp;
256 assert(!op.getBody().empty());
257 mlir::Block *originalBlock = op->getBlock();
258 mlir::Block *swopBlock =
259 rewriter.splitBlock(originalBlock, op->getIterator());
260 rewriter.inlineRegionBefore(op.getBody(), exitBlock);
263 rewriteYieldOp(rewriter, switchYield, exitBlock);
265 rewriter.setInsertionPointToEnd(originalBlock);
266 cir::BrOp::create(rewriter, op.getLoc(), swopBlock);
271 llvm::SmallVector<mlir::APInt, 8> caseValues;
272 llvm::SmallVector<mlir::Block *, 8> caseDestinations;
273 llvm::SmallVector<mlir::ValueRange, 8> caseOperands;
275 llvm::SmallVector<std::pair<APInt, APInt>> rangeValues;
276 llvm::SmallVector<mlir::Block *> rangeDestinations;
277 llvm::SmallVector<mlir::ValueRange> rangeOperands;
280 mlir::Block *defaultDestination = exitBlock;
281 mlir::ValueRange defaultOperands = exitBlock->getArguments();
284 for (cir::CaseOp caseOp : cases) {
285 mlir::Region ®ion = caseOp.getCaseRegion();
288 switch (caseOp.getKind()) {
289 case cir::CaseOpKind::Default:
290 defaultDestination = ®ion.front();
291 defaultOperands = defaultDestination->getArguments();
293 case cir::CaseOpKind::Range:
294 assert(caseOp.getValue().size() == 2 &&
295 "Case range should have 2 case value");
296 rangeValues.push_back(
297 {cast<cir::IntAttr>(caseOp.getValue()[0]).getValue(),
298 cast<cir::IntAttr>(caseOp.getValue()[1]).getValue()});
299 rangeDestinations.push_back(®ion.front());
300 rangeOperands.push_back(rangeDestinations.back()->getArguments());
302 case cir::CaseOpKind::Anyof:
303 case cir::CaseOpKind::Equal:
305 for (
const mlir::Attribute &value : caseOp.getValue()) {
306 caseValues.push_back(cast<cir::IntAttr>(value).getValue());
307 caseDestinations.push_back(®ion.front());
308 caseOperands.push_back(caseDestinations.back()->getArguments());
314 walkRegionSkipping<cir::LoopOpInterface, cir::SwitchOp>(
315 region, [&](mlir::Operation *op) {
316 if (!isa<cir::BreakOp>(op))
317 return mlir::WalkResult::advance();
319 lowerTerminator(op, exitBlock, rewriter);
320 return mlir::WalkResult::skip();
324 for (mlir::Block &blk : region.getBlocks()) {
325 if (blk.getNumSuccessors())
328 if (
auto yieldOp = dyn_cast<cir::YieldOp>(blk.getTerminator())) {
329 mlir::Operation *nextOp = caseOp->getNextNode();
330 assert(nextOp &&
"caseOp is not expected to be the last op");
331 mlir::Block *oldBlock = nextOp->getBlock();
332 mlir::Block *newBlock =
333 rewriter.splitBlock(oldBlock, nextOp->getIterator());
334 rewriter.setInsertionPointToEnd(oldBlock);
335 cir::BrOp::create(rewriter, nextOp->getLoc(), mlir::ValueRange(),
337 rewriteYieldOp(rewriter, yieldOp, newBlock);
341 mlir::Block *oldBlock = caseOp->getBlock();
342 mlir::Block *newBlock =
343 rewriter.splitBlock(oldBlock, caseOp->getIterator());
345 mlir::Block &entryBlock = caseOp.getCaseRegion().front();
346 rewriter.inlineRegionBefore(caseOp.getCaseRegion(), newBlock);
349 rewriter.setInsertionPointToEnd(oldBlock);
350 cir::BrOp::create(rewriter, caseOp.getLoc(), &entryBlock);
354 for (cir::CaseOp caseOp : cases) {
355 mlir::Block *caseBlock = caseOp->getBlock();
358 if (caseBlock->hasNoPredecessors())
359 rewriter.eraseBlock(caseBlock);
361 rewriter.eraseOp(caseOp);
364 for (
auto [rangeVal, operand, destination] :
365 llvm::zip(rangeValues, rangeOperands, rangeDestinations)) {
366 APInt lowerBound = rangeVal.first;
367 APInt upperBound = rangeVal.second;
370 if (lowerBound.sgt(upperBound))
375 constexpr int kSmallRangeThreshold = 64;
376 if ((upperBound - lowerBound)
377 .ult(llvm::APInt(32, kSmallRangeThreshold))) {
378 for (APInt iValue = lowerBound; iValue.sle(upperBound); ++iValue) {
379 caseValues.push_back(iValue);
380 caseOperands.push_back(operand);
381 caseDestinations.push_back(destination);
387 condBrToRangeDestination(op, rewriter, destination,
388 defaultDestination, lowerBound, upperBound);
389 defaultOperands = operand;
393 rewriter.setInsertionPoint(op);
394 rewriter.replaceOpWithNewOp<cir::SwitchFlatOp>(
395 op, op.getCondition(), defaultDestination, defaultOperands, caseValues,
396 caseDestinations, caseOperands);
398 return mlir::success();
402class CIRLoopOpInterfaceFlattening
403 :
public mlir::OpInterfaceRewritePattern<cir::LoopOpInterface> {
405 using mlir::OpInterfaceRewritePattern<
406 cir::LoopOpInterface>::OpInterfaceRewritePattern;
408 inline void lowerConditionOp(cir::ConditionOp op, mlir::Block *body,
410 mlir::PatternRewriter &rewriter)
const {
411 mlir::OpBuilder::InsertionGuard guard(rewriter);
412 rewriter.setInsertionPoint(op);
413 rewriter.replaceOpWithNewOp<cir::BrCondOp>(op, op.getCondition(), body,
418 matchAndRewrite(cir::LoopOpInterface op,
419 mlir::PatternRewriter &rewriter)
const final {
421 mlir::Block *entry = rewriter.getInsertionBlock();
423 rewriter.splitBlock(entry, rewriter.getInsertionPoint());
424 mlir::Block *cond = &op.getCond().front();
425 mlir::Block *body = &op.getBody().front();
427 (op.maybeGetStep() ? &op.maybeGetStep()->front() :
nullptr);
430 rewriter.setInsertionPointToEnd(entry);
431 cir::BrOp::create(rewriter, op.getLoc(), &op.getEntry().front());
434 auto conditionOp = cast<cir::ConditionOp>(cond->getTerminator());
435 lowerConditionOp(conditionOp, body, exit, rewriter);
442 mlir::Block *dest = (
step ?
step : cond);
443 op.walkBodySkippingNestedLoops([&](mlir::Operation *op) {
444 if (!isa<cir::ContinueOp>(op))
445 return mlir::WalkResult::advance();
447 lowerTerminator(op, dest, rewriter);
448 return mlir::WalkResult::skip();
453 walkRegionSkipping<cir::LoopOpInterface>(
454 op.getBody(), [&](mlir::Operation *op) {
455 if (!isa<cir::BreakOp>(op))
456 return mlir::WalkResult::advance();
458 lowerTerminator(op, exit, rewriter);
459 return mlir::WalkResult::skip();
463 for (mlir::Block &blk : op.getBody().getBlocks()) {
464 auto bodyYield = dyn_cast<cir::YieldOp>(blk.getTerminator());
466 lowerTerminator(bodyYield, (
step ?
step : cond), rewriter);
471 lowerTerminator(cast<cir::YieldOp>(
step->getTerminator()), cond,
475 rewriter.inlineRegionBefore(op.getCond(), exit);
476 rewriter.inlineRegionBefore(op.getBody(), exit);
478 rewriter.inlineRegionBefore(*op.maybeGetStep(), exit);
480 rewriter.eraseOp(op);
481 return mlir::success();
485class CIRTernaryOpFlattening :
public mlir::OpRewritePattern<cir::TernaryOp> {
487 using OpRewritePattern<cir::TernaryOp>::OpRewritePattern;
490 matchAndRewrite(cir::TernaryOp op,
491 mlir::PatternRewriter &rewriter)
const override {
492 Location loc = op->getLoc();
493 Block *condBlock = rewriter.getInsertionBlock();
494 Block::iterator opPosition = rewriter.getInsertionPoint();
495 Block *remainingOpsBlock = rewriter.splitBlock(condBlock, opPosition);
496 llvm::SmallVector<mlir::Location, 2> locs;
499 if (op->getResultTypes().size())
501 Block *continueBlock =
502 rewriter.createBlock(remainingOpsBlock, op->getResultTypes(), locs);
503 cir::BrOp::create(rewriter, loc, remainingOpsBlock);
505 Region &trueRegion = op.getTrueRegion();
506 Block *trueBlock = &trueRegion.front();
507 mlir::Operation *trueTerminator = trueRegion.back().getTerminator();
508 rewriter.setInsertionPointToEnd(&trueRegion.back());
511 if (
auto trueYieldOp = dyn_cast<cir::YieldOp>(trueTerminator)) {
512 rewriter.replaceOpWithNewOp<cir::BrOp>(trueYieldOp, trueYieldOp.getArgs(),
514 }
else if (isa<cir::UnreachableOp>(trueTerminator)) {
517 trueTerminator->emitError(
"unexpected terminator in ternary true region, "
518 "expected yield or unreachable, got: ")
519 << trueTerminator->getName();
520 return mlir::failure();
522 rewriter.inlineRegionBefore(trueRegion, continueBlock);
524 Block *falseBlock = continueBlock;
525 Region &falseRegion = op.getFalseRegion();
527 falseBlock = &falseRegion.front();
528 mlir::Operation *falseTerminator = falseRegion.back().getTerminator();
529 rewriter.setInsertionPointToEnd(&falseRegion.back());
532 if (
auto falseYieldOp = dyn_cast<cir::YieldOp>(falseTerminator)) {
533 rewriter.replaceOpWithNewOp<cir::BrOp>(
534 falseYieldOp, falseYieldOp.getArgs(), continueBlock);
535 }
else if (isa<cir::UnreachableOp>(falseTerminator)) {
538 falseTerminator->emitError(
"unexpected terminator in ternary false "
539 "region, expected yield or unreachable, got: ")
540 << falseTerminator->getName();
541 return mlir::failure();
543 rewriter.inlineRegionBefore(falseRegion, continueBlock);
545 rewriter.setInsertionPointToEnd(condBlock);
546 cir::BrCondOp::create(rewriter, loc, op.getCond(), trueBlock, falseBlock);
548 rewriter.replaceOp(op, continueBlock->getArguments());
551 return mlir::success();
555class CIRTryOpFlattening :
public mlir::OpRewritePattern<cir::TryOp> {
557 using OpRewritePattern<cir::TryOp>::OpRewritePattern;
559 mlir::Block *buildTryBody(cir::TryOp tryOp,
560 mlir::PatternRewriter &rewriter)
const {
563 mlir::Block *beforeTryScopeBlock = rewriter.getInsertionBlock();
564 mlir::Block *afterTry =
565 rewriter.splitBlock(beforeTryScopeBlock, rewriter.getInsertionPoint());
568 mlir::Block *beforeBody = &tryOp.getTryRegion().front();
569 rewriter.inlineRegionBefore(tryOp.getTryRegion(), afterTry);
572 rewriter.setInsertionPointToEnd(beforeTryScopeBlock);
573 cir::BrOp::create(rewriter, tryOp.getLoc(), mlir::ValueRange(), beforeBody);
577 void buildHandlers(cir::TryOp tryOp, mlir::PatternRewriter &rewriter,
578 mlir::Block *afterBody, mlir::Block *afterTry,
579 SmallVectorImpl<cir::CallOp> &callsToRewrite,
580 SmallVectorImpl<mlir::Block *> &landingPads)
const {
582 rewriter.setInsertionPointToEnd(afterBody);
584 mlir::Block *beforeCatch = rewriter.getInsertionBlock();
585 rewriter.setInsertionPointToEnd(beforeCatch);
589 if (
auto tryBodyYield = dyn_cast<cir::YieldOp>(afterBody->getTerminator()))
590 rewriter.replaceOpWithNewOp<cir::BrOp>(tryBodyYield, afterTry);
592 mlir::ArrayAttr handlers = tryOp.getHandlerTypesAttr();
593 if (!handlers || handlers.empty())
596 llvm_unreachable(
"TryOpFlattening buildHandlers with CallsOp is NYI");
600 matchAndRewrite(cir::TryOp tryOp,
601 mlir::PatternRewriter &rewriter)
const override {
602 mlir::OpBuilder::InsertionGuard guard(rewriter);
603 mlir::Block *afterBody = &tryOp.getTryRegion().back();
607 llvm::SmallVector<cir::CallOp, 4> callsToRewrite;
608 tryOp.getTryRegion().walk([&](CallOp op) {
613 if (op->getParentOfType<cir::TryOp>() != tryOp)
615 callsToRewrite.push_back(op);
618 if (!callsToRewrite.empty())
620 "TryOpFlattening with try block that contains CallOps is NYI");
623 mlir::Block *afterTry = buildTryBody(tryOp, rewriter);
626 llvm::SmallVector<mlir::Block *, 4> landingPads;
627 buildHandlers(tryOp, rewriter, afterBody, afterTry, callsToRewrite,
630 rewriter.eraseOp(tryOp);
632 assert((landingPads.size() == callsToRewrite.size()) &&
633 "expected matching number of entries");
636 auto brOp = dyn_cast<cir::BrOp>(afterTry->getTerminator());
637 if (brOp && brOp.getDest()->hasNoPredecessors()) {
638 mlir::Block *srcBlock = brOp.getDest();
639 rewriter.eraseOp(brOp);
640 rewriter.mergeBlocks(srcBlock, afterTry);
643 return mlir::success();
647void populateFlattenCFGPatterns(RewritePatternSet &patterns) {
649 .add<CIRIfFlattening, CIRLoopOpInterfaceFlattening, CIRScopeOpFlattening,
650 CIRSwitchOpFlattening, CIRTernaryOpFlattening, CIRTryOpFlattening>(
651 patterns.getContext());
654void CIRFlattenCFGPass::runOnOperation() {
655 RewritePatternSet patterns(&getContext());
656 populateFlattenCFGPatterns(patterns);
659 llvm::SmallVector<Operation *, 16> ops;
660 getOperation()->walk<mlir::WalkOrder::PostOrder>([&](Operation *op) {
664 if (isa<IfOp, ScopeOp, SwitchOp, LoopOpInterface, TernaryOp, TryOp>(op))
669 if (applyOpPatternsGreedily(ops, std::move(patterns)).failed())
678 return std::make_unique<CIRFlattenCFGPass>();
std::unique_ptr< Pass > createCIRFlattenCFGPass()
float __ovld __cnfn step(float, float)
Returns 0.0 if x < edge, otherwise it returns 1.0.
static bool stackSaveOp()