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 mlir::DistinctAttr loopId = {},
44 cir::LoopHintAttr loopHints = {}) {
45 assert(op->hasTrait<mlir::OpTrait::IsTerminator>() &&
"not a terminator");
46 mlir::OpBuilder::InsertionGuard guard(rewriter);
47 rewriter.setInsertionPoint(op);
48 rewriter.replaceOpWithNewOp<cir::BrOp>(op, mlir::ValueRange{}, loopId,
54template <
typename... Ops>
55void walkRegionSkipping(
57 mlir::function_ref<mlir::WalkResult(mlir::Operation *)> callback) {
58 region.walk<mlir::WalkOrder::PreOrder>([&](mlir::Operation *op) {
60 return mlir::WalkResult::skip();
74static bool hasNestedOpsToFlatten(mlir::Region ®ion) {
76 .walk([](mlir::Operation *op) {
77 if (op->getNumRegions() > 0 && !isa<cir::CaseOp>(op))
78 return mlir::WalkResult::interrupt();
79 return mlir::WalkResult::advance();
90static bool isNonReturningTerminator(mlir::Operation *op) {
91 return mlir::isa_and_nonnull<cir::UnreachableOp, cir::TrapOp>(op);
109static mlir::LogicalResult
110rewriteRegionExitToContinue(mlir::PatternRewriter &rewriter,
111 mlir::Region ®ion, mlir::Block *continueBlock,
112 llvm::StringRef regionDescription) {
113 mlir::Operation *terminator = region.back().getTerminator();
114 rewriter.setInsertionPointToEnd(®ion.back());
115 if (
auto yieldOp = mlir::dyn_cast<cir::YieldOp>(terminator)) {
116 rewriter.replaceOpWithNewOp<cir::BrOp>(
117 yieldOp, yieldOp.getArgs(),
nullptr,
nullptr,
119 return mlir::success();
121 if (isNonReturningTerminator(terminator))
122 return mlir::success();
123 terminator->emitError(
"unexpected terminator in ")
125 <<
" region, expected yield, unreachable, or trap, got: "
126 << terminator->getName();
127 return mlir::failure();
130struct CIRFlattenCFGPass :
public impl::CIRFlattenCFGBase<CIRFlattenCFGPass> {
132 CIRFlattenCFGPass() =
default;
133 void runOnOperation()
override;
136struct CIRIfFlattening :
public mlir::OpRewritePattern<cir::IfOp> {
137 using OpRewritePattern<IfOp>::OpRewritePattern;
140 matchAndRewrite(cir::IfOp ifOp,
141 mlir::PatternRewriter &rewriter)
const override {
142 mlir::OpBuilder::InsertionGuard guard(rewriter);
143 mlir::Location loc = ifOp.getLoc();
144 bool emptyElse = ifOp.getElseRegion().empty();
145 mlir::Block *currentBlock = rewriter.getInsertionBlock();
146 mlir::Block *remainingOpsBlock =
147 rewriter.splitBlock(currentBlock, rewriter.getInsertionPoint());
148 mlir::Block *continueBlock;
149 if (ifOp->getResults().empty())
150 continueBlock = remainingOpsBlock;
152 llvm_unreachable(
"NYI");
155 mlir::Block *thenBeforeBody = &ifOp.getThenRegion().front();
156 mlir::Block *thenAfterBody = &ifOp.getThenRegion().back();
157 rewriter.inlineRegionBefore(ifOp.getThenRegion(), continueBlock);
159 rewriter.setInsertionPointToEnd(thenAfterBody);
160 if (
auto thenYieldOp =
161 dyn_cast<cir::YieldOp>(thenAfterBody->getTerminator())) {
162 rewriter.replaceOpWithNewOp<cir::BrOp>(
163 thenYieldOp, thenYieldOp.getArgs(),
164 nullptr,
nullptr, continueBlock);
167 rewriter.setInsertionPointToEnd(continueBlock);
170 mlir::Block *elseBeforeBody =
nullptr;
171 mlir::Block *elseAfterBody =
nullptr;
173 elseBeforeBody = &ifOp.getElseRegion().front();
174 elseAfterBody = &ifOp.getElseRegion().back();
175 rewriter.inlineRegionBefore(ifOp.getElseRegion(), continueBlock);
177 elseBeforeBody = elseAfterBody = continueBlock;
180 rewriter.setInsertionPointToEnd(currentBlock);
181 cir::BrCondOp::create(rewriter, loc, ifOp.getCondition(), thenBeforeBody,
185 rewriter.setInsertionPointToEnd(elseAfterBody);
186 if (
auto elseYieldOP =
187 dyn_cast<cir::YieldOp>(elseAfterBody->getTerminator())) {
188 rewriter.replaceOpWithNewOp<cir::BrOp>(
189 elseYieldOP, elseYieldOP.getArgs(),
nullptr,
190 nullptr, continueBlock);
194 rewriter.replaceOp(ifOp, continueBlock->getArguments());
195 return mlir::success();
199class CIRScopeOpFlattening :
public mlir::OpRewritePattern<cir::ScopeOp> {
201 using OpRewritePattern<cir::ScopeOp>::OpRewritePattern;
204 matchAndRewrite(cir::ScopeOp scopeOp,
205 mlir::PatternRewriter &rewriter)
const override {
206 mlir::OpBuilder::InsertionGuard guard(rewriter);
207 mlir::Location loc = scopeOp.getLoc();
215 if (scopeOp.isEmpty()) {
216 rewriter.eraseOp(scopeOp);
217 return mlir::success();
222 mlir::Block *currentBlock = rewriter.getInsertionBlock();
223 mlir::Block *continueBlock =
224 rewriter.splitBlock(currentBlock, rewriter.getInsertionPoint());
225 if (scopeOp.getNumResults() > 0)
226 continueBlock->addArguments(scopeOp.getResultTypes(), loc);
229 mlir::Block *beforeBody = &scopeOp.getScopeRegion().front();
230 mlir::Block *afterBody = &scopeOp.getScopeRegion().back();
231 rewriter.inlineRegionBefore(scopeOp.getScopeRegion(), continueBlock);
234 rewriter.setInsertionPointToEnd(currentBlock);
236 cir::BrOp::create(rewriter, loc, mlir::ValueRange(),
nullptr,
237 nullptr, beforeBody);
241 rewriter.setInsertionPointToEnd(afterBody);
242 if (
auto yieldOp = dyn_cast<cir::YieldOp>(afterBody->getTerminator())) {
243 rewriter.replaceOpWithNewOp<cir::BrOp>(
244 yieldOp, yieldOp.getArgs(),
245 nullptr,
nullptr, continueBlock);
249 rewriter.replaceOp(scopeOp, continueBlock->getArguments());
251 return mlir::success();
255class CIRSwitchOpFlattening :
public mlir::OpRewritePattern<cir::SwitchOp> {
257 using OpRewritePattern<cir::SwitchOp>::OpRewritePattern;
259 inline void rewriteYieldOp(mlir::PatternRewriter &rewriter,
260 cir::YieldOp yieldOp,
261 mlir::Block *destination)
const {
262 rewriter.setInsertionPoint(yieldOp);
263 rewriter.replaceOpWithNewOp<cir::BrOp>(yieldOp, yieldOp.getOperands(),
265 nullptr, destination);
269 Block *condBrToRangeDestination(cir::SwitchOp op,
270 mlir::PatternRewriter &rewriter,
271 mlir::Block *rangeDestination,
272 mlir::Block *defaultDestination,
273 const APInt &lowerBound,
274 const APInt &upperBound)
const {
275 auto condType = mlir::cast<cir::IntType>(op.getCondition().getType());
276 bool isSigned = condType.isSigned();
278 (isSigned ? lowerBound.sle(upperBound) : lowerBound.ule(upperBound)) &&
280 mlir::Block *resBlock = rewriter.createBlock(defaultDestination);
286 cir::IntType uIntType =
287 cir::IntType::get(op.getContext(), condType.getWidth(),
290 cir::ConstantOp lowerBoundValue = cir::ConstantOp::create(
291 rewriter, op.getLoc(), cir::IntAttr::get(condType, lowerBound));
292 mlir::Value diffValue = cir::SubOp::create(
293 rewriter, op.getLoc(), op.getCondition(), lowerBoundValue);
301 diffValue = cir::CastOp::create(rewriter, op.getLoc(), uIntType,
302 CastKind::integral, diffValue);
304 cir::ConstantOp rangeLength = cir::ConstantOp::create(
305 rewriter, op.getLoc(),
306 cir::IntAttr::get(uIntType, upperBound - lowerBound));
308 cir::CmpOp cmpResult = cir::CmpOp::create(
309 rewriter, op.getLoc(), cir::CmpOpKind::le, diffValue, rangeLength);
310 cir::BrCondOp::create(rewriter, op.getLoc(), cmpResult, rangeDestination,
316 matchAndRewrite(cir::SwitchOp op,
317 mlir::PatternRewriter &rewriter)
const override {
322 for (mlir::Region ®ion : op->getRegions())
323 if (hasNestedOpsToFlatten(region))
324 return mlir::failure();
327 if (op.getBody().hasOneBlock() &&
328 op.getBody().front().without_terminator().empty()) {
329 rewriter.eraseOp(op);
330 return mlir::success();
333 llvm::SmallVector<CaseOp> cases;
334 op.collectCases(cases);
337 mlir::Block *exitBlock = rewriter.splitBlock(
338 rewriter.getBlock(), op->getNextNode()->getIterator());
351 walkRegionSkipping<cir::LoopOpInterface, cir::SwitchOp>(
352 op.getBody(), [&](mlir::Operation *op) {
353 if (!isa<cir::BreakOp>(op))
354 return mlir::WalkResult::advance();
356 lowerTerminator(op, exitBlock, rewriter);
357 return mlir::WalkResult::skip();
363 cir::YieldOp switchYield =
nullptr;
365 for (mlir::Block &block :
366 llvm::make_early_inc_range(op.getBody().getBlocks()))
367 if (
auto yieldOp = dyn_cast<cir::YieldOp>(block.getTerminator()))
368 switchYield = yieldOp;
370 assert(!op.getBody().empty());
371 mlir::Block *originalBlock = op->getBlock();
372 mlir::Block *swopBlock =
373 rewriter.splitBlock(originalBlock, op->getIterator());
374 rewriter.inlineRegionBefore(op.getBody(), exitBlock);
377 rewriteYieldOp(rewriter, switchYield, exitBlock);
379 rewriter.setInsertionPointToEnd(originalBlock);
380 cir::BrOp::create(rewriter, op.getLoc(), swopBlock);
385 llvm::SmallVector<mlir::APInt, 8> caseValues;
386 llvm::SmallVector<mlir::Block *, 8> caseDestinations;
387 llvm::SmallVector<mlir::ValueRange, 8> caseOperands;
389 llvm::SmallVector<std::pair<APInt, APInt>> rangeValues;
390 llvm::SmallVector<mlir::Block *> rangeDestinations;
391 llvm::SmallVector<mlir::ValueRange> rangeOperands;
394 mlir::Block *defaultDestination = exitBlock;
395 mlir::ValueRange defaultOperands = exitBlock->getArguments();
398 for (cir::CaseOp caseOp : cases) {
399 mlir::Region ®ion = caseOp.getCaseRegion();
402 switch (caseOp.getKind()) {
403 case cir::CaseOpKind::Default:
404 defaultDestination = ®ion.front();
405 defaultOperands = defaultDestination->getArguments();
407 case cir::CaseOpKind::Range:
408 assert(caseOp.getValue().size() == 2 &&
409 "Case range should have 2 case value");
410 rangeValues.push_back(
411 {cast<cir::IntAttr>(caseOp.getValue()[0]).getValue(),
412 cast<cir::IntAttr>(caseOp.getValue()[1]).getValue()});
413 rangeDestinations.push_back(®ion.front());
414 rangeOperands.push_back(rangeDestinations.back()->getArguments());
416 case cir::CaseOpKind::Anyof:
417 case cir::CaseOpKind::Equal:
419 for (
const mlir::Attribute &value : caseOp.getValue()) {
420 caseValues.push_back(cast<cir::IntAttr>(value).getValue());
421 caseDestinations.push_back(®ion.front());
422 caseOperands.push_back(caseDestinations.back()->getArguments());
428 for (mlir::Block &blk : region.getBlocks()) {
429 if (blk.getNumSuccessors())
432 if (
auto yieldOp = dyn_cast<cir::YieldOp>(blk.getTerminator())) {
433 mlir::Operation *nextOp = caseOp->getNextNode();
434 assert(nextOp &&
"caseOp is not expected to be the last op");
435 mlir::Block *oldBlock = nextOp->getBlock();
436 mlir::Block *newBlock =
437 rewriter.splitBlock(oldBlock, nextOp->getIterator());
438 rewriter.setInsertionPointToEnd(oldBlock);
439 cir::BrOp::create(rewriter, nextOp->getLoc(), mlir::ValueRange(),
442 rewriteYieldOp(rewriter, yieldOp, newBlock);
446 mlir::Block *oldBlock = caseOp->getBlock();
447 mlir::Block *newBlock =
448 rewriter.splitBlock(oldBlock, caseOp->getIterator());
450 mlir::Block &entryBlock = caseOp.getCaseRegion().front();
451 rewriter.inlineRegionBefore(caseOp.getCaseRegion(), newBlock);
454 rewriter.setInsertionPointToEnd(oldBlock);
455 cir::BrOp::create(rewriter, caseOp.getLoc(), &entryBlock);
459 for (cir::CaseOp caseOp : cases) {
460 mlir::Block *caseBlock = caseOp->getBlock();
463 if (caseBlock->hasNoPredecessors())
464 rewriter.eraseBlock(caseBlock);
466 rewriter.eraseOp(caseOp);
470 mlir::cast<cir::IntType>(op.getCondition().getType()).isSigned();
471 for (
auto [rangeVal, operand, destination] :
472 llvm::zip(rangeValues, rangeOperands, rangeDestinations)) {
473 APInt lowerBound = rangeVal.first;
474 APInt upperBound = rangeVal.second;
477 if (isSigned ? lowerBound.sgt(upperBound) : lowerBound.ugt(upperBound))
482 constexpr uint64_t kSmallRangeThreshold = 64;
483 APInt rangeSize = upperBound - lowerBound;
484 if (rangeSize.ult(kSmallRangeThreshold)) {
491 APInt caseValue = lowerBound;
492 for (uint64_t n = rangeSize.getZExtValue() + 1; n != 0; --n) {
493 caseValues.push_back(caseValue++);
494 caseOperands.push_back(operand);
495 caseDestinations.push_back(destination);
501 condBrToRangeDestination(op, rewriter, destination,
502 defaultDestination, lowerBound, upperBound);
503 defaultOperands = operand;
507 rewriter.setInsertionPoint(op);
508 rewriter.replaceOpWithNewOp<cir::SwitchFlatOp>(
509 op, op.getCondition(), defaultDestination, defaultOperands, caseValues,
510 caseDestinations, caseOperands);
512 return mlir::success();
516class CIRLoopOpInterfaceFlattening
517 :
public mlir::OpInterfaceRewritePattern<cir::LoopOpInterface> {
519 using mlir::OpInterfaceRewritePattern<
520 cir::LoopOpInterface>::OpInterfaceRewritePattern;
522 inline void lowerConditionOp(cir::ConditionOp op, mlir::Block *body,
524 mlir::PatternRewriter &rewriter,
525 mlir::DistinctAttr loopId = {},
526 cir::LoopHintAttr loopHints = {})
const {
527 mlir::OpBuilder::InsertionGuard guard(rewriter);
528 rewriter.setInsertionPoint(op);
529 rewriter.replaceOpWithNewOp<cir::BrCondOp>(
530 op, op.getCondition(), mlir::ValueRange{}, mlir::ValueRange{}, loopId,
531 loopHints, body, exit);
568 rewriteLoopWithCleanup(cir::LoopOpInterface op,
569 mlir::PatternRewriter &rewriter)
const {
570 mlir::Location loc = op.getLoc();
571 mlir::Region *stepRegion = op.maybeGetStep();
573 cir::CleanupKindAttr cleanupKind = op.maybeGetCleanupKind();
574 assert(cleanupKind &&
"loop cleanup region without a cleanup kind");
576 mlir::Region &condRegion = op.getCond();
577 mlir::Region &bodyRegion = op.getBody();
578 mlir::Region &cleanupRegion = *op.maybeGetCleanup();
584 cast<cir::ConditionOp>(condRegion.back().getTerminator());
585 mlir::Value condVal = conditionOp.getCondition();
590 mlir::Block *bodyFront = &bodyRegion.front();
591 mlir::Block *stepFront = stepRegion ? &stepRegion->front() :
nullptr;
596 llvm::SmallVector<cir::YieldOp> bodyYieldsToStep;
597 llvm::SmallVector<cir::ContinueOp> continuesToStep;
599 for (mlir::Block &blk : bodyRegion.getBlocks())
600 if (
auto y = dyn_cast<cir::YieldOp>(blk.getTerminator()))
601 bodyYieldsToStep.push_back(y);
602 op.walkBodySkippingNestedLoops([&](mlir::Operation *o) {
603 if (
auto c = dyn_cast<cir::ContinueOp>(o)) {
604 continuesToStep.push_back(c);
605 return mlir::WalkResult::skip();
607 return mlir::WalkResult::advance();
613 rewriter.inlineRegionBefore(bodyRegion, condRegion, condRegion.end());
615 rewriter.inlineRegionBefore(*stepRegion, condRegion, condRegion.end());
619 mlir::Block *breakBlock =
620 rewriter.createBlock(&condRegion, condRegion.end());
621 rewriter.setInsertionPointToEnd(breakBlock);
622 cir::BreakOp::create(rewriter, conditionOp.getLoc());
624 rewriter.setInsertionPoint(conditionOp);
625 rewriter.replaceOpWithNewOp<cir::BrCondOp>(conditionOp, condVal, bodyFront,
630 for (cir::YieldOp y : bodyYieldsToStep)
631 lowerTerminator(y, stepFront, rewriter);
632 for (cir::ContinueOp c : continuesToStep)
633 lowerTerminator(c, stepFront, rewriter);
637 mlir::Block *newBodyBlock = rewriter.createBlock(&bodyRegion);
638 rewriter.setInsertionPointToEnd(newBodyBlock);
639 auto emitYield = [](mlir::OpBuilder &b, mlir::Location l) {
640 cir::YieldOp::create(b, l);
642 auto scope = cir::CleanupScopeOp::create(
643 rewriter, loc, cleanupKind.getValue(), emitYield, emitYield);
644 cir::YieldOp::create(rewriter, loc);
649 mlir::Block *bodyPlaceholder = &scope.getBodyRegion().front();
650 rewriter.inlineRegionBefore(condRegion, bodyPlaceholder);
651 rewriter.eraseBlock(bodyPlaceholder);
653 mlir::Block *cleanupPlaceholder = &scope.getCleanupRegion().front();
654 rewriter.inlineRegionBefore(cleanupRegion, cleanupPlaceholder);
655 rewriter.eraseBlock(cleanupPlaceholder);
658 mlir::Block *newCondBlock = rewriter.createBlock(&condRegion);
659 rewriter.setInsertionPointToEnd(newCondBlock);
660 mlir::Value trueVal = cir::ConstantOp::create(
661 rewriter, loc, cir::BoolAttr::get(rewriter.getContext(),
true));
662 cir::ConditionOp::create(rewriter, loc, trueVal);
666 mlir::Block *newStepBlock = rewriter.createBlock(stepRegion);
667 rewriter.setInsertionPointToEnd(newStepBlock);
668 cir::YieldOp::create(rewriter, loc);
674 if (
auto whileOp = mlir::dyn_cast<cir::WhileOp>(op.getOperation()))
675 whileOp.removeCleanupKindAttr();
676 else if (
auto forOp = mlir::dyn_cast<cir::ForOp>(op.getOperation()))
677 forOp.removeCleanupKindAttr();
679 return mlir::success();
683 matchAndRewrite(cir::LoopOpInterface op,
684 mlir::PatternRewriter &rewriter)
const final {
689 for (mlir::Region ®ion : op->getRegions())
690 if (hasNestedOpsToFlatten(region))
691 return mlir::failure();
701 if (op.maybeGetCleanup())
702 return rewriteLoopWithCleanup(op, rewriter);
705 mlir::Block *entry = rewriter.getInsertionBlock();
707 rewriter.splitBlock(entry, rewriter.getInsertionPoint());
708 mlir::Block *cond = &op.getCond().front();
709 mlir::Block *body = &op.getBody().front();
711 (op.maybeGetStep() ? &op.maybeGetStep()->front() :
nullptr);
714 mlir::Block *firstBlock = &op.getEntry().front();
715 rewriter.setInsertionPointToEnd(entry);
716 cir::BrOp::create(rewriter, op.getLoc(), firstBlock);
722 cir::LoopHintAttr loopHints = op.getLoopHintsAttr();
723 mlir::DistinctAttr loopId = op.getLoopIdAttr();
724 auto idFor = [&](mlir::Block *target) {
725 return target == firstBlock ? loopId : mlir::DistinctAttr{};
727 auto hintsFor = [&](mlir::Block *target) {
728 return target == firstBlock ? loopHints : cir::LoopHintAttr{};
736 cast<cir::ConditionOp>(op.getCond().back().getTerminator());
737 lowerConditionOp(conditionOp, body, exit, rewriter, idFor(body),
745 mlir::Block *dest = (
step ?
step : cond);
746 op.walkBodySkippingNestedLoops([&](mlir::Operation *op) {
747 if (!isa<cir::ContinueOp>(op))
748 return mlir::WalkResult::advance();
750 lowerTerminator(op, dest, rewriter, idFor(dest), hintsFor(dest));
751 return mlir::WalkResult::skip();
755 walkRegionSkipping<cir::LoopOpInterface, cir::SwitchOp>(
756 op.getBody(), [&](mlir::Operation *op) {
757 if (!isa<cir::BreakOp>(op))
758 return mlir::WalkResult::advance();
760 lowerTerminator(op, exit, rewriter);
761 return mlir::WalkResult::skip();
765 for (mlir::Block &blk : op.getBody().getBlocks()) {
766 auto bodyYield = dyn_cast<cir::YieldOp>(blk.getTerminator());
768 lowerTerminator(bodyYield, dest, rewriter, idFor(dest), hintsFor(dest));
776 cast<cir::YieldOp>(op.maybeGetStep()->back().getTerminator()), cond,
777 rewriter, idFor(cond), hintsFor(cond));
780 rewriter.inlineRegionBefore(op.getCond(), exit);
781 rewriter.inlineRegionBefore(op.getBody(), exit);
783 rewriter.inlineRegionBefore(*op.maybeGetStep(), exit);
785 rewriter.eraseOp(op);
786 return mlir::success();
790class CIRTernaryOpFlattening :
public mlir::OpRewritePattern<cir::TernaryOp> {
792 using OpRewritePattern<cir::TernaryOp>::OpRewritePattern;
795 matchAndRewrite(cir::TernaryOp op,
796 mlir::PatternRewriter &rewriter)
const override {
797 Location loc = op->getLoc();
798 Block *condBlock = rewriter.getInsertionBlock();
799 Block::iterator opPosition = rewriter.getInsertionPoint();
800 Block *remainingOpsBlock = rewriter.splitBlock(condBlock, opPosition);
801 llvm::SmallVector<mlir::Location, 2> locs;
804 if (op->getResultTypes().size())
806 Block *continueBlock =
807 rewriter.createBlock(remainingOpsBlock, op->getResultTypes(), locs);
808 cir::BrOp::create(rewriter, loc, remainingOpsBlock);
810 Region &trueRegion = op.getTrueRegion();
811 Block *trueBlock = &trueRegion.front();
816 if (failed(rewriteRegionExitToContinue(rewriter, trueRegion, continueBlock,
818 return mlir::success();
819 rewriter.inlineRegionBefore(trueRegion, continueBlock);
821 Block *falseBlock = continueBlock;
822 Region &falseRegion = op.getFalseRegion();
824 falseBlock = &falseRegion.front();
825 if (failed(rewriteRegionExitToContinue(rewriter, falseRegion, continueBlock,
827 return mlir::success();
828 rewriter.inlineRegionBefore(falseRegion, continueBlock);
830 rewriter.setInsertionPointToEnd(condBlock);
831 cir::BrCondOp::create(rewriter, loc, op.getCond(), trueBlock, falseBlock);
833 rewriter.replaceOp(op, continueBlock->getArguments());
836 return mlir::success();
843static cir::AllocaOp getOrCreateCleanupDestSlot(cir::FuncOp funcOp,
844 mlir::PatternRewriter &rewriter,
845 mlir::Location loc) {
846 mlir::Block &entryBlock = funcOp.getBody().front();
849 auto it = llvm::find_if(entryBlock, [](
auto &op) {
850 return mlir::isa<AllocaOp>(&op) &&
851 mlir::cast<AllocaOp>(&op).getCleanupDestSlot();
853 if (it != entryBlock.end())
854 return mlir::cast<cir::AllocaOp>(*it);
857 mlir::OpBuilder::InsertionGuard guard(rewriter);
858 rewriter.setInsertionPointToStart(&entryBlock);
859 cir::IntType s32Type =
860 cir::IntType::get(rewriter.getContext(), 32,
true);
862 cir::PointerType ptrToS32Type = cir::PointerType::get(
863 s32Type, dataLayout.getAllocaAddrSpace(rewriter.getContext()));
864 uint64_t alignment = dataLayout.getAlignment(s32Type,
true).value();
865 auto allocaOp = cir::AllocaOp::create(
866 rewriter, loc, ptrToS32Type,
"__cleanup_dest_slot",
867 rewriter.getI64IntegerAttr(alignment));
868 allocaOp.setCleanupDestSlot(
true);
878static bool isLifetimeMarkerOnly(mlir::Region ®ion) {
879 return llvm::hasSingleElement(region) &&
882 [](mlir::Operation &op) { return isa<cir::LifetimeEndOp>(op); }) &&
883 llvm::all_of(region.front(), [](mlir::Operation &op) {
884 return isa<cir::LifetimeEndOp, cir::YieldOp>(op);
891static bool hasEnclosingEHRequirement(mlir::Operation *op) {
892 for (mlir::Region *region = op->getParentRegion(); region;
893 region = region->getParentRegion()) {
894 mlir::Operation *parent = region->getParentOp();
895 if (!parent || isa<cir::FuncOp>(parent))
897 if (
auto tryOp = dyn_cast<cir::TryOp>(parent)) {
898 auto handlers = tryOp.getHandlerTypesAttr();
899 if (region == &tryOp.getTryRegion() && handlers &&
900 llvm::any_of(handlers, [](mlir::Attribute handler) {
901 return !isa<cir::UnwindAttr>(handler);
904 }
else if (
auto cleanupOp = dyn_cast<cir::CleanupScopeOp>(parent)) {
905 if (region == &cleanupOp.getBodyRegion() &&
906 cleanupOp.getCleanupKindAttr().isEH() &&
907 !isLifetimeMarkerOnly(cleanupOp.getCleanupRegion()))
909 }
else if (
auto loopOp = dyn_cast<cir::LoopOpInterface>(parent)) {
911 mlir::Region *
cleanup = loopOp.maybeGetCleanup();
912 if (cleanup && region != cleanup && loopOp.maybeGetCleanupKind().isEH() &&
913 !isLifetimeMarkerOnly(*cleanup))
925collectThrowingCalls(mlir::Region ®ion,
927 region.walk([&](cir::CallOp callOp) {
931 if (!callOp.getNothrow() && !callOp.getMusttail())
932 callsToRewrite.push_back(callOp);
942collectThrows(mlir::Region ®ion,
945 [&](cir::ThrowOp throwOp) { throwsToRewrite.push_back(throwOp); });
954static void collectResumeOps(mlir::Region ®ion,
956 region.walk([&](cir::ResumeOp resumeOp) { resumeOps.push_back(resumeOp); });
962static mlir::Block *buildUnwindBlock(mlir::Block *dest,
bool isCleanupOnly,
964 mlir::Block *insertBefore,
965 mlir::PatternRewriter &rewriter) {
966 mlir::Block *unwindBlock = rewriter.createBlock(insertBefore);
967 rewriter.setInsertionPointToEnd(unwindBlock);
969 cir::EhInitiateOp::create(rewriter, loc, isCleanupOnly);
970 cir::BrOp::create(rewriter, loc, mlir::ValueRange{ehInitiate.getEhToken()},
971 nullptr,
nullptr, dest);
978static mlir::Block *buildTerminateUnwindBlock(mlir::Location loc,
979 mlir::Block *insertBefore,
980 mlir::PatternRewriter &rewriter) {
981 mlir::Block *terminateBlock = rewriter.createBlock(insertBefore);
982 rewriter.setInsertionPointToEnd(terminateBlock);
983 auto ehInitiate = cir::EhInitiateOp::create(rewriter, loc,
false);
984 cir::EhTerminateOp::create(rewriter, loc, ehInitiate.getEhToken());
985 return terminateBlock;
988class CIRCleanupScopeOpFlattening
989 :
public mlir::OpRewritePattern<cir::CleanupScopeOp> {
991 using OpRewritePattern<cir::CleanupScopeOp>::OpRewritePattern;
996 mlir::Operation *exitOp;
1002 CleanupExit(mlir::Operation *op,
int id) : exitOp(op), destinationId(id) {}
1012 static bool gotoTargetsLabelInRegion(cir::GotoOp gotoOp,
1013 mlir::Region ®ion) {
1014 llvm::StringRef targetLabel = gotoOp.getLabel();
1016 .walk([&](cir::LabelOp labelOp) {
1017 if (labelOp.getLabel() == targetLabel)
1018 return mlir::WalkResult::interrupt();
1019 return mlir::WalkResult::advance();
1047 void collectExits(mlir::Region &cleanupBodyRegion,
1048 llvm::SmallVectorImpl<CleanupExit> &exits,
1049 int &nextId)
const {
1053 for (mlir::Block &block : cleanupBodyRegion) {
1054 auto *terminator = block.getTerminator();
1055 if (isa<cir::YieldOp>(terminator))
1056 exits.emplace_back(terminator, nextId++);
1063 auto isGotoThatExitsCleanup = [&](mlir::Operation *op) {
1064 auto gotoOp = dyn_cast<cir::GotoOp>(op);
1065 return gotoOp && !gotoTargetsLabelInRegion(gotoOp, cleanupBodyRegion);
1074 auto isReturnThatExitsCleanup = [](mlir::Operation *op) {
1075 if (!isa<cir::ReturnOp>(op))
1077 auto callOp = dyn_cast_if_present<cir::CallOp>(op->getPrevNode());
1078 return !callOp || !callOp.getMusttail();
1085 auto collectExitsInLoop = [&](mlir::Operation *loopOp) {
1086 loopOp->walk<mlir::WalkOrder::PreOrder>([&](mlir::Operation *nestedOp) {
1087 if (isa<cir::CoroCleanupOp>(nestedOp) ||
1088 isReturnThatExitsCleanup(nestedOp) ||
1089 isGotoThatExitsCleanup(nestedOp)) {
1090 exits.emplace_back(nestedOp, nextId++);
1092 return mlir::WalkResult::advance();
1097 std::function<void(mlir::Region &,
bool)> collectExitsInCleanup;
1098 std::function<void(mlir::Operation *)> collectExitsInSwitch;
1102 collectExitsInSwitch = [&](mlir::Operation *switchOp) {
1103 switchOp->walk<mlir::WalkOrder::PreOrder>([&](mlir::Operation *nestedOp) {
1104 if (isa<cir::CleanupScopeOp>(nestedOp)) {
1107 collectExitsInCleanup(
1108 cast<cir::CleanupScopeOp>(nestedOp).getBodyRegion(),
1110 return mlir::WalkResult::skip();
1111 }
else if (isa<cir::LoopOpInterface>(nestedOp)) {
1112 collectExitsInLoop(nestedOp);
1113 return mlir::WalkResult::skip();
1114 }
else if (isa<cir::ContinueOp, cir::CoReturnOp, cir::CoroCleanupOp>(
1116 isReturnThatExitsCleanup(nestedOp) ||
1117 isGotoThatExitsCleanup(nestedOp)) {
1118 exits.emplace_back(nestedOp, nextId++);
1120 return mlir::WalkResult::advance();
1127 collectExitsInCleanup = [&](mlir::Region ®ion,
bool ignoreBreak) {
1128 region.walk<mlir::WalkOrder::PreOrder>([&](mlir::Operation *op) {
1135 if (!ignoreBreak && isa<cir::BreakOp>(op)) {
1136 exits.emplace_back(op, nextId++);
1137 }
else if (isa<cir::ContinueOp, cir::CoReturnOp, cir::CoroCleanupOp>(
1139 isReturnThatExitsCleanup(op) || isGotoThatExitsCleanup(op)) {
1140 exits.emplace_back(op, nextId++);
1141 }
else if (isa<cir::CleanupScopeOp>(op)) {
1143 collectExitsInCleanup(cast<cir::CleanupScopeOp>(op).getBodyRegion(),
1145 return mlir::WalkResult::skip();
1146 }
else if (isa<cir::LoopOpInterface>(op)) {
1150 collectExitsInLoop(op);
1151 return mlir::WalkResult::skip();
1152 }
else if (isa<cir::SwitchOp>(op)) {
1156 collectExitsInSwitch(op);
1157 return mlir::WalkResult::skip();
1159 return mlir::WalkResult::advance();
1164 collectExitsInCleanup(cleanupBodyRegion,
false);
1170 static bool shouldSinkReturnOperand(mlir::Value operand,
1171 cir::ReturnOp returnOp) {
1173 mlir::Operation *defOp = operand.getDefiningOp();
1179 if (!mlir::isa<cir::ConstantOp, cir::LoadOp>(defOp))
1183 if (!operand.hasOneUse())
1187 if (defOp->getBlock() != returnOp->getBlock())
1190 if (
auto loadOp = mlir::dyn_cast<cir::LoadOp>(defOp)) {
1192 mlir::Value ptr = loadOp.getAddr();
1193 auto funcOp = returnOp->getParentOfType<cir::FuncOp>();
1194 assert(funcOp &&
"Return op has no function parent?");
1195 mlir::Block &funcEntryBlock = funcOp.getBody().front();
1199 mlir::dyn_cast_if_present<cir::AllocaOp>(ptr.getDefiningOp()))
1200 return allocaOp->getBlock() == &funcEntryBlock;
1206 assert(mlir::isa<cir::ConstantOp>(defOp) &&
"Expected constant op");
1215 getReturnOpOperands(cir::ReturnOp returnOp, mlir::Operation *exitOp,
1216 mlir::Location loc, mlir::PatternRewriter &rewriter,
1217 llvm::SmallVectorImpl<mlir::Value> &returnValues)
const {
1218 mlir::Block *destBlock = rewriter.getInsertionBlock();
1219 auto funcOp = exitOp->getParentOfType<cir::FuncOp>();
1220 assert(funcOp &&
"Return op has no function parent?");
1221 mlir::Block &funcEntryBlock = funcOp.getBody().front();
1223 for (mlir::Value operand : returnOp.getOperands()) {
1224 if (shouldSinkReturnOperand(operand, returnOp)) {
1226 mlir::Operation *defOp = operand.getDefiningOp();
1227 rewriter.moveOpBefore(defOp, destBlock, destBlock->end());
1228 returnValues.push_back(operand);
1231 cir::AllocaOp alloca;
1233 mlir::OpBuilder::InsertionGuard guard(rewriter);
1234 rewriter.setInsertionPointToStart(&funcEntryBlock);
1235 cir::CIRDataLayout dataLayout(
1236 funcOp->getParentOfType<mlir::ModuleOp>());
1238 dataLayout.getAlignment(operand.getType(),
true).value();
1239 cir::PointerType ptrType = cir::PointerType::get(operand.getType());
1241 cir::AllocaOp::create(rewriter, loc, ptrType,
"__ret_operand_tmp",
1242 rewriter.getI64IntegerAttr(alignment));
1247 mlir::OpBuilder::InsertionGuard guard(rewriter);
1248 rewriter.setInsertionPoint(exitOp);
1249 cir::StoreOp::create(rewriter, loc, operand, alloca,
1252 mlir::IntegerAttr(),
1253 cir::SyncScopeKindAttr(), cir::MemOrderAttr());
1257 rewriter.setInsertionPointToEnd(destBlock);
1259 cir::LoadOp::create(rewriter, loc, alloca,
false,
1261 mlir::IntegerAttr(),
1262 cir::SyncScopeKindAttr(), cir::MemOrderAttr(),
1264 returnValues.push_back(loaded);
1274 createExitTerminator(mlir::Operation *exitOp, mlir::Location loc,
1275 mlir::Block *continueBlock,
1276 mlir::PatternRewriter &rewriter)
const {
1277 return llvm::TypeSwitch<mlir::Operation *, mlir::LogicalResult>(exitOp)
1278 .Case<cir::YieldOp>([&](
auto) {
1280 cir::BrOp::create(rewriter, loc, continueBlock);
1281 return mlir::success();
1283 .Case<cir::BreakOp>([&](
auto) {
1285 cir::BreakOp::create(rewriter, loc);
1286 return mlir::success();
1288 .Case<cir::ContinueOp>([&](
auto) {
1290 cir::ContinueOp::create(rewriter, loc);
1291 return mlir::success();
1293 .Case<cir::CoReturnOp>([&](
auto) {
1297 cir::CoReturnOp::create(rewriter, loc);
1298 return mlir::success();
1300 .Case<cir::CoroCleanupOp>([&](
auto) {
1303 cir::CoroCleanupOp::create(rewriter, loc);
1304 return mlir::success();
1306 .Case<cir::ReturnOp>([&](
auto returnOp) {
1310 if (returnOp.hasOperand()) {
1311 llvm::SmallVector<mlir::Value, 2> returnValues;
1312 getReturnOpOperands(returnOp, exitOp, loc, rewriter, returnValues);
1313 cir::ReturnOp::create(rewriter, loc, returnValues);
1315 cir::ReturnOp::create(rewriter, loc);
1317 return mlir::success();
1319 .Case<cir::GotoOp>([&](
auto gotoOp) {
1324 cir::GotoOp::create(rewriter, loc, gotoOp.getLabel());
1325 return mlir::success();
1327 .
Default([&](mlir::Operation *op) {
1328 cir::UnreachableOp::create(rewriter, loc);
1329 return op->emitError(
1330 "unexpected exit operation in cleanup scope body");
1336 static bool regionExitsOnlyFromLastBlock(mlir::Region ®ion) {
1337 for (mlir::Block &block : region) {
1338 if (&block == ®ion.back())
1340 bool expectedTerminator =
1341 llvm::TypeSwitch<mlir::Operation *, bool>(block.getTerminator())
1348 .Case<cir::YieldOp, cir::ReturnOp, cir::ResumeFlatOp,
1349 cir::ContinueOp, cir::BreakOp, cir::GotoOp>(
1350 [](
auto) {
return false; })
1359 .Case<cir::TryCallOp>([](
auto) {
return false; })
1363 .Case<cir::EhDispatchOp>([](
auto) {
return false; })
1367 .Case<cir::SwitchFlatOp>([](
auto) {
return false; })
1370 .Case<cir::UnreachableOp, cir::TrapOp>([](
auto) {
return true; })
1372 .Case<cir::IndirectBrOp>([](
auto) {
return false; })
1375 .Case<cir::BrOp>([&](cir::BrOp brOp) {
1376 assert(brOp.getDest()->getParent() == ®ion &&
1377 "branch destination is not in the region");
1380 .Case<cir::BrCondOp>([&](cir::BrCondOp brCondOp) {
1381 assert(brCondOp.getDestTrue()->getParent() == ®ion &&
1382 "branch destination is not in the region");
1383 assert(brCondOp.getDestFalse()->getParent() == ®ion &&
1384 "branch destination is not in the region");
1388 .
Default([](mlir::Operation *) ->
bool {
1389 llvm_unreachable(
"unexpected terminator in cleanup region");
1391 if (!expectedTerminator)
1419 mlir::Block *buildEHCleanupBlocks(cir::CleanupScopeOp cleanupOp,
1421 mlir::Block *insertBefore,
1422 mlir::PatternRewriter &rewriter)
const {
1423 assert(regionExitsOnlyFromLastBlock(cleanupOp.getCleanupRegion()) &&
1424 "cleanup region has exits in non-final blocks");
1428 mlir::Block *blockBeforeClone =
insertBefore->getPrevNode();
1431 rewriter.cloneRegionBefore(cleanupOp.getCleanupRegion(), insertBefore);
1434 mlir::Block *clonedEntry = blockBeforeClone
1435 ? blockBeforeClone->getNextNode()
1440 auto ehTokenType = cir::EhTokenType::get(rewriter.getContext());
1441 mlir::Value ehToken = clonedEntry->addArgument(ehTokenType, loc);
1443 rewriter.setInsertionPointToStart(clonedEntry);
1444 auto beginCleanup = cir::BeginCleanupOp::create(rewriter, loc, ehToken);
1448 mlir::Block *lastClonedBlock =
insertBefore->getPrevNode();
1450 mlir::dyn_cast<cir::YieldOp>(lastClonedBlock->getTerminator());
1452 rewriter.setInsertionPoint(yieldOp);
1453 cir::EndCleanupOp::create(rewriter, loc, beginCleanup.getCleanupToken());
1454 rewriter.replaceOpWithNewOp<cir::ResumeOp>(yieldOp, ehToken);
1456 cleanupOp->emitError(
"Not yet implemented: cleanup region terminated "
1457 "with non-yield operation");
1486 flattenCleanup(cir::CleanupScopeOp cleanupOp,
1487 llvm::SmallVectorImpl<CleanupExit> &exits,
1488 llvm::SmallVectorImpl<cir::CallOp> &callsToRewrite,
1489 llvm::SmallVectorImpl<cir::ThrowOp> &throwsToRewrite,
1490 llvm::SmallVectorImpl<cir::ResumeOp> &resumeOpsToChain,
1491 mlir::PatternRewriter &rewriter)
const {
1492 mlir::Location loc = cleanupOp.getLoc();
1493 cir::CleanupKindAttr cleanupKind = cleanupOp.getCleanupKindAttr();
1494 bool hasNormalCleanup = cleanupKind.isNormal();
1495 bool hasEHCleanup = cleanupKind.isEH();
1496 bool isMultiExit = exits.size() > 1;
1503 bool emitNormalCleanup = hasNormalCleanup && !exits.empty();
1506 mlir::Block *bodyEntry = &cleanupOp.getBodyRegion().front();
1507 mlir::Block *cleanupEntry = &cleanupOp.getCleanupRegion().front();
1508 mlir::Block *cleanupExit = &cleanupOp.getCleanupRegion().back();
1509 assert(regionExitsOnlyFromLastBlock(cleanupOp.getCleanupRegion()) &&
1510 "cleanup region has exits in non-final blocks");
1511 auto cleanupYield = dyn_cast<cir::YieldOp>(cleanupExit->getTerminator());
1512 if (!cleanupYield) {
1513 return rewriter.notifyMatchFailure(cleanupOp,
1514 "Not yet implemented: cleanup region "
1515 "terminated with non-yield operation");
1522 cir::AllocaOp destSlot;
1523 if (isMultiExit && emitNormalCleanup) {
1524 auto funcOp = cleanupOp->getParentOfType<cir::FuncOp>();
1526 return cleanupOp->emitError(
"cleanup scope not inside a function");
1527 destSlot = getOrCreateCleanupDestSlot(funcOp, rewriter, loc);
1531 mlir::Block *currentBlock = rewriter.getInsertionBlock();
1532 mlir::Block *continueBlock =
1533 rewriter.splitBlock(currentBlock, rewriter.getInsertionPoint());
1543 mlir::Block *unwindBlock =
nullptr;
1544 mlir::Block *ehCleanupEntry =
nullptr;
1545 if (hasEHCleanup && (!callsToRewrite.empty() || !throwsToRewrite.empty() ||
1546 !resumeOpsToChain.empty())) {
1548 buildEHCleanupBlocks(cleanupOp, loc, continueBlock, rewriter);
1552 if (!callsToRewrite.empty() || !throwsToRewrite.empty())
1553 unwindBlock = buildUnwindBlock(ehCleanupEntry,
true,
1554 loc, ehCleanupEntry, rewriter);
1561 mlir::Block *normalInsertPt =
1562 unwindBlock ? unwindBlock
1563 : (ehCleanupEntry ? ehCleanupEntry : continueBlock);
1566 rewriter.inlineRegionBefore(cleanupOp.getBodyRegion(), normalInsertPt);
1569 if (emitNormalCleanup)
1570 rewriter.inlineRegionBefore(cleanupOp.getCleanupRegion(), normalInsertPt);
1573 rewriter.setInsertionPointToEnd(currentBlock);
1574 cir::BrOp::create(rewriter, loc, bodyEntry);
1577 mlir::LogicalResult result = mlir::success();
1578 if (emitNormalCleanup) {
1580 mlir::Block *exitBlock = rewriter.createBlock(normalInsertPt);
1583 rewriter.setInsertionPoint(cleanupYield);
1584 rewriter.replaceOpWithNewOp<cir::BrOp>(cleanupYield, exitBlock);
1588 rewriter.setInsertionPointToEnd(exitBlock);
1592 cir::LoadOp::create(rewriter, loc, destSlot,
false,
1594 mlir::IntegerAttr(),
1595 cir::SyncScopeKindAttr(), cir::MemOrderAttr(),
1599 llvm::SmallVector<mlir::APInt, 8> caseValues;
1600 llvm::SmallVector<mlir::Block *, 8> caseDestinations;
1601 llvm::SmallVector<mlir::ValueRange, 8> caseOperands;
1602 cir::IntType s32Type =
1603 cir::IntType::get(rewriter.getContext(), 32,
true);
1605 for (
const CleanupExit &exit : exits) {
1607 mlir::Block *destBlock = rewriter.createBlock(normalInsertPt);
1608 rewriter.setInsertionPointToEnd(destBlock);
1610 createExitTerminator(exit.exitOp, loc, continueBlock, rewriter);
1613 caseValues.push_back(
1614 llvm::APInt(32,
static_cast<uint64_t>(exit.destinationId),
true));
1615 caseDestinations.push_back(destBlock);
1616 caseOperands.push_back(mlir::ValueRange());
1620 rewriter.setInsertionPoint(exit.exitOp);
1621 auto destIdConst = cir::ConstantOp::create(
1622 rewriter, loc, cir::IntAttr::get(s32Type, exit.destinationId));
1623 cir::StoreOp::create(rewriter, loc, destIdConst, destSlot,
1626 mlir::IntegerAttr(),
1627 cir::SyncScopeKindAttr(), cir::MemOrderAttr());
1628 rewriter.replaceOpWithNewOp<cir::BrOp>(exit.exitOp, cleanupEntry);
1636 if (result.failed())
1641 mlir::Block *defaultBlock = rewriter.createBlock(normalInsertPt);
1642 rewriter.setInsertionPointToEnd(defaultBlock);
1643 cir::UnreachableOp::create(rewriter, loc);
1646 rewriter.setInsertionPointToEnd(exitBlock);
1647 cir::SwitchFlatOp::create(rewriter, loc, slotValue, defaultBlock,
1648 mlir::ValueRange(), caseValues,
1649 caseDestinations, caseOperands);
1653 rewriter.setInsertionPointToEnd(exitBlock);
1654 mlir::Operation *exitOp = exits[0].exitOp;
1655 result = createExitTerminator(exitOp, loc, continueBlock, rewriter);
1658 rewriter.setInsertionPoint(exitOp);
1659 rewriter.replaceOpWithNewOp<cir::BrOp>(exitOp, cleanupEntry);
1664 for (CleanupExit &exit : exits) {
1665 if (isa<cir::YieldOp>(exit.exitOp)) {
1666 rewriter.setInsertionPoint(exit.exitOp);
1667 rewriter.replaceOpWithNewOp<cir::BrOp>(exit.exitOp, continueBlock);
1678 for (cir::CallOp callOp : callsToRewrite)
1680 for (cir::ThrowOp throwOp : throwsToRewrite)
1690 if (ehCleanupEntry) {
1691 llvm::SmallVector<cir::CallOp> ehCleanupThrowingCalls;
1692 llvm::SmallVector<cir::ThrowOp> ehCleanupThrows;
1693 for (mlir::Block *block = ehCleanupEntry; block != continueBlock;
1694 block = block->getNextNode()) {
1695 block->walk([&](mlir::Operation *op) {
1696 if (
auto callOp = mlir::dyn_cast<cir::CallOp>(op)) {
1697 if (!callOp.getNothrow())
1698 ehCleanupThrowingCalls.push_back(callOp);
1699 }
else if (
auto throwOp = mlir::dyn_cast<cir::ThrowOp>(op)) {
1700 ehCleanupThrows.push_back(throwOp);
1704 if (!ehCleanupThrowingCalls.empty() || !ehCleanupThrows.empty()) {
1705 mlir::Block *terminateBlock =
1706 buildTerminateUnwindBlock(loc, continueBlock, rewriter);
1707 for (cir::CallOp callOp : ehCleanupThrowingCalls)
1709 for (cir::ThrowOp throwOp : ehCleanupThrows)
1719 if (ehCleanupEntry) {
1720 for (cir::ResumeOp resumeOp : resumeOpsToChain) {
1721 mlir::Value ehToken = resumeOp.getEhToken();
1722 rewriter.setInsertionPoint(resumeOp);
1723 rewriter.replaceOpWithNewOp<cir::BrOp>(
1724 resumeOp, mlir::ValueRange{ehToken},
nullptr,
1725 nullptr, ehCleanupEntry);
1730 rewriter.eraseOp(cleanupOp);
1735 return mlir::success();
1739 matchAndRewrite(cir::CleanupScopeOp cleanupOp,
1740 mlir::PatternRewriter &rewriter)
const override {
1741 mlir::OpBuilder::InsertionGuard guard(rewriter);
1755 llvm::SmallVector<cir::CleanupScopeOp> deadNestedOps;
1756 cleanupOp.getBodyRegion().walk([&](cir::CleanupScopeOp nested) {
1757 if (mlir::isOpTriviallyDead(nested))
1758 deadNestedOps.push_back(nested);
1760 for (
auto op : deadNestedOps)
1761 rewriter.eraseOp(op);
1763 if (hasNestedOpsToFlatten(cleanupOp.getBodyRegion()))
1764 return mlir::failure();
1766 bool hasEHCleanup = cleanupOp.getCleanupKindAttr().isEH();
1769 llvm::SmallVector<CleanupExit> exits;
1771 collectExits(cleanupOp.getBodyRegion(), exits, nextId);
1776 bool hasMustTailCall =
false;
1777 cleanupOp.getBodyRegion().walk(
1778 [&](cir::CallOp callOp) { hasMustTailCall |= callOp.getMusttail(); });
1779 assert((!exits.empty() || hasMustTailCall) &&
1780 "cleanup scope body has no exit");
1786 llvm::SmallVector<cir::CallOp> callsToRewrite;
1787 llvm::SmallVector<cir::ThrowOp> throwsToRewrite;
1788 if (hasEHCleanup && (!isLifetimeMarkerOnly(cleanupOp.getCleanupRegion()) ||
1789 hasEnclosingEHRequirement(cleanupOp))) {
1790 collectThrowingCalls(cleanupOp.getBodyRegion(), callsToRewrite);
1791 collectThrows(cleanupOp.getBodyRegion(), throwsToRewrite);
1797 llvm::SmallVector<cir::ResumeOp> resumeOpsToChain;
1799 collectResumeOps(cleanupOp.getBodyRegion(), resumeOpsToChain);
1801 return flattenCleanup(cleanupOp, exits, callsToRewrite, throwsToRewrite,
1802 resumeOpsToChain, rewriter);
1809static cir::EhInitiateOp traceToEhInitiate(mlir::Value ehToken) {
1811 if (
auto initiate = ehToken.getDefiningOp<cir::EhInitiateOp>())
1813 auto blockArg = mlir::dyn_cast<mlir::BlockArgument>(ehToken);
1816 mlir::Block *pred = blockArg.getOwner()->getSinglePredecessor();
1819 auto brOp = mlir::dyn_cast<cir::BrOp>(pred->getTerminator());
1822 ehToken = brOp.getDestOperands()[blockArg.getArgNumber()];
1827class CIRTryOpFlattening :
public mlir::OpRewritePattern<cir::TryOp> {
1829 using OpRewritePattern<cir::TryOp>::OpRewritePattern;
1834 mlir::Block *buildCatchDispatchBlock(
1835 cir::TryOp tryOp, mlir::ArrayAttr handlerTypes,
1836 llvm::SmallVectorImpl<mlir::Block *> &catchHandlerBlocks,
1837 mlir::Location loc, mlir::Block *insertBefore,
1838 mlir::PatternRewriter &rewriter)
const {
1839 mlir::Block *dispatchBlock = rewriter.createBlock(insertBefore);
1840 auto ehTokenType = cir::EhTokenType::get(rewriter.getContext());
1841 mlir::Value ehToken = dispatchBlock->addArgument(ehTokenType, loc);
1843 rewriter.setInsertionPointToEnd(dispatchBlock);
1846 llvm::SmallVector<mlir::Attribute> catchTypeAttrs;
1847 llvm::SmallVector<mlir::Block *> catchDests;
1848 mlir::Block *defaultDest =
nullptr;
1849 bool defaultIsCatchAll =
false;
1855 cir::EhFilterAttr filterAttr;
1856 mlir::Block *filterUnwindDest =
nullptr;
1857 mlir::Block *filterClauseDest =
nullptr;
1859 for (
auto zipped : llvm::zip(handlerTypes, catchHandlerBlocks)) {
1860 mlir::Attribute typeAttr = std::get<0>(zipped);
1861 mlir::Block *handlerBlock = std::get<1>(zipped);
1862 llvm::TypeSwitch<mlir::Attribute>(typeAttr)
1863 .Case<cir::CatchAllAttr>([&](
auto) {
1864 assert(!defaultDest &&
"multiple catch_all or unwind handlers");
1865 defaultDest = handlerBlock;
1866 defaultIsCatchAll =
true;
1868 .Case<cir::UnwindAttr>([&](
auto) {
1869 assert(!defaultDest &&
"multiple catch_all or unwind handlers");
1870 defaultDest = handlerBlock;
1871 defaultIsCatchAll =
false;
1873 .Case<cir::EhFilterAttr>([&](cir::EhFilterAttr ehFilter) {
1874 assert(!filterAttr &&
"multiple filter handlers");
1875 filterAttr = ehFilter;
1876 filterUnwindDest = handlerBlock;
1878 .Case<cir::EhUnexpectedAttr>([&](
auto) {
1879 assert(!filterClauseDest &&
"multiple unexpected handlers");
1880 filterClauseDest = handlerBlock;
1882 .
Default([&](mlir::Attribute attr) {
1884 catchTypeAttrs.push_back(attr);
1885 catchDests.push_back(handlerBlock);
1890 assert(filterUnwindDest && filterClauseDest &&
1891 "filter handler requires an unexpected handler");
1892 assert(!defaultDest &&
1893 "filter handler cannot share a dispatch with catch_all or unwind");
1894 catchTypeAttrs.push_back(filterAttr);
1895 catchDests.push_back(filterClauseDest);
1896 defaultDest = filterUnwindDest;
1897 defaultIsCatchAll =
false;
1900 assert(defaultDest &&
"dispatch must have a catch_all or unwind handler");
1902 mlir::ArrayAttr catchTypesArrayAttr;
1903 if (!catchTypeAttrs.empty())
1904 catchTypesArrayAttr = rewriter.getArrayAttr(catchTypeAttrs);
1906 cir::EhDispatchOp::create(rewriter, loc, ehToken, catchTypesArrayAttr,
1907 defaultIsCatchAll, defaultDest, catchDests);
1909 return dispatchBlock;
1926 mlir::Block *flattenCatchHandler(mlir::Region &handlerRegion,
1927 mlir::Block *continueBlock,
1929 mlir::Block *insertBefore,
1930 mlir::PatternRewriter &rewriter)
const {
1932 mlir::Block *handlerEntry = &handlerRegion.front();
1935 rewriter.inlineRegionBefore(handlerRegion, insertBefore);
1938 for (mlir::Block &block : llvm::make_range(handlerEntry->getIterator(),
1940 if (
auto yieldOp = dyn_cast<cir::YieldOp>(block.getTerminator())) {
1953 if (mlir::Operation *prev = yieldOp->getPrevNode())
1954 return isa<cir::EndCatchOp>(prev);
1955 llvm::SmallPtrSet<mlir::Block *, 8> visited;
1956 llvm::SmallVector<mlir::Block *, 4> worklist;
1957 for (mlir::Block *pred : block.getPredecessors())
1958 worklist.push_back(pred);
1959 while (!worklist.empty()) {
1960 mlir::Block *b = worklist.pop_back_val();
1961 if (!visited.insert(b).second)
1963 mlir::Operation *term = b->getTerminator();
1964 if (mlir::Operation *prev = term->getPrevNode()) {
1965 if (isa<cir::EndCatchOp>(prev))
1968 for (mlir::Block *pred : b->getPredecessors())
1969 worklist.push_back(pred);
1973 "expected end_catch reachable before yield "
1974 "in catch handler");
1975 rewriter.setInsertionPoint(yieldOp);
1976 rewriter.replaceOpWithNewOp<cir::BrOp>(yieldOp, continueBlock);
1980 return handlerEntry;
1989 mlir::Block *flattenUnwindHandler(mlir::Region &unwindRegion,
1991 mlir::Block *insertBefore,
1992 mlir::PatternRewriter &rewriter)
const {
1993 mlir::Block *unwindEntry = &unwindRegion.front();
1994 rewriter.inlineRegionBefore(unwindRegion, insertBefore);
1999 matchAndRewrite(cir::TryOp tryOp,
2000 mlir::PatternRewriter &rewriter)
const override {
2007 for (mlir::Region ®ion : tryOp->getRegions())
2008 if (hasNestedOpsToFlatten(region))
2009 return mlir::failure();
2011 mlir::OpBuilder::InsertionGuard guard(rewriter);
2012 mlir::Location loc = tryOp.getLoc();
2014 mlir::ArrayAttr handlerTypes = tryOp.getHandlerTypesAttr();
2015 mlir::MutableArrayRef<mlir::Region> handlerRegions =
2016 tryOp.getHandlerRegions();
2019 llvm::SmallVector<cir::CallOp> callsToRewrite;
2020 collectThrowingCalls(tryOp.getTryRegion(), callsToRewrite);
2021 llvm::SmallVector<cir::ThrowOp> throwsToRewrite;
2022 collectThrows(tryOp.getTryRegion(), throwsToRewrite);
2025 llvm::SmallVector<cir::ResumeOp> resumeOpsToChain;
2026 collectResumeOps(tryOp.getTryRegion(), resumeOpsToChain);
2029 mlir::Block *currentBlock = rewriter.getInsertionBlock();
2030 mlir::Block *continueBlock =
2031 rewriter.splitBlock(currentBlock, rewriter.getInsertionPoint());
2034 mlir::Block *bodyEntry = &tryOp.getTryRegion().front();
2035 mlir::Block *bodyExit = &tryOp.getTryRegion().back();
2038 rewriter.inlineRegionBefore(tryOp.getTryRegion(), continueBlock);
2041 rewriter.setInsertionPointToEnd(currentBlock);
2042 cir::BrOp::create(rewriter, loc, bodyEntry);
2045 if (
auto bodyYield = dyn_cast<cir::YieldOp>(bodyExit->getTerminator())) {
2046 rewriter.setInsertionPoint(bodyYield);
2047 rewriter.replaceOpWithNewOp<cir::BrOp>(bodyYield, continueBlock);
2051 if (!handlerTypes || handlerTypes.empty()) {
2052 rewriter.eraseOp(tryOp);
2053 return mlir::success();
2061 if (callsToRewrite.empty() && throwsToRewrite.empty() &&
2062 resumeOpsToChain.empty()) {
2063 for (mlir::Region &handlerRegion : handlerRegions)
2064 for (mlir::Block &block : handlerRegion)
2065 block.dropAllDefinedValueUses();
2066 rewriter.eraseOp(tryOp);
2067 return mlir::success();
2073 llvm::SmallVector<mlir::Block *> catchHandlerBlocks;
2075 for (
const auto &[idx, typeAttr] : llvm::enumerate(handlerTypes)) {
2076 mlir::Region &handlerRegion = handlerRegions[idx];
2078 if (mlir::isa<cir::UnwindAttr, cir::EhFilterAttr, cir::EhUnexpectedAttr>(
2080 mlir::Block *unwindEntry =
2081 flattenUnwindHandler(handlerRegion, loc, continueBlock, rewriter);
2082 catchHandlerBlocks.push_back(unwindEntry);
2084 mlir::Block *handlerEntry = flattenCatchHandler(
2085 handlerRegion, continueBlock, loc, continueBlock, rewriter);
2086 catchHandlerBlocks.push_back(handlerEntry);
2091 mlir::Block *dispatchBlock =
2092 buildCatchDispatchBlock(tryOp, handlerTypes, catchHandlerBlocks, loc,
2093 catchHandlerBlocks.front(), rewriter);
2104 handlerTypes && llvm::any_of(handlerTypes, [](mlir::Attribute attr) {
2105 return mlir::isa<cir::CatchAllAttr>(attr);
2114 bool isCleanupOnly = tryOp.getCleanup() && !hasCatchAll;
2115 if (!callsToRewrite.empty() || !throwsToRewrite.empty()) {
2117 mlir::Block *unwindBlock = buildUnwindBlock(dispatchBlock, isCleanupOnly,
2118 loc, dispatchBlock, rewriter);
2120 for (cir::CallOp callOp : callsToRewrite)
2122 for (cir::ThrowOp throwOp : throwsToRewrite)
2129 for (cir::ResumeOp resumeOp : resumeOpsToChain) {
2134 if (
auto ehInitiate = traceToEhInitiate(resumeOp.getEhToken())) {
2135 rewriter.modifyOpInPlace(ehInitiate,
2136 [&] { ehInitiate.removeCleanupAttr(); });
2140 mlir::Value ehToken = resumeOp.getEhToken();
2141 rewriter.setInsertionPoint(resumeOp);
2142 rewriter.replaceOpWithNewOp<cir::BrOp>(
2143 resumeOp, mlir::ValueRange{ehToken},
nullptr,
2144 nullptr, dispatchBlock);
2148 rewriter.eraseOp(tryOp);
2150 return mlir::success();
2155collectCoReturnsOps(mlir::Region ®ion,
2158 [&](cir::CoReturnOp coReturnOp) { coReturnOps.push_back(coReturnOp); });
2176class CIRCoroutineFlattening :
public mlir::OpRewritePattern<cir::CoroutineOp> {
2180 template <
class PlaceholderOp>
2182 collectAwaitPlaceholders(cir::CoroutineOp coroOp,
2183 llvm::SmallVectorImpl<PlaceholderOp> &ops)
const {
2184 auto collect = [&](PlaceholderOp op) { ops.push_back(op); };
2185 coroOp.getInitialSuspend().walk(collect);
2186 coroOp.getBody().walk(collect);
2187 coroOp.getFinalSuspend().walk(collect);
2191 void replaceYieldWithBr(mlir::PatternRewriter &rewriter, mlir::Region ®ion,
2192 mlir::Block *dest)
const {
2193 auto yieldOp = cast<cir::YieldOp>(region.back().getTerminator());
2194 rewriter.setInsertionPoint(yieldOp);
2195 rewriter.replaceOpWithNewOp<cir::BrOp>(yieldOp, dest, yieldOp.getArgs());
2200 void inlineInitialSuspend(mlir::PatternRewriter &rewriter,
2201 cir::CoroutineOp coroOp, mlir::Block *insertBefore,
2202 mlir::Block *beforeBody)
const {
2203 mlir::Region &initialRegion = coroOp.getInitialSuspend();
2204 replaceYieldWithBr(rewriter, initialRegion, beforeBody);
2205 rewriter.inlineRegionBefore(initialRegion, insertBefore);
2211 void inlineBody(mlir::PatternRewriter &rewriter, cir::CoroutineOp coroOp,
2212 mlir::Block *insertBefore, mlir::Block *beforeFinal)
const {
2213 mlir::Region &bodyRegion = coroOp.getBody();
2215 llvm::SmallVector<cir::CoReturnOp> coReturnsOps;
2216 collectCoReturnsOps(bodyRegion, coReturnsOps);
2218 mlir::Operation *bodyTerminator = bodyRegion.back().getTerminator();
2219 if (
auto bodyYield = dyn_cast<cir::YieldOp>(bodyTerminator)) {
2220 rewriter.setInsertionPoint(bodyYield);
2221 rewriter.replaceOpWithNewOp<cir::BrOp>(bodyYield, beforeFinal,
2222 mlir::ValueRange{});
2225 for (
auto &coReturn : coReturnsOps) {
2226 rewriter.setInsertionPoint(coReturn);
2227 rewriter.replaceOpWithNewOp<cir::BrOp>(coReturn, beforeFinal,
2228 mlir::ValueRange{});
2231 rewriter.inlineRegionBefore(bodyRegion, insertBefore);
2236 void inlineFinalSuspend(mlir::PatternRewriter &rewriter,
2237 cir::CoroutineOp coroOp, mlir::Block *insertBefore,
2238 mlir::Block *beforeDestroy)
const {
2239 mlir::Region &finalRegion = coroOp.getFinalSuspend();
2240 replaceYieldWithBr(rewriter, finalRegion, beforeDestroy);
2241 rewriter.inlineRegionBefore(finalRegion, insertBefore);
2246 void inlineDestroy(mlir::PatternRewriter &rewriter, cir::CoroutineOp coroOp,
2247 mlir::Block *insertBefore, mlir::Block *beforeExit)
const {
2248 mlir::Region &destroyRegion = coroOp.getDestroy();
2249 replaceYieldWithBr(rewriter, destroyRegion, beforeExit);
2250 rewriter.inlineRegionBefore(destroyRegion, insertBefore);
2254 void inlineExit(mlir::PatternRewriter &rewriter, cir::CoroutineOp coroOp,
2255 mlir::Block *insertBefore)
const {
2256 rewriter.inlineRegionBefore(coroOp.getExit(), insertBefore);
2261 void lowerCoroExits(mlir::PatternRewriter &rewriter,
2262 llvm::ArrayRef<cir::CoroExitOp> coroExitsOps,
2263 mlir::Block *beforeExit)
const {
2264 for (cir::CoroExitOp coroExit : coroExitsOps) {
2268 rewriter.setInsertionPoint(coroExit);
2269 rewriter.replaceOpWithNewOp<cir::BrOp>(coroExit, beforeExit,
2270 mlir::ValueRange{});
2278 void lowerCoroCleanups(mlir::PatternRewriter &rewriter,
2279 llvm::ArrayRef<cir::CoroCleanupOp> coroCleanupOps,
2280 mlir::Block *beforeDestroy)
const {
2281 for (cir::CoroCleanupOp coroCleanup : coroCleanupOps) {
2282 rewriter.setInsertionPoint(coroCleanup);
2283 rewriter.replaceOpWithNewOp<cir::BrOp>(coroCleanup, beforeDestroy,
2284 mlir::ValueRange{});
2289 using OpRewritePattern<cir::CoroutineOp>::OpRewritePattern;
2292 matchAndRewrite(cir::CoroutineOp coroOp,
2293 mlir::PatternRewriter &rewriter)
const override {
2296 for (mlir::Region ®ion : coroOp->getRegions())
2297 if (hasNestedOpsToFlatten(region))
2298 return mlir::failure();
2303 llvm::SmallVector<cir::CoroExitOp> coroExitsOps;
2304 collectAwaitPlaceholders(coroOp, coroExitsOps);
2305 llvm::SmallVector<cir::CoroCleanupOp> coroCleanupOps;
2306 collectAwaitPlaceholders(coroOp, coroCleanupOps);
2309 mlir::Block *beforeInitial = &coroOp.getInitialSuspend().front();
2310 mlir::Block *beforeBody = &coroOp.getBody().front();
2311 mlir::Block *beforeFinal = &coroOp.getFinalSuspend().front();
2312 mlir::Block *beforeDestroy = &coroOp.getDestroy().front();
2313 mlir::Block *beforeExit = &coroOp.getExit().front();
2315 mlir::Block *coroBlock = rewriter.getInsertionBlock();
2318 mlir::Block *remainingOpsBlock =
2319 rewriter.splitBlock(coroBlock, rewriter.getInsertionPoint());
2321 lowerCoroExits(rewriter, coroExitsOps, beforeExit);
2322 lowerCoroCleanups(rewriter, coroCleanupOps, beforeDestroy);
2324 inlineInitialSuspend(rewriter, coroOp, remainingOpsBlock, beforeBody);
2325 inlineBody(rewriter, coroOp, remainingOpsBlock, beforeFinal);
2326 inlineFinalSuspend(rewriter, coroOp, remainingOpsBlock, beforeDestroy);
2327 inlineDestroy(rewriter, coroOp, remainingOpsBlock, beforeExit);
2328 inlineExit(rewriter, coroOp, remainingOpsBlock);
2330 rewriter.setInsertionPointToEnd(coroBlock);
2331 cir::BrOp::create(rewriter, coroOp.getLoc(), beforeInitial,
2332 mlir::ValueRange{});
2334 auto func = coroOp->getParentOfType<cir::FuncOp>();
2338 func.setCoroutineKind(cir::CoroutineKind::FlattenedCoroutine);
2340 rewriter.eraseOp(coroOp);
2341 return mlir::success();
2345class CIRAwaitOpFlattening :
public mlir::OpRewritePattern<cir::AwaitOp> {
2350 void flattenReadyRegion(mlir::PatternRewriter &rewriter, cir::AwaitOp awaitOp,
2351 mlir::Block *resumeEntry, mlir::Block *suspendEntry,
2352 mlir::Block *remainingOpsBlock)
const {
2353 mlir::Region &readyRegion = awaitOp.getReady();
2355 cast<cir::ConditionOp>(readyRegion.back().getTerminator());
2356 rewriter.setInsertionPoint(conditionOp);
2357 rewriter.replaceOpWithNewOp<cir::BrCondOp>(
2358 conditionOp, conditionOp.getCondition(), resumeEntry, suspendEntry);
2359 rewriter.inlineRegionBefore(readyRegion, remainingOpsBlock);
2367 void flattenSuspendRegion(mlir::PatternRewriter &rewriter,
2368 cir::AwaitOp awaitOp, mlir::Block *resumeEntry,
2369 mlir::Block *remainingOpsBlock)
const {
2370 mlir::Region &suspendRegion = awaitOp.getSuspend();
2372 cast<cir::CoroSuspendPoint>(suspendRegion.back().getTerminator());
2373 cir::CoroSuspendOp coroSuspendIntri =
2374 emitCoroSuspend(rewriter, awaitOp, suspendPoint);
2378 rewriter.inlineRegionBefore(suspendRegion, remainingOpsBlock);
2380 mlir::Location loc = awaitOp.getLoc();
2381 mlir::Block *cleanupBlock =
2382 createDestroyBlock(rewriter, loc, remainingOpsBlock);
2383 mlir::Block *retBlock =
2384 createSuspendedBlock(rewriter, loc, remainingOpsBlock);
2387 llvm::SmallVector<mlir::APInt> caseValues{mlir::APInt(8, 0),
2389 llvm::SmallVector<mlir::Block *> caseDestinations{resumeEntry,
2391 llvm::SmallVector<mlir::ValueRange> caseOperands{caseValues.size()};
2395 rewriter.setInsertionPoint(suspendPoint);
2396 rewriter.replaceOpWithNewOp<cir::SwitchFlatOp>(
2397 suspendPoint, coroSuspendIntri.getResult(), retBlock,
2398 mlir::ValueRange{}, caseValues, caseDestinations, caseOperands);
2403 cir::CoroSuspendOp emitCoroSuspend(mlir::PatternRewriter &rewriter,
2404 cir::AwaitOp awaitOp,
2405 cir::CoroSuspendPoint suspendPoint)
const {
2406 mlir::Location loc = awaitOp.getLoc();
2410 rewriter.setInsertionPointToStart(&awaitOp.getSuspend().front());
2415 auto voidPtrTy = cir::PointerType::get(cir::VoidType::get(getContext()));
2416 auto nullPtr = cir::ConstantOp::create(
2418 cir::ConstPtrAttr::get(voidPtrTy, rewriter.getI64IntegerAttr(0)));
2419 auto coroSave = cir::CoroSaveOp::create(rewriter, loc, nullPtr);
2420 rewriter.setInsertionPoint(suspendPoint);
2421 if (suspendPoint.getCond() !=
nullptr) {
2422 auto *oldinsertpoint = rewriter.getInsertionBlock();
2423 mlir::Block *suspendBlock = rewriter.splitBlock(
2424 rewriter.getInsertionBlock(), rewriter.getInsertionPoint());
2425 mlir::OpBuilder::InsertionGuard guard(rewriter);
2426 rewriter.setInsertionPointToEnd(oldinsertpoint);
2427 cir::BrCondOp::create(rewriter, loc, suspendPoint.getCond(), suspendBlock,
2428 &awaitOp.getResume().front(), mlir::ValueRange{},
2429 mlir::ValueRange{});
2431 rewriter.setInsertionPoint(suspendPoint);
2437 bool isFinalSuspend = awaitOp.getKind() == cir::AwaitKind::Final;
2438 auto isFinalCoroSuspend = cir::ConstantOp::create(
2439 rewriter, loc, cir::BoolAttr::get(getContext(), isFinalSuspend));
2445 return cir::CoroSuspendOp::create(
2447 mlir::ValueRange{coroSave.getResult(), isFinalCoroSuspend});
2457 mlir::Block *createDestroyBlock(mlir::PatternRewriter &rewriter,
2459 mlir::Block *insertBefore)
const {
2460 mlir::OpBuilder::InsertionGuard guard(rewriter);
2461 mlir::Block *cleanupBlock = rewriter.createBlock(insertBefore);
2462 cir::CoroCleanupOp::create(rewriter, loc);
2463 return cleanupBlock;
2468 mlir::Block *createSuspendedBlock(mlir::PatternRewriter &rewriter,
2470 mlir::Block *insertBefore)
const {
2471 mlir::OpBuilder::InsertionGuard guard(rewriter);
2473 mlir::Block *retBlock = rewriter.createBlock(insertBefore);
2474 cir::CoroExitOp::create(rewriter, loc);
2478 void flattenResumeRegion(mlir::PatternRewriter &rewriter,
2479 cir::AwaitOp awaitOp,
2480 mlir::Block *remainingOpsBlock)
const {
2481 mlir::Region &resumeRegion = awaitOp.getResume();
2482 auto resumeYield = cast<cir::YieldOp>(resumeRegion.back().getTerminator());
2483 rewriter.setInsertionPoint(resumeYield);
2486 rewriter.replaceOpWithNewOp<cir::BrOp>(resumeYield, remainingOpsBlock);
2487 rewriter.inlineRegionBefore(resumeRegion, remainingOpsBlock);
2491 using OpRewritePattern<cir::AwaitOp>::OpRewritePattern;
2515 matchAndRewrite(cir::AwaitOp awaitOp,
2516 mlir::PatternRewriter &rewriter)
const override {
2520 for (mlir::Region ®ion : awaitOp->getRegions())
2521 if (hasNestedOpsToFlatten(region))
2522 return mlir::failure();
2524 mlir::Block *awaitBlock = rewriter.getInsertionBlock();
2527 mlir::Block *remainingOpsBlock =
2528 rewriter.splitBlock(awaitBlock, rewriter.getInsertionPoint());
2531 mlir::Block *readyEntry = &awaitOp.getReady().front();
2532 mlir::Block *suspendEntry = &awaitOp.getSuspend().front();
2533 mlir::Block *resumeEntry = &awaitOp.getResume().front();
2535 flattenReadyRegion(rewriter, awaitOp, resumeEntry, suspendEntry,
2540 rewriter.setInsertionPointToEnd(awaitBlock);
2541 cir::BrOp::create(rewriter, awaitOp.getLoc(), readyEntry);
2543 flattenSuspendRegion(rewriter, awaitOp, resumeEntry, remainingOpsBlock);
2544 flattenResumeRegion(rewriter, awaitOp, remainingOpsBlock);
2546 rewriter.eraseOp(awaitOp);
2547 return mlir::success();
2551void populateFlattenCFGPatterns(RewritePatternSet &patterns) {
2553 .add<CIRIfFlattening, CIRLoopOpInterfaceFlattening, CIRScopeOpFlattening,
2554 CIRSwitchOpFlattening, CIRTernaryOpFlattening,
2555 CIRCleanupScopeOpFlattening, CIRTryOpFlattening,
2556 CIRAwaitOpFlattening, CIRCoroutineFlattening>(patterns.getContext());
2564class MLIRChangedListener final :
public mlir::RewriterBase::Listener {
2565 bool hasChanged =
false;
2568 void reset() { hasChanged =
false; }
2570 bool changed()
const {
return hasChanged; }
2572 void notifyBlockErased(Block *)
override { hasChanged =
true; }
2573 void notifyOperationModified(Operation *)
override { hasChanged =
true; }
2574 void notifyOperationReplaced(Operation *, Operation *)
override {
2577 void notifyOperationReplaced(Operation *, ValueRange)
override {
2580 void notifyOperationErased(Operation *)
override { hasChanged =
true; }
2584 void notifyOperationInserted(Operation *,
2585 mlir::IRRewriter::InsertPoint)
override {
2588 void notifyBlockInserted(Block *, Region *, Region::iterator)
override {
2594void CIRFlattenCFGPass::runOnOperation() {
2595 RewritePatternSet patternList(&getContext());
2596 populateFlattenCFGPatterns(patternList);
2597 FrozenRewritePatternSet patterns(std::move(patternList));
2599 PatternApplicator applicator(patterns);
2602 applicator.applyDefaultCostModel();
2604 mlir::PatternRewriter rewriter(&getContext());
2605 MLIRChangedListener changedListener;
2606 rewriter.setListener(&changedListener);
2609 changedListener.reset();
2616 llvm::SmallVector<Operation *, 16> ops;
2617 getOperation()->walk<mlir::WalkOrder::PostOrder>([&](Operation *op) {
2618 if (isa<IfOp, ScopeOp, SwitchOp, LoopOpInterface, TernaryOp,
2619 CleanupScopeOp, TryOp, AwaitOp, CoroutineOp>(op))
2623 for (mlir::Operation *op : ops) {
2624 rewriter.setInsertionPoint(op);
2625 (void)applicator.matchAndRewrite(op, rewriter);
2627 }
while (changedListener.changed());
2635 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()
static bool coroGetRetObjManager()
static bool coroUnwindRegion()