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/Interfaces/SideEffectInterfaces.h"
20#include "mlir/Rewrite/PatternApplicator.h"
21#include "mlir/Support/LogicalResult.h"
22#include "mlir/Transforms/DialectConversion.h"
28#include "llvm/ADT/TypeSwitch.h"
34#define GEN_PASS_DEF_CIRFLATTENCFG
35#include "clang/CIR/Dialect/Passes.h.inc"
41void lowerTerminator(mlir::Operation *op, mlir::Block *dest,
42 mlir::PatternRewriter &rewriter,
43 cir::LoopHintAttr loopHints = {}) {
44 assert(op->hasTrait<mlir::OpTrait::IsTerminator>() &&
"not a terminator");
45 mlir::OpBuilder::InsertionGuard guard(rewriter);
46 rewriter.setInsertionPoint(op);
47 rewriter.replaceOpWithNewOp<cir::BrOp>(op, mlir::ValueRange{}, loopHints,
53template <
typename... Ops>
54void walkRegionSkipping(
56 mlir::function_ref<mlir::WalkResult(mlir::Operation *)> callback) {
57 region.walk<mlir::WalkOrder::PreOrder>([&](mlir::Operation *op) {
59 return mlir::WalkResult::skip();
73static bool hasNestedOpsToFlatten(mlir::Region ®ion) {
75 .walk([](mlir::Operation *op) {
76 if (op->getNumRegions() > 0 && !isa<cir::CaseOp>(op))
77 return mlir::WalkResult::interrupt();
78 return mlir::WalkResult::advance();
89static bool isNonReturningTerminator(mlir::Operation *op) {
90 return mlir::isa_and_nonnull<cir::UnreachableOp, cir::TrapOp>(op);
108static mlir::LogicalResult
109rewriteRegionExitToContinue(mlir::PatternRewriter &rewriter,
110 mlir::Region ®ion, mlir::Block *continueBlock,
111 llvm::StringRef regionDescription) {
112 mlir::Operation *terminator = region.back().getTerminator();
113 rewriter.setInsertionPointToEnd(®ion.back());
114 if (
auto yieldOp = mlir::dyn_cast<cir::YieldOp>(terminator)) {
115 rewriter.replaceOpWithNewOp<cir::BrOp>(
116 yieldOp, yieldOp.getArgs(),
nullptr, continueBlock);
117 return mlir::success();
119 if (isNonReturningTerminator(terminator))
120 return mlir::success();
121 terminator->emitError(
"unexpected terminator in ")
123 <<
" region, expected yield, unreachable, or trap, got: "
124 << terminator->getName();
125 return mlir::failure();
128struct CIRFlattenCFGPass :
public impl::CIRFlattenCFGBase<CIRFlattenCFGPass> {
130 CIRFlattenCFGPass() =
default;
131 void runOnOperation()
override;
134struct CIRIfFlattening :
public mlir::OpRewritePattern<cir::IfOp> {
135 using OpRewritePattern<IfOp>::OpRewritePattern;
138 matchAndRewrite(cir::IfOp ifOp,
139 mlir::PatternRewriter &rewriter)
const override {
140 mlir::OpBuilder::InsertionGuard guard(rewriter);
141 mlir::Location loc = ifOp.getLoc();
142 bool emptyElse = ifOp.getElseRegion().empty();
143 mlir::Block *currentBlock = rewriter.getInsertionBlock();
144 mlir::Block *remainingOpsBlock =
145 rewriter.splitBlock(currentBlock, rewriter.getInsertionPoint());
146 mlir::Block *continueBlock;
147 if (ifOp->getResults().empty())
148 continueBlock = remainingOpsBlock;
150 llvm_unreachable(
"NYI");
153 mlir::Block *thenBeforeBody = &ifOp.getThenRegion().front();
154 mlir::Block *thenAfterBody = &ifOp.getThenRegion().back();
155 rewriter.inlineRegionBefore(ifOp.getThenRegion(), continueBlock);
157 rewriter.setInsertionPointToEnd(thenAfterBody);
158 if (
auto thenYieldOp =
159 dyn_cast<cir::YieldOp>(thenAfterBody->getTerminator())) {
160 rewriter.replaceOpWithNewOp<cir::BrOp>(thenYieldOp, thenYieldOp.getArgs(),
165 rewriter.setInsertionPointToEnd(continueBlock);
168 mlir::Block *elseBeforeBody =
nullptr;
169 mlir::Block *elseAfterBody =
nullptr;
171 elseBeforeBody = &ifOp.getElseRegion().front();
172 elseAfterBody = &ifOp.getElseRegion().back();
173 rewriter.inlineRegionBefore(ifOp.getElseRegion(), continueBlock);
175 elseBeforeBody = elseAfterBody = continueBlock;
178 rewriter.setInsertionPointToEnd(currentBlock);
179 cir::BrCondOp::create(rewriter, loc, ifOp.getCondition(), thenBeforeBody,
183 rewriter.setInsertionPointToEnd(elseAfterBody);
184 if (
auto elseYieldOP =
185 dyn_cast<cir::YieldOp>(elseAfterBody->getTerminator())) {
186 rewriter.replaceOpWithNewOp<cir::BrOp>(
187 elseYieldOP, elseYieldOP.getArgs(),
nullptr,
192 rewriter.replaceOp(ifOp, continueBlock->getArguments());
193 return mlir::success();
197class CIRScopeOpFlattening :
public mlir::OpRewritePattern<cir::ScopeOp> {
199 using OpRewritePattern<cir::ScopeOp>::OpRewritePattern;
202 matchAndRewrite(cir::ScopeOp scopeOp,
203 mlir::PatternRewriter &rewriter)
const override {
204 mlir::OpBuilder::InsertionGuard guard(rewriter);
205 mlir::Location loc = scopeOp.getLoc();
213 if (scopeOp.isEmpty()) {
214 rewriter.eraseOp(scopeOp);
215 return mlir::success();
220 mlir::Block *currentBlock = rewriter.getInsertionBlock();
221 mlir::Block *continueBlock =
222 rewriter.splitBlock(currentBlock, rewriter.getInsertionPoint());
223 if (scopeOp.getNumResults() > 0)
224 continueBlock->addArguments(scopeOp.getResultTypes(), loc);
227 mlir::Block *beforeBody = &scopeOp.getScopeRegion().front();
228 mlir::Block *afterBody = &scopeOp.getScopeRegion().back();
229 rewriter.inlineRegionBefore(scopeOp.getScopeRegion(), continueBlock);
232 rewriter.setInsertionPointToEnd(currentBlock);
234 cir::BrOp::create(rewriter, loc, mlir::ValueRange(),
nullptr,
239 rewriter.setInsertionPointToEnd(afterBody);
240 if (
auto yieldOp = dyn_cast<cir::YieldOp>(afterBody->getTerminator())) {
241 rewriter.replaceOpWithNewOp<cir::BrOp>(yieldOp, yieldOp.getArgs(),
247 rewriter.replaceOp(scopeOp, continueBlock->getArguments());
249 return mlir::success();
253class CIRSwitchOpFlattening :
public mlir::OpRewritePattern<cir::SwitchOp> {
255 using OpRewritePattern<cir::SwitchOp>::OpRewritePattern;
257 inline void rewriteYieldOp(mlir::PatternRewriter &rewriter,
258 cir::YieldOp yieldOp,
259 mlir::Block *destination)
const {
260 rewriter.setInsertionPoint(yieldOp);
261 rewriter.replaceOpWithNewOp<cir::BrOp>(yieldOp, yieldOp.getOperands(),
262 nullptr, destination);
266 Block *condBrToRangeDestination(cir::SwitchOp op,
267 mlir::PatternRewriter &rewriter,
268 mlir::Block *rangeDestination,
269 mlir::Block *defaultDestination,
270 const APInt &lowerBound,
271 const APInt &upperBound)
const {
272 auto condType = mlir::cast<cir::IntType>(op.getCondition().getType());
273 bool isSigned = condType.isSigned();
275 (isSigned ? lowerBound.sle(upperBound) : lowerBound.ule(upperBound)) &&
277 mlir::Block *resBlock = rewriter.createBlock(defaultDestination);
283 cir::IntType uIntType =
284 cir::IntType::get(op.getContext(), condType.getWidth(),
287 cir::ConstantOp lowerBoundValue = cir::ConstantOp::create(
288 rewriter, op.getLoc(), cir::IntAttr::get(condType, lowerBound));
289 mlir::Value diffValue = cir::SubOp::create(
290 rewriter, op.getLoc(), op.getCondition(), lowerBoundValue);
298 diffValue = cir::CastOp::create(rewriter, op.getLoc(), uIntType,
299 CastKind::integral, diffValue);
301 cir::ConstantOp rangeLength = cir::ConstantOp::create(
302 rewriter, op.getLoc(),
303 cir::IntAttr::get(uIntType, upperBound - lowerBound));
305 cir::CmpOp cmpResult = cir::CmpOp::create(
306 rewriter, op.getLoc(), cir::CmpOpKind::le, diffValue, rangeLength);
307 cir::BrCondOp::create(rewriter, op.getLoc(), cmpResult, rangeDestination,
313 matchAndRewrite(cir::SwitchOp op,
314 mlir::PatternRewriter &rewriter)
const override {
319 for (mlir::Region ®ion : op->getRegions())
320 if (hasNestedOpsToFlatten(region))
321 return mlir::failure();
324 if (op.getBody().hasOneBlock() &&
325 op.getBody().front().without_terminator().empty()) {
326 rewriter.eraseOp(op);
327 return mlir::success();
330 llvm::SmallVector<CaseOp> cases;
331 op.collectCases(cases);
334 mlir::Block *exitBlock = rewriter.splitBlock(
335 rewriter.getBlock(), op->getNextNode()->getIterator());
348 walkRegionSkipping<cir::LoopOpInterface, cir::SwitchOp>(
349 op.getBody(), [&](mlir::Operation *op) {
350 if (!isa<cir::BreakOp>(op))
351 return mlir::WalkResult::advance();
353 lowerTerminator(op, exitBlock, rewriter);
354 return mlir::WalkResult::skip();
360 cir::YieldOp switchYield =
nullptr;
362 for (mlir::Block &block :
363 llvm::make_early_inc_range(op.getBody().getBlocks()))
364 if (
auto yieldOp = dyn_cast<cir::YieldOp>(block.getTerminator()))
365 switchYield = yieldOp;
367 assert(!op.getBody().empty());
368 mlir::Block *originalBlock = op->getBlock();
369 mlir::Block *swopBlock =
370 rewriter.splitBlock(originalBlock, op->getIterator());
371 rewriter.inlineRegionBefore(op.getBody(), exitBlock);
374 rewriteYieldOp(rewriter, switchYield, exitBlock);
376 rewriter.setInsertionPointToEnd(originalBlock);
377 cir::BrOp::create(rewriter, op.getLoc(), swopBlock);
382 llvm::SmallVector<mlir::APInt, 8> caseValues;
383 llvm::SmallVector<mlir::Block *, 8> caseDestinations;
384 llvm::SmallVector<mlir::ValueRange, 8> caseOperands;
386 llvm::SmallVector<std::pair<APInt, APInt>> rangeValues;
387 llvm::SmallVector<mlir::Block *> rangeDestinations;
388 llvm::SmallVector<mlir::ValueRange> rangeOperands;
391 mlir::Block *defaultDestination = exitBlock;
392 mlir::ValueRange defaultOperands = exitBlock->getArguments();
395 for (cir::CaseOp caseOp : cases) {
396 mlir::Region ®ion = caseOp.getCaseRegion();
399 switch (caseOp.getKind()) {
400 case cir::CaseOpKind::Default:
401 defaultDestination = ®ion.front();
402 defaultOperands = defaultDestination->getArguments();
404 case cir::CaseOpKind::Range:
405 assert(caseOp.getValue().size() == 2 &&
406 "Case range should have 2 case value");
407 rangeValues.push_back(
408 {cast<cir::IntAttr>(caseOp.getValue()[0]).getValue(),
409 cast<cir::IntAttr>(caseOp.getValue()[1]).getValue()});
410 rangeDestinations.push_back(®ion.front());
411 rangeOperands.push_back(rangeDestinations.back()->getArguments());
413 case cir::CaseOpKind::Anyof:
414 case cir::CaseOpKind::Equal:
416 for (
const mlir::Attribute &value : caseOp.getValue()) {
417 caseValues.push_back(cast<cir::IntAttr>(value).getValue());
418 caseDestinations.push_back(®ion.front());
419 caseOperands.push_back(caseDestinations.back()->getArguments());
425 for (mlir::Block &blk : region.getBlocks()) {
426 if (blk.getNumSuccessors())
429 if (
auto yieldOp = dyn_cast<cir::YieldOp>(blk.getTerminator())) {
430 mlir::Operation *nextOp = caseOp->getNextNode();
431 assert(nextOp &&
"caseOp is not expected to be the last op");
432 mlir::Block *oldBlock = nextOp->getBlock();
433 mlir::Block *newBlock =
434 rewriter.splitBlock(oldBlock, nextOp->getIterator());
435 rewriter.setInsertionPointToEnd(oldBlock);
436 cir::BrOp::create(rewriter, nextOp->getLoc(), mlir::ValueRange(),
438 rewriteYieldOp(rewriter, yieldOp, newBlock);
442 mlir::Block *oldBlock = caseOp->getBlock();
443 mlir::Block *newBlock =
444 rewriter.splitBlock(oldBlock, caseOp->getIterator());
446 mlir::Block &entryBlock = caseOp.getCaseRegion().front();
447 rewriter.inlineRegionBefore(caseOp.getCaseRegion(), newBlock);
450 rewriter.setInsertionPointToEnd(oldBlock);
451 cir::BrOp::create(rewriter, caseOp.getLoc(), &entryBlock);
455 for (cir::CaseOp caseOp : cases) {
456 mlir::Block *caseBlock = caseOp->getBlock();
459 if (caseBlock->hasNoPredecessors())
460 rewriter.eraseBlock(caseBlock);
462 rewriter.eraseOp(caseOp);
466 mlir::cast<cir::IntType>(op.getCondition().getType()).isSigned();
467 for (
auto [rangeVal, operand, destination] :
468 llvm::zip(rangeValues, rangeOperands, rangeDestinations)) {
469 APInt lowerBound = rangeVal.first;
470 APInt upperBound = rangeVal.second;
473 if (isSigned ? lowerBound.sgt(upperBound) : lowerBound.ugt(upperBound))
478 constexpr uint64_t kSmallRangeThreshold = 64;
479 APInt rangeSize = upperBound - lowerBound;
480 if (rangeSize.ult(kSmallRangeThreshold)) {
487 APInt caseValue = lowerBound;
488 for (uint64_t n = rangeSize.getZExtValue() + 1; n != 0; --n) {
489 caseValues.push_back(caseValue++);
490 caseOperands.push_back(operand);
491 caseDestinations.push_back(destination);
497 condBrToRangeDestination(op, rewriter, destination,
498 defaultDestination, lowerBound, upperBound);
499 defaultOperands = operand;
503 rewriter.setInsertionPoint(op);
504 rewriter.replaceOpWithNewOp<cir::SwitchFlatOp>(
505 op, op.getCondition(), defaultDestination, defaultOperands, caseValues,
506 caseDestinations, caseOperands);
508 return mlir::success();
512class CIRLoopOpInterfaceFlattening
513 :
public mlir::OpInterfaceRewritePattern<cir::LoopOpInterface> {
515 using mlir::OpInterfaceRewritePattern<
516 cir::LoopOpInterface>::OpInterfaceRewritePattern;
518 inline void lowerConditionOp(cir::ConditionOp op, mlir::Block *body,
520 mlir::PatternRewriter &rewriter,
521 cir::LoopHintAttr loopHints = {})
const {
522 mlir::OpBuilder::InsertionGuard guard(rewriter);
523 rewriter.setInsertionPoint(op);
524 rewriter.replaceOpWithNewOp<cir::BrCondOp>(
525 op, op.getCondition(), mlir::ValueRange{}, mlir::ValueRange{},
526 loopHints, body, exit);
563 rewriteLoopWithCleanup(cir::LoopOpInterface op,
564 mlir::PatternRewriter &rewriter)
const {
565 mlir::Location loc = op.getLoc();
566 mlir::Region *stepRegion = op.maybeGetStep();
568 cir::CleanupKindAttr cleanupKind = op.maybeGetCleanupKind();
569 assert(cleanupKind &&
"loop cleanup region without a cleanup kind");
571 mlir::Region &condRegion = op.getCond();
572 mlir::Region &bodyRegion = op.getBody();
573 mlir::Region &cleanupRegion = *op.maybeGetCleanup();
579 cast<cir::ConditionOp>(condRegion.back().getTerminator());
580 mlir::Value condVal = conditionOp.getCondition();
585 mlir::Block *bodyFront = &bodyRegion.front();
586 mlir::Block *stepFront = stepRegion ? &stepRegion->front() :
nullptr;
591 llvm::SmallVector<cir::YieldOp> bodyYieldsToStep;
592 llvm::SmallVector<cir::ContinueOp> continuesToStep;
594 for (mlir::Block &blk : bodyRegion.getBlocks())
595 if (
auto y = dyn_cast<cir::YieldOp>(blk.getTerminator()))
596 bodyYieldsToStep.push_back(y);
597 op.walkBodySkippingNestedLoops([&](mlir::Operation *o) {
598 if (
auto c = dyn_cast<cir::ContinueOp>(o)) {
599 continuesToStep.push_back(c);
600 return mlir::WalkResult::skip();
602 return mlir::WalkResult::advance();
608 rewriter.inlineRegionBefore(bodyRegion, condRegion, condRegion.end());
610 rewriter.inlineRegionBefore(*stepRegion, condRegion, condRegion.end());
614 mlir::Block *breakBlock =
615 rewriter.createBlock(&condRegion, condRegion.end());
616 rewriter.setInsertionPointToEnd(breakBlock);
617 cir::BreakOp::create(rewriter, conditionOp.getLoc());
619 rewriter.setInsertionPoint(conditionOp);
620 rewriter.replaceOpWithNewOp<cir::BrCondOp>(conditionOp, condVal, bodyFront,
625 for (cir::YieldOp y : bodyYieldsToStep)
626 lowerTerminator(y, stepFront, rewriter);
627 for (cir::ContinueOp c : continuesToStep)
628 lowerTerminator(c, stepFront, rewriter);
632 mlir::Block *newBodyBlock = rewriter.createBlock(&bodyRegion);
633 rewriter.setInsertionPointToEnd(newBodyBlock);
634 auto emitYield = [](mlir::OpBuilder &b, mlir::Location l) {
635 cir::YieldOp::create(b, l);
637 auto scope = cir::CleanupScopeOp::create(
638 rewriter, loc, cleanupKind.getValue(), emitYield, emitYield);
639 cir::YieldOp::create(rewriter, loc);
644 mlir::Block *bodyPlaceholder = &scope.getBodyRegion().front();
645 rewriter.inlineRegionBefore(condRegion, bodyPlaceholder);
646 rewriter.eraseBlock(bodyPlaceholder);
648 mlir::Block *cleanupPlaceholder = &scope.getCleanupRegion().front();
649 rewriter.inlineRegionBefore(cleanupRegion, cleanupPlaceholder);
650 rewriter.eraseBlock(cleanupPlaceholder);
653 mlir::Block *newCondBlock = rewriter.createBlock(&condRegion);
654 rewriter.setInsertionPointToEnd(newCondBlock);
655 mlir::Value trueVal = cir::ConstantOp::create(
656 rewriter, loc, cir::BoolAttr::get(rewriter.getContext(),
true));
657 cir::ConditionOp::create(rewriter, loc, trueVal);
661 mlir::Block *newStepBlock = rewriter.createBlock(stepRegion);
662 rewriter.setInsertionPointToEnd(newStepBlock);
663 cir::YieldOp::create(rewriter, loc);
669 if (
auto whileOp = mlir::dyn_cast<cir::WhileOp>(op.getOperation()))
670 whileOp.removeCleanupKindAttr();
671 else if (
auto forOp = mlir::dyn_cast<cir::ForOp>(op.getOperation()))
672 forOp.removeCleanupKindAttr();
674 return mlir::success();
678 matchAndRewrite(cir::LoopOpInterface op,
679 mlir::PatternRewriter &rewriter)
const final {
684 for (mlir::Region ®ion : op->getRegions())
685 if (hasNestedOpsToFlatten(region))
686 return mlir::failure();
696 if (op.maybeGetCleanup())
697 return rewriteLoopWithCleanup(op, rewriter);
700 mlir::Block *entry = rewriter.getInsertionBlock();
702 rewriter.splitBlock(entry, rewriter.getInsertionPoint());
703 mlir::Block *cond = &op.getCond().front();
704 mlir::Block *body = &op.getBody().front();
706 (op.maybeGetStep() ? &op.maybeGetStep()->front() :
nullptr);
709 mlir::Block *firstBlock = &op.getEntry().front();
710 rewriter.setInsertionPointToEnd(entry);
711 cir::BrOp::create(rewriter, op.getLoc(), firstBlock);
716 cir::LoopHintAttr loopHints = op.getLoopHintsAttr();
717 auto hintsFor = [&](mlir::Block *target) {
718 return target == firstBlock ? loopHints : cir::LoopHintAttr{};
726 cast<cir::ConditionOp>(op.getCond().back().getTerminator());
727 lowerConditionOp(conditionOp, body, exit, rewriter, hintsFor(body));
734 mlir::Block *dest = (
step ?
step : cond);
735 op.walkBodySkippingNestedLoops([&](mlir::Operation *op) {
736 if (!isa<cir::ContinueOp>(op))
737 return mlir::WalkResult::advance();
739 lowerTerminator(op, dest, rewriter, hintsFor(dest));
740 return mlir::WalkResult::skip();
744 walkRegionSkipping<cir::LoopOpInterface, cir::SwitchOp>(
745 op.getBody(), [&](mlir::Operation *op) {
746 if (!isa<cir::BreakOp>(op))
747 return mlir::WalkResult::advance();
749 lowerTerminator(op, exit, rewriter);
750 return mlir::WalkResult::skip();
754 for (mlir::Block &blk : op.getBody().getBlocks()) {
755 auto bodyYield = dyn_cast<cir::YieldOp>(blk.getTerminator());
757 lowerTerminator(bodyYield, dest, rewriter, hintsFor(dest));
765 cast<cir::YieldOp>(op.maybeGetStep()->back().getTerminator()), cond,
766 rewriter, hintsFor(cond));
769 rewriter.inlineRegionBefore(op.getCond(), exit);
770 rewriter.inlineRegionBefore(op.getBody(), exit);
772 rewriter.inlineRegionBefore(*op.maybeGetStep(), exit);
774 rewriter.eraseOp(op);
775 return mlir::success();
779class CIRTernaryOpFlattening :
public mlir::OpRewritePattern<cir::TernaryOp> {
781 using OpRewritePattern<cir::TernaryOp>::OpRewritePattern;
784 matchAndRewrite(cir::TernaryOp op,
785 mlir::PatternRewriter &rewriter)
const override {
786 Location loc = op->getLoc();
787 Block *condBlock = rewriter.getInsertionBlock();
788 Block::iterator opPosition = rewriter.getInsertionPoint();
789 Block *remainingOpsBlock = rewriter.splitBlock(condBlock, opPosition);
790 llvm::SmallVector<mlir::Location, 2> locs;
793 if (op->getResultTypes().size())
795 Block *continueBlock =
796 rewriter.createBlock(remainingOpsBlock, op->getResultTypes(), locs);
797 cir::BrOp::create(rewriter, loc, remainingOpsBlock);
799 Region &trueRegion = op.getTrueRegion();
800 Block *trueBlock = &trueRegion.front();
805 if (failed(rewriteRegionExitToContinue(rewriter, trueRegion, continueBlock,
807 return mlir::success();
808 rewriter.inlineRegionBefore(trueRegion, continueBlock);
810 Block *falseBlock = continueBlock;
811 Region &falseRegion = op.getFalseRegion();
813 falseBlock = &falseRegion.front();
814 if (failed(rewriteRegionExitToContinue(rewriter, falseRegion, continueBlock,
816 return mlir::success();
817 rewriter.inlineRegionBefore(falseRegion, continueBlock);
819 rewriter.setInsertionPointToEnd(condBlock);
820 cir::BrCondOp::create(rewriter, loc, op.getCond(), trueBlock, falseBlock);
822 rewriter.replaceOp(op, continueBlock->getArguments());
825 return mlir::success();
832static cir::AllocaOp getOrCreateCleanupDestSlot(cir::FuncOp funcOp,
833 mlir::PatternRewriter &rewriter,
834 mlir::Location loc) {
835 mlir::Block &entryBlock = funcOp.getBody().front();
838 auto it = llvm::find_if(entryBlock, [](
auto &op) {
839 return mlir::isa<AllocaOp>(&op) &&
840 mlir::cast<AllocaOp>(&op).getCleanupDestSlot();
842 if (it != entryBlock.end())
843 return mlir::cast<cir::AllocaOp>(*it);
846 mlir::OpBuilder::InsertionGuard guard(rewriter);
847 rewriter.setInsertionPointToStart(&entryBlock);
848 cir::IntType s32Type =
849 cir::IntType::get(rewriter.getContext(), 32,
true);
851 cir::PointerType ptrToS32Type = cir::PointerType::get(
852 s32Type, dataLayout.getAllocaAddrSpace(rewriter.getContext()));
853 uint64_t alignment = dataLayout.getAlignment(s32Type,
true).value();
854 auto allocaOp = cir::AllocaOp::create(
855 rewriter, loc, ptrToS32Type,
"__cleanup_dest_slot",
856 rewriter.getI64IntegerAttr(alignment));
857 allocaOp.setCleanupDestSlot(
true);
867static bool isLifetimeMarkerOnly(mlir::Region ®ion) {
868 return llvm::hasSingleElement(region) &&
871 [](mlir::Operation &op) { return isa<cir::LifetimeEndOp>(op); }) &&
872 llvm::all_of(region.front(), [](mlir::Operation &op) {
873 return isa<cir::LifetimeEndOp, cir::YieldOp>(op);
880static bool hasEnclosingEHRequirement(mlir::Operation *op) {
881 for (mlir::Region *region = op->getParentRegion(); region;
882 region = region->getParentRegion()) {
883 mlir::Operation *parent = region->getParentOp();
884 if (!parent || isa<cir::FuncOp>(parent))
886 if (
auto tryOp = dyn_cast<cir::TryOp>(parent)) {
887 auto handlers = tryOp.getHandlerTypesAttr();
888 if (region == &tryOp.getTryRegion() && handlers &&
889 llvm::any_of(handlers, [](mlir::Attribute handler) {
890 return !isa<cir::UnwindAttr>(handler);
893 }
else if (
auto cleanupOp = dyn_cast<cir::CleanupScopeOp>(parent)) {
894 if (region == &cleanupOp.getBodyRegion() &&
895 cleanupOp.getCleanupKindAttr().isEH() &&
896 !isLifetimeMarkerOnly(cleanupOp.getCleanupRegion()))
898 }
else if (
auto loopOp = dyn_cast<cir::LoopOpInterface>(parent)) {
900 mlir::Region *
cleanup = loopOp.maybeGetCleanup();
901 if (cleanup && region != cleanup && loopOp.maybeGetCleanupKind().isEH() &&
902 !isLifetimeMarkerOnly(*cleanup))
914collectThrowingCalls(mlir::Region ®ion,
916 region.walk([&](cir::CallOp callOp) {
920 if (!callOp.getNothrow() && !callOp.getMusttail())
921 callsToRewrite.push_back(callOp);
931collectThrows(mlir::Region ®ion,
934 [&](cir::ThrowOp throwOp) { throwsToRewrite.push_back(throwOp); });
943static void collectResumeOps(mlir::Region ®ion,
945 region.walk([&](cir::ResumeOp resumeOp) { resumeOps.push_back(resumeOp); });
951static mlir::Block *buildUnwindBlock(mlir::Block *dest,
bool isCleanupOnly,
953 mlir::Block *insertBefore,
954 mlir::PatternRewriter &rewriter) {
955 mlir::Block *unwindBlock = rewriter.createBlock(insertBefore);
956 rewriter.setInsertionPointToEnd(unwindBlock);
958 cir::EhInitiateOp::create(rewriter, loc, isCleanupOnly);
959 cir::BrOp::create(rewriter, loc, mlir::ValueRange{ehInitiate.getEhToken()},
967static mlir::Block *buildTerminateUnwindBlock(mlir::Location loc,
968 mlir::Block *insertBefore,
969 mlir::PatternRewriter &rewriter) {
970 mlir::Block *terminateBlock = rewriter.createBlock(insertBefore);
971 rewriter.setInsertionPointToEnd(terminateBlock);
972 auto ehInitiate = cir::EhInitiateOp::create(rewriter, loc,
false);
973 cir::EhTerminateOp::create(rewriter, loc, ehInitiate.getEhToken());
974 return terminateBlock;
977class CIRCleanupScopeOpFlattening
978 :
public mlir::OpRewritePattern<cir::CleanupScopeOp> {
980 using OpRewritePattern<cir::CleanupScopeOp>::OpRewritePattern;
985 mlir::Operation *exitOp;
991 CleanupExit(mlir::Operation *op,
int id) : exitOp(op), destinationId(id) {}
1001 static bool gotoTargetsLabelInRegion(cir::GotoOp gotoOp,
1002 mlir::Region ®ion) {
1003 llvm::StringRef targetLabel = gotoOp.getLabel();
1005 .walk([&](cir::LabelOp labelOp) {
1006 if (labelOp.getLabel() == targetLabel)
1007 return mlir::WalkResult::interrupt();
1008 return mlir::WalkResult::advance();
1036 void collectExits(mlir::Region &cleanupBodyRegion,
1037 llvm::SmallVectorImpl<CleanupExit> &exits,
1038 int &nextId)
const {
1042 for (mlir::Block &block : cleanupBodyRegion) {
1043 auto *terminator = block.getTerminator();
1044 if (isa<cir::YieldOp>(terminator))
1045 exits.emplace_back(terminator, nextId++);
1052 auto isGotoThatExitsCleanup = [&](mlir::Operation *op) {
1053 auto gotoOp = dyn_cast<cir::GotoOp>(op);
1054 return gotoOp && !gotoTargetsLabelInRegion(gotoOp, cleanupBodyRegion);
1063 auto isReturnThatExitsCleanup = [](mlir::Operation *op) {
1064 if (!isa<cir::ReturnOp>(op))
1066 auto callOp = dyn_cast_if_present<cir::CallOp>(op->getPrevNode());
1067 return !callOp || !callOp.getMusttail();
1074 auto collectExitsInLoop = [&](mlir::Operation *loopOp) {
1075 loopOp->walk<mlir::WalkOrder::PreOrder>([&](mlir::Operation *nestedOp) {
1076 if (isReturnThatExitsCleanup(nestedOp)) {
1077 exits.emplace_back(nestedOp, nextId++);
1078 }
else if (isGotoThatExitsCleanup(nestedOp)) {
1079 exits.emplace_back(nestedOp, nextId++);
1081 return mlir::WalkResult::advance();
1086 std::function<void(mlir::Region &,
bool)> collectExitsInCleanup;
1087 std::function<void(mlir::Operation *)> collectExitsInSwitch;
1091 collectExitsInSwitch = [&](mlir::Operation *switchOp) {
1092 switchOp->walk<mlir::WalkOrder::PreOrder>([&](mlir::Operation *nestedOp) {
1093 if (isa<cir::CleanupScopeOp>(nestedOp)) {
1096 collectExitsInCleanup(
1097 cast<cir::CleanupScopeOp>(nestedOp).getBodyRegion(),
1099 return mlir::WalkResult::skip();
1100 }
else if (isa<cir::LoopOpInterface>(nestedOp)) {
1101 collectExitsInLoop(nestedOp);
1102 return mlir::WalkResult::skip();
1103 }
else if (isa<cir::ContinueOp>(nestedOp) ||
1104 isReturnThatExitsCleanup(nestedOp)) {
1105 exits.emplace_back(nestedOp, nextId++);
1106 }
else if (isGotoThatExitsCleanup(nestedOp)) {
1107 exits.emplace_back(nestedOp, nextId++);
1109 return mlir::WalkResult::advance();
1116 collectExitsInCleanup = [&](mlir::Region ®ion,
bool ignoreBreak) {
1117 region.walk<mlir::WalkOrder::PreOrder>([&](mlir::Operation *op) {
1124 if (!ignoreBreak && isa<cir::BreakOp>(op)) {
1125 exits.emplace_back(op, nextId++);
1126 }
else if (isa<cir::ContinueOp>(op) || isReturnThatExitsCleanup(op)) {
1127 exits.emplace_back(op, nextId++);
1128 }
else if (isGotoThatExitsCleanup(op)) {
1129 exits.emplace_back(op, nextId++);
1130 }
else if (isa<cir::CleanupScopeOp>(op)) {
1132 collectExitsInCleanup(cast<cir::CleanupScopeOp>(op).getBodyRegion(),
1134 return mlir::WalkResult::skip();
1135 }
else if (isa<cir::LoopOpInterface>(op)) {
1139 collectExitsInLoop(op);
1140 return mlir::WalkResult::skip();
1141 }
else if (isa<cir::SwitchOp>(op)) {
1145 collectExitsInSwitch(op);
1146 return mlir::WalkResult::skip();
1148 return mlir::WalkResult::advance();
1153 collectExitsInCleanup(cleanupBodyRegion,
false);
1159 static bool shouldSinkReturnOperand(mlir::Value operand,
1160 cir::ReturnOp returnOp) {
1162 mlir::Operation *defOp = operand.getDefiningOp();
1168 if (!mlir::isa<cir::ConstantOp, cir::LoadOp>(defOp))
1172 if (!operand.hasOneUse())
1176 if (defOp->getBlock() != returnOp->getBlock())
1179 if (
auto loadOp = mlir::dyn_cast<cir::LoadOp>(defOp)) {
1181 mlir::Value ptr = loadOp.getAddr();
1182 auto funcOp = returnOp->getParentOfType<cir::FuncOp>();
1183 assert(funcOp &&
"Return op has no function parent?");
1184 mlir::Block &funcEntryBlock = funcOp.getBody().front();
1188 mlir::dyn_cast_if_present<cir::AllocaOp>(ptr.getDefiningOp()))
1189 return allocaOp->getBlock() == &funcEntryBlock;
1195 assert(mlir::isa<cir::ConstantOp>(defOp) &&
"Expected constant op");
1204 getReturnOpOperands(cir::ReturnOp returnOp, mlir::Operation *exitOp,
1205 mlir::Location loc, mlir::PatternRewriter &rewriter,
1206 llvm::SmallVectorImpl<mlir::Value> &returnValues)
const {
1207 mlir::Block *destBlock = rewriter.getInsertionBlock();
1208 auto funcOp = exitOp->getParentOfType<cir::FuncOp>();
1209 assert(funcOp &&
"Return op has no function parent?");
1210 mlir::Block &funcEntryBlock = funcOp.getBody().front();
1212 for (mlir::Value operand : returnOp.getOperands()) {
1213 if (shouldSinkReturnOperand(operand, returnOp)) {
1215 mlir::Operation *defOp = operand.getDefiningOp();
1216 rewriter.moveOpBefore(defOp, destBlock, destBlock->end());
1217 returnValues.push_back(operand);
1220 cir::AllocaOp alloca;
1222 mlir::OpBuilder::InsertionGuard guard(rewriter);
1223 rewriter.setInsertionPointToStart(&funcEntryBlock);
1224 cir::CIRDataLayout dataLayout(
1225 funcOp->getParentOfType<mlir::ModuleOp>());
1227 dataLayout.getAlignment(operand.getType(),
true).value();
1228 cir::PointerType ptrType = cir::PointerType::get(operand.getType());
1230 cir::AllocaOp::create(rewriter, loc, ptrType,
"__ret_operand_tmp",
1231 rewriter.getI64IntegerAttr(alignment));
1236 mlir::OpBuilder::InsertionGuard guard(rewriter);
1237 rewriter.setInsertionPoint(exitOp);
1238 cir::StoreOp::create(rewriter, loc, operand, alloca,
1241 mlir::IntegerAttr(),
1242 cir::SyncScopeKindAttr(), cir::MemOrderAttr());
1246 rewriter.setInsertionPointToEnd(destBlock);
1248 cir::LoadOp::create(rewriter, loc, alloca,
false,
1250 mlir::IntegerAttr(),
1251 cir::SyncScopeKindAttr(), cir::MemOrderAttr(),
1253 returnValues.push_back(loaded);
1263 createExitTerminator(mlir::Operation *exitOp, mlir::Location loc,
1264 mlir::Block *continueBlock,
1265 mlir::PatternRewriter &rewriter)
const {
1266 return llvm::TypeSwitch<mlir::Operation *, mlir::LogicalResult>(exitOp)
1267 .Case<cir::YieldOp>([&](
auto) {
1269 cir::BrOp::create(rewriter, loc, continueBlock);
1270 return mlir::success();
1272 .Case<cir::BreakOp>([&](
auto) {
1274 cir::BreakOp::create(rewriter, loc);
1275 return mlir::success();
1277 .Case<cir::ContinueOp>([&](
auto) {
1279 cir::ContinueOp::create(rewriter, loc);
1280 return mlir::success();
1282 .Case<cir::ReturnOp>([&](
auto returnOp) {
1286 if (returnOp.hasOperand()) {
1287 llvm::SmallVector<mlir::Value, 2> returnValues;
1288 getReturnOpOperands(returnOp, exitOp, loc, rewriter, returnValues);
1289 cir::ReturnOp::create(rewriter, loc, returnValues);
1291 cir::ReturnOp::create(rewriter, loc);
1293 return mlir::success();
1295 .Case<cir::GotoOp>([&](
auto gotoOp) {
1300 cir::GotoOp::create(rewriter, loc, gotoOp.getLabel());
1301 return mlir::success();
1303 .
Default([&](mlir::Operation *op) {
1304 cir::UnreachableOp::create(rewriter, loc);
1305 return op->emitError(
1306 "unexpected exit operation in cleanup scope body");
1312 static bool regionExitsOnlyFromLastBlock(mlir::Region ®ion) {
1313 for (mlir::Block &block : region) {
1314 if (&block == ®ion.back())
1316 bool expectedTerminator =
1317 llvm::TypeSwitch<mlir::Operation *, bool>(block.getTerminator())
1324 .Case<cir::YieldOp, cir::ReturnOp, cir::ResumeFlatOp,
1325 cir::ContinueOp, cir::BreakOp, cir::GotoOp>(
1326 [](
auto) {
return false; })
1335 .Case<cir::TryCallOp>([](
auto) {
return false; })
1339 .Case<cir::EhDispatchOp>([](
auto) {
return false; })
1343 .Case<cir::SwitchFlatOp>([](
auto) {
return false; })
1346 .Case<cir::UnreachableOp, cir::TrapOp>([](
auto) {
return true; })
1348 .Case<cir::IndirectBrOp>([](
auto) {
return false; })
1351 .Case<cir::BrOp>([&](cir::BrOp brOp) {
1352 assert(brOp.getDest()->getParent() == ®ion &&
1353 "branch destination is not in the region");
1356 .Case<cir::BrCondOp>([&](cir::BrCondOp brCondOp) {
1357 assert(brCondOp.getDestTrue()->getParent() == ®ion &&
1358 "branch destination is not in the region");
1359 assert(brCondOp.getDestFalse()->getParent() == ®ion &&
1360 "branch destination is not in the region");
1364 .
Default([](mlir::Operation *) ->
bool {
1365 llvm_unreachable(
"unexpected terminator in cleanup region");
1367 if (!expectedTerminator)
1395 mlir::Block *buildEHCleanupBlocks(cir::CleanupScopeOp cleanupOp,
1397 mlir::Block *insertBefore,
1398 mlir::PatternRewriter &rewriter)
const {
1399 assert(regionExitsOnlyFromLastBlock(cleanupOp.getCleanupRegion()) &&
1400 "cleanup region has exits in non-final blocks");
1404 mlir::Block *blockBeforeClone =
insertBefore->getPrevNode();
1407 rewriter.cloneRegionBefore(cleanupOp.getCleanupRegion(), insertBefore);
1410 mlir::Block *clonedEntry = blockBeforeClone
1411 ? blockBeforeClone->getNextNode()
1416 auto ehTokenType = cir::EhTokenType::get(rewriter.getContext());
1417 mlir::Value ehToken = clonedEntry->addArgument(ehTokenType, loc);
1419 rewriter.setInsertionPointToStart(clonedEntry);
1420 auto beginCleanup = cir::BeginCleanupOp::create(rewriter, loc, ehToken);
1424 mlir::Block *lastClonedBlock =
insertBefore->getPrevNode();
1426 mlir::dyn_cast<cir::YieldOp>(lastClonedBlock->getTerminator());
1428 rewriter.setInsertionPoint(yieldOp);
1429 cir::EndCleanupOp::create(rewriter, loc, beginCleanup.getCleanupToken());
1430 rewriter.replaceOpWithNewOp<cir::ResumeOp>(yieldOp, ehToken);
1432 cleanupOp->emitError(
"Not yet implemented: cleanup region terminated "
1433 "with non-yield operation");
1462 flattenCleanup(cir::CleanupScopeOp cleanupOp,
1463 llvm::SmallVectorImpl<CleanupExit> &exits,
1464 llvm::SmallVectorImpl<cir::CallOp> &callsToRewrite,
1465 llvm::SmallVectorImpl<cir::ThrowOp> &throwsToRewrite,
1466 llvm::SmallVectorImpl<cir::ResumeOp> &resumeOpsToChain,
1467 mlir::PatternRewriter &rewriter)
const {
1468 mlir::Location loc = cleanupOp.getLoc();
1469 cir::CleanupKindAttr cleanupKind = cleanupOp.getCleanupKindAttr();
1470 bool hasNormalCleanup = cleanupKind.isNormal();
1471 bool hasEHCleanup = cleanupKind.isEH();
1472 bool isMultiExit = exits.size() > 1;
1479 bool emitNormalCleanup = hasNormalCleanup && !exits.empty();
1482 mlir::Block *bodyEntry = &cleanupOp.getBodyRegion().front();
1483 mlir::Block *cleanupEntry = &cleanupOp.getCleanupRegion().front();
1484 mlir::Block *cleanupExit = &cleanupOp.getCleanupRegion().back();
1485 assert(regionExitsOnlyFromLastBlock(cleanupOp.getCleanupRegion()) &&
1486 "cleanup region has exits in non-final blocks");
1487 auto cleanupYield = dyn_cast<cir::YieldOp>(cleanupExit->getTerminator());
1488 if (!cleanupYield) {
1489 return rewriter.notifyMatchFailure(cleanupOp,
1490 "Not yet implemented: cleanup region "
1491 "terminated with non-yield operation");
1498 cir::AllocaOp destSlot;
1499 if (isMultiExit && emitNormalCleanup) {
1500 auto funcOp = cleanupOp->getParentOfType<cir::FuncOp>();
1502 return cleanupOp->emitError(
"cleanup scope not inside a function");
1503 destSlot = getOrCreateCleanupDestSlot(funcOp, rewriter, loc);
1507 mlir::Block *currentBlock = rewriter.getInsertionBlock();
1508 mlir::Block *continueBlock =
1509 rewriter.splitBlock(currentBlock, rewriter.getInsertionPoint());
1519 mlir::Block *unwindBlock =
nullptr;
1520 mlir::Block *ehCleanupEntry =
nullptr;
1521 if (hasEHCleanup && (!callsToRewrite.empty() || !throwsToRewrite.empty() ||
1522 !resumeOpsToChain.empty())) {
1524 buildEHCleanupBlocks(cleanupOp, loc, continueBlock, rewriter);
1528 if (!callsToRewrite.empty() || !throwsToRewrite.empty())
1529 unwindBlock = buildUnwindBlock(ehCleanupEntry,
true,
1530 loc, ehCleanupEntry, rewriter);
1537 mlir::Block *normalInsertPt =
1538 unwindBlock ? unwindBlock
1539 : (ehCleanupEntry ? ehCleanupEntry : continueBlock);
1542 rewriter.inlineRegionBefore(cleanupOp.getBodyRegion(), normalInsertPt);
1545 if (emitNormalCleanup)
1546 rewriter.inlineRegionBefore(cleanupOp.getCleanupRegion(), normalInsertPt);
1549 rewriter.setInsertionPointToEnd(currentBlock);
1550 cir::BrOp::create(rewriter, loc, bodyEntry);
1553 mlir::LogicalResult result = mlir::success();
1554 if (emitNormalCleanup) {
1556 mlir::Block *exitBlock = rewriter.createBlock(normalInsertPt);
1559 rewriter.setInsertionPoint(cleanupYield);
1560 rewriter.replaceOpWithNewOp<cir::BrOp>(cleanupYield, exitBlock);
1564 rewriter.setInsertionPointToEnd(exitBlock);
1568 cir::LoadOp::create(rewriter, loc, destSlot,
false,
1570 mlir::IntegerAttr(),
1571 cir::SyncScopeKindAttr(), cir::MemOrderAttr(),
1575 llvm::SmallVector<mlir::APInt, 8> caseValues;
1576 llvm::SmallVector<mlir::Block *, 8> caseDestinations;
1577 llvm::SmallVector<mlir::ValueRange, 8> caseOperands;
1578 cir::IntType s32Type =
1579 cir::IntType::get(rewriter.getContext(), 32,
true);
1581 for (
const CleanupExit &exit : exits) {
1583 mlir::Block *destBlock = rewriter.createBlock(normalInsertPt);
1584 rewriter.setInsertionPointToEnd(destBlock);
1586 createExitTerminator(exit.exitOp, loc, continueBlock, rewriter);
1589 caseValues.push_back(
1590 llvm::APInt(32,
static_cast<uint64_t>(exit.destinationId),
true));
1591 caseDestinations.push_back(destBlock);
1592 caseOperands.push_back(mlir::ValueRange());
1596 rewriter.setInsertionPoint(exit.exitOp);
1597 auto destIdConst = cir::ConstantOp::create(
1598 rewriter, loc, cir::IntAttr::get(s32Type, exit.destinationId));
1599 cir::StoreOp::create(rewriter, loc, destIdConst, destSlot,
1602 mlir::IntegerAttr(),
1603 cir::SyncScopeKindAttr(), cir::MemOrderAttr());
1604 rewriter.replaceOpWithNewOp<cir::BrOp>(exit.exitOp, cleanupEntry);
1612 if (result.failed())
1617 mlir::Block *defaultBlock = rewriter.createBlock(normalInsertPt);
1618 rewriter.setInsertionPointToEnd(defaultBlock);
1619 cir::UnreachableOp::create(rewriter, loc);
1622 rewriter.setInsertionPointToEnd(exitBlock);
1623 cir::SwitchFlatOp::create(rewriter, loc, slotValue, defaultBlock,
1624 mlir::ValueRange(), caseValues,
1625 caseDestinations, caseOperands);
1629 rewriter.setInsertionPointToEnd(exitBlock);
1630 mlir::Operation *exitOp = exits[0].exitOp;
1631 result = createExitTerminator(exitOp, loc, continueBlock, rewriter);
1634 rewriter.setInsertionPoint(exitOp);
1635 rewriter.replaceOpWithNewOp<cir::BrOp>(exitOp, cleanupEntry);
1640 for (CleanupExit &exit : exits) {
1641 if (isa<cir::YieldOp>(exit.exitOp)) {
1642 rewriter.setInsertionPoint(exit.exitOp);
1643 rewriter.replaceOpWithNewOp<cir::BrOp>(exit.exitOp, continueBlock);
1654 for (cir::CallOp callOp : callsToRewrite)
1656 for (cir::ThrowOp throwOp : throwsToRewrite)
1666 if (ehCleanupEntry) {
1667 llvm::SmallVector<cir::CallOp> ehCleanupThrowingCalls;
1668 llvm::SmallVector<cir::ThrowOp> ehCleanupThrows;
1669 for (mlir::Block *block = ehCleanupEntry; block != continueBlock;
1670 block = block->getNextNode()) {
1671 block->walk([&](mlir::Operation *op) {
1672 if (
auto callOp = mlir::dyn_cast<cir::CallOp>(op)) {
1673 if (!callOp.getNothrow())
1674 ehCleanupThrowingCalls.push_back(callOp);
1675 }
else if (
auto throwOp = mlir::dyn_cast<cir::ThrowOp>(op)) {
1676 ehCleanupThrows.push_back(throwOp);
1680 if (!ehCleanupThrowingCalls.empty() || !ehCleanupThrows.empty()) {
1681 mlir::Block *terminateBlock =
1682 buildTerminateUnwindBlock(loc, continueBlock, rewriter);
1683 for (cir::CallOp callOp : ehCleanupThrowingCalls)
1685 for (cir::ThrowOp throwOp : ehCleanupThrows)
1695 if (ehCleanupEntry) {
1696 for (cir::ResumeOp resumeOp : resumeOpsToChain) {
1697 mlir::Value ehToken = resumeOp.getEhToken();
1698 rewriter.setInsertionPoint(resumeOp);
1699 rewriter.replaceOpWithNewOp<cir::BrOp>(
1700 resumeOp, mlir::ValueRange{ehToken},
nullptr,
1706 rewriter.eraseOp(cleanupOp);
1711 return mlir::success();
1715 matchAndRewrite(cir::CleanupScopeOp cleanupOp,
1716 mlir::PatternRewriter &rewriter)
const override {
1717 mlir::OpBuilder::InsertionGuard guard(rewriter);
1731 llvm::SmallVector<cir::CleanupScopeOp> deadNestedOps;
1732 cleanupOp.getBodyRegion().walk([&](cir::CleanupScopeOp nested) {
1733 if (mlir::isOpTriviallyDead(nested))
1734 deadNestedOps.push_back(nested);
1736 for (
auto op : deadNestedOps)
1737 rewriter.eraseOp(op);
1739 if (hasNestedOpsToFlatten(cleanupOp.getBodyRegion()))
1740 return mlir::failure();
1742 bool hasEHCleanup = cleanupOp.getCleanupKindAttr().isEH();
1745 llvm::SmallVector<CleanupExit> exits;
1747 collectExits(cleanupOp.getBodyRegion(), exits, nextId);
1752 bool hasMustTailCall =
false;
1753 cleanupOp.getBodyRegion().walk(
1754 [&](cir::CallOp callOp) { hasMustTailCall |= callOp.getMusttail(); });
1755 assert((!exits.empty() || hasMustTailCall) &&
1756 "cleanup scope body has no exit");
1762 llvm::SmallVector<cir::CallOp> callsToRewrite;
1763 llvm::SmallVector<cir::ThrowOp> throwsToRewrite;
1764 if (hasEHCleanup && (!isLifetimeMarkerOnly(cleanupOp.getCleanupRegion()) ||
1765 hasEnclosingEHRequirement(cleanupOp))) {
1766 collectThrowingCalls(cleanupOp.getBodyRegion(), callsToRewrite);
1767 collectThrows(cleanupOp.getBodyRegion(), throwsToRewrite);
1773 llvm::SmallVector<cir::ResumeOp> resumeOpsToChain;
1775 collectResumeOps(cleanupOp.getBodyRegion(), resumeOpsToChain);
1777 return flattenCleanup(cleanupOp, exits, callsToRewrite, throwsToRewrite,
1778 resumeOpsToChain, rewriter);
1785static cir::EhInitiateOp traceToEhInitiate(mlir::Value ehToken) {
1787 if (
auto initiate = ehToken.getDefiningOp<cir::EhInitiateOp>())
1789 auto blockArg = mlir::dyn_cast<mlir::BlockArgument>(ehToken);
1792 mlir::Block *pred = blockArg.getOwner()->getSinglePredecessor();
1795 auto brOp = mlir::dyn_cast<cir::BrOp>(pred->getTerminator());
1798 ehToken = brOp.getDestOperands()[blockArg.getArgNumber()];
1803class CIRTryOpFlattening :
public mlir::OpRewritePattern<cir::TryOp> {
1805 using OpRewritePattern<cir::TryOp>::OpRewritePattern;
1810 mlir::Block *buildCatchDispatchBlock(
1811 cir::TryOp tryOp, mlir::ArrayAttr handlerTypes,
1812 llvm::SmallVectorImpl<mlir::Block *> &catchHandlerBlocks,
1813 mlir::Location loc, mlir::Block *insertBefore,
1814 mlir::PatternRewriter &rewriter)
const {
1815 mlir::Block *dispatchBlock = rewriter.createBlock(insertBefore);
1816 auto ehTokenType = cir::EhTokenType::get(rewriter.getContext());
1817 mlir::Value ehToken = dispatchBlock->addArgument(ehTokenType, loc);
1819 rewriter.setInsertionPointToEnd(dispatchBlock);
1822 llvm::SmallVector<mlir::Attribute> catchTypeAttrs;
1823 llvm::SmallVector<mlir::Block *> catchDests;
1824 mlir::Block *defaultDest =
nullptr;
1825 bool defaultIsCatchAll =
false;
1831 cir::EhFilterAttr filterAttr;
1832 mlir::Block *filterUnwindDest =
nullptr;
1833 mlir::Block *filterClauseDest =
nullptr;
1835 for (
auto zipped : llvm::zip(handlerTypes, catchHandlerBlocks)) {
1836 mlir::Attribute typeAttr = std::get<0>(zipped);
1837 mlir::Block *handlerBlock = std::get<1>(zipped);
1838 llvm::TypeSwitch<mlir::Attribute>(typeAttr)
1839 .Case<cir::CatchAllAttr>([&](
auto) {
1840 assert(!defaultDest &&
"multiple catch_all or unwind handlers");
1841 defaultDest = handlerBlock;
1842 defaultIsCatchAll =
true;
1844 .Case<cir::UnwindAttr>([&](
auto) {
1845 assert(!defaultDest &&
"multiple catch_all or unwind handlers");
1846 defaultDest = handlerBlock;
1847 defaultIsCatchAll =
false;
1849 .Case<cir::EhFilterAttr>([&](cir::EhFilterAttr ehFilter) {
1850 assert(!filterAttr &&
"multiple filter handlers");
1851 filterAttr = ehFilter;
1852 filterUnwindDest = handlerBlock;
1854 .Case<cir::EhUnexpectedAttr>([&](
auto) {
1855 assert(!filterClauseDest &&
"multiple unexpected handlers");
1856 filterClauseDest = handlerBlock;
1858 .
Default([&](mlir::Attribute attr) {
1860 catchTypeAttrs.push_back(attr);
1861 catchDests.push_back(handlerBlock);
1866 assert(filterUnwindDest && filterClauseDest &&
1867 "filter handler requires an unexpected handler");
1868 assert(!defaultDest &&
1869 "filter handler cannot share a dispatch with catch_all or unwind");
1870 catchTypeAttrs.push_back(filterAttr);
1871 catchDests.push_back(filterClauseDest);
1872 defaultDest = filterUnwindDest;
1873 defaultIsCatchAll =
false;
1876 assert(defaultDest &&
"dispatch must have a catch_all or unwind handler");
1878 mlir::ArrayAttr catchTypesArrayAttr;
1879 if (!catchTypeAttrs.empty())
1880 catchTypesArrayAttr = rewriter.getArrayAttr(catchTypeAttrs);
1882 cir::EhDispatchOp::create(rewriter, loc, ehToken, catchTypesArrayAttr,
1883 defaultIsCatchAll, defaultDest, catchDests);
1885 return dispatchBlock;
1902 mlir::Block *flattenCatchHandler(mlir::Region &handlerRegion,
1903 mlir::Block *continueBlock,
1905 mlir::Block *insertBefore,
1906 mlir::PatternRewriter &rewriter)
const {
1908 mlir::Block *handlerEntry = &handlerRegion.front();
1911 rewriter.inlineRegionBefore(handlerRegion, insertBefore);
1914 for (mlir::Block &block : llvm::make_range(handlerEntry->getIterator(),
1916 if (
auto yieldOp = dyn_cast<cir::YieldOp>(block.getTerminator())) {
1929 if (mlir::Operation *prev = yieldOp->getPrevNode())
1930 return isa<cir::EndCatchOp>(prev);
1931 llvm::SmallPtrSet<mlir::Block *, 8> visited;
1932 llvm::SmallVector<mlir::Block *, 4> worklist;
1933 for (mlir::Block *pred : block.getPredecessors())
1934 worklist.push_back(pred);
1935 while (!worklist.empty()) {
1936 mlir::Block *b = worklist.pop_back_val();
1937 if (!visited.insert(b).second)
1939 mlir::Operation *term = b->getTerminator();
1940 if (mlir::Operation *prev = term->getPrevNode()) {
1941 if (isa<cir::EndCatchOp>(prev))
1944 for (mlir::Block *pred : b->getPredecessors())
1945 worklist.push_back(pred);
1949 "expected end_catch reachable before yield "
1950 "in catch handler");
1951 rewriter.setInsertionPoint(yieldOp);
1952 rewriter.replaceOpWithNewOp<cir::BrOp>(yieldOp, continueBlock);
1956 return handlerEntry;
1965 mlir::Block *flattenUnwindHandler(mlir::Region &unwindRegion,
1967 mlir::Block *insertBefore,
1968 mlir::PatternRewriter &rewriter)
const {
1969 mlir::Block *unwindEntry = &unwindRegion.front();
1970 rewriter.inlineRegionBefore(unwindRegion, insertBefore);
1975 matchAndRewrite(cir::TryOp tryOp,
1976 mlir::PatternRewriter &rewriter)
const override {
1983 for (mlir::Region ®ion : tryOp->getRegions())
1984 if (hasNestedOpsToFlatten(region))
1985 return mlir::failure();
1987 mlir::OpBuilder::InsertionGuard guard(rewriter);
1988 mlir::Location loc = tryOp.getLoc();
1990 mlir::ArrayAttr handlerTypes = tryOp.getHandlerTypesAttr();
1991 mlir::MutableArrayRef<mlir::Region> handlerRegions =
1992 tryOp.getHandlerRegions();
1995 llvm::SmallVector<cir::CallOp> callsToRewrite;
1996 collectThrowingCalls(tryOp.getTryRegion(), callsToRewrite);
1997 llvm::SmallVector<cir::ThrowOp> throwsToRewrite;
1998 collectThrows(tryOp.getTryRegion(), throwsToRewrite);
2001 llvm::SmallVector<cir::ResumeOp> resumeOpsToChain;
2002 collectResumeOps(tryOp.getTryRegion(), resumeOpsToChain);
2005 mlir::Block *currentBlock = rewriter.getInsertionBlock();
2006 mlir::Block *continueBlock =
2007 rewriter.splitBlock(currentBlock, rewriter.getInsertionPoint());
2010 mlir::Block *bodyEntry = &tryOp.getTryRegion().front();
2011 mlir::Block *bodyExit = &tryOp.getTryRegion().back();
2014 rewriter.inlineRegionBefore(tryOp.getTryRegion(), continueBlock);
2017 rewriter.setInsertionPointToEnd(currentBlock);
2018 cir::BrOp::create(rewriter, loc, bodyEntry);
2021 if (
auto bodyYield = dyn_cast<cir::YieldOp>(bodyExit->getTerminator())) {
2022 rewriter.setInsertionPoint(bodyYield);
2023 rewriter.replaceOpWithNewOp<cir::BrOp>(bodyYield, continueBlock);
2027 if (!handlerTypes || handlerTypes.empty()) {
2028 rewriter.eraseOp(tryOp);
2029 return mlir::success();
2037 if (callsToRewrite.empty() && throwsToRewrite.empty() &&
2038 resumeOpsToChain.empty()) {
2039 for (mlir::Region &handlerRegion : handlerRegions)
2040 for (mlir::Block &block : handlerRegion)
2041 block.dropAllDefinedValueUses();
2042 rewriter.eraseOp(tryOp);
2043 return mlir::success();
2049 llvm::SmallVector<mlir::Block *> catchHandlerBlocks;
2051 for (
const auto &[idx, typeAttr] : llvm::enumerate(handlerTypes)) {
2052 mlir::Region &handlerRegion = handlerRegions[idx];
2054 if (mlir::isa<cir::UnwindAttr, cir::EhFilterAttr, cir::EhUnexpectedAttr>(
2056 mlir::Block *unwindEntry =
2057 flattenUnwindHandler(handlerRegion, loc, continueBlock, rewriter);
2058 catchHandlerBlocks.push_back(unwindEntry);
2060 mlir::Block *handlerEntry = flattenCatchHandler(
2061 handlerRegion, continueBlock, loc, continueBlock, rewriter);
2062 catchHandlerBlocks.push_back(handlerEntry);
2067 mlir::Block *dispatchBlock =
2068 buildCatchDispatchBlock(tryOp, handlerTypes, catchHandlerBlocks, loc,
2069 catchHandlerBlocks.front(), rewriter);
2080 handlerTypes && llvm::any_of(handlerTypes, [](mlir::Attribute attr) {
2081 return mlir::isa<cir::CatchAllAttr>(attr);
2090 bool isCleanupOnly = tryOp.getCleanup() && !hasCatchAll;
2091 if (!callsToRewrite.empty() || !throwsToRewrite.empty()) {
2093 mlir::Block *unwindBlock = buildUnwindBlock(dispatchBlock, isCleanupOnly,
2094 loc, dispatchBlock, rewriter);
2096 for (cir::CallOp callOp : callsToRewrite)
2098 for (cir::ThrowOp throwOp : throwsToRewrite)
2105 for (cir::ResumeOp resumeOp : resumeOpsToChain) {
2110 if (
auto ehInitiate = traceToEhInitiate(resumeOp.getEhToken())) {
2111 rewriter.modifyOpInPlace(ehInitiate,
2112 [&] { ehInitiate.removeCleanupAttr(); });
2116 mlir::Value ehToken = resumeOp.getEhToken();
2117 rewriter.setInsertionPoint(resumeOp);
2118 rewriter.replaceOpWithNewOp<cir::BrOp>(
2119 resumeOp, mlir::ValueRange{ehToken},
nullptr,
2124 rewriter.eraseOp(tryOp);
2126 return mlir::success();
2130void populateFlattenCFGPatterns(RewritePatternSet &patterns) {
2132 .add<CIRIfFlattening, CIRLoopOpInterfaceFlattening, CIRScopeOpFlattening,
2133 CIRSwitchOpFlattening, CIRTernaryOpFlattening,
2134 CIRCleanupScopeOpFlattening, CIRTryOpFlattening>(
2135 patterns.getContext());
2143class MLIRChangedListener final :
public mlir::RewriterBase::Listener {
2144 bool hasChanged =
false;
2147 void reset() { hasChanged =
false; }
2149 bool changed()
const {
return hasChanged; }
2151 void notifyBlockErased(Block *)
override { hasChanged =
true; }
2152 void notifyOperationModified(Operation *)
override { hasChanged =
true; }
2153 void notifyOperationReplaced(Operation *, Operation *)
override {
2156 void notifyOperationReplaced(Operation *, ValueRange)
override {
2159 void notifyOperationErased(Operation *)
override { hasChanged =
true; }
2163 void notifyOperationInserted(Operation *,
2164 mlir::IRRewriter::InsertPoint)
override {
2167 void notifyBlockInserted(Block *, Region *, Region::iterator)
override {
2173void CIRFlattenCFGPass::runOnOperation() {
2174 RewritePatternSet patternList(&getContext());
2175 populateFlattenCFGPatterns(patternList);
2176 FrozenRewritePatternSet patterns(std::move(patternList));
2178 PatternApplicator applicator(patterns);
2181 applicator.applyDefaultCostModel();
2183 mlir::PatternRewriter rewriter(&getContext());
2184 MLIRChangedListener changedListener;
2185 rewriter.setListener(&changedListener);
2188 changedListener.reset();
2195 llvm::SmallVector<Operation *, 16> ops;
2196 getOperation()->walk<mlir::WalkOrder::PostOrder>([&](Operation *op) {
2197 if (isa<IfOp, ScopeOp, SwitchOp, LoopOpInterface, TernaryOp,
2198 CleanupScopeOp, TryOp>(op))
2202 for (mlir::Operation *op : ops) {
2203 rewriter.setInsertionPoint(op);
2204 (void)applicator.matchAndRewrite(op, rewriter);
2206 }
while (changedListener.changed());
2214 return std::make_unique<CIRFlattenCFGPass>();
mlir::Block * replaceThrowWithTryThrow(cir::ThrowOp throwOp, mlir::Block *unwindDest, mlir::Location loc, mlir::RewriterBase &rewriter)
Replace a cir::ThrowOp with a cir::TryThrowOp whose unwind destination is unwindDest.
mlir::Block * replaceCallWithTryCall(cir::CallOp callOp, mlir::Block *unwindDest, mlir::Location loc, mlir::RewriterBase &rewriter)
Replace a cir::CallOp with a cir::TryCallOp whose unwind destination is unwindDest.
@ Default
Set to the current date and time.
std::unique_ptr< Pass > createCIRFlattenCFGPass()
int const char * function
float __ovld __cnfn step(float, float)
Returns 0.0 if x < edge, otherwise it returns 1.0.
static bool stackSaveOp()