MLIR 24.0.0git
SCFToControlFlow.cpp
Go to the documentation of this file.
1//===- SCFToControlFlow.cpp - SCF to CF conversion ------------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// This file implements a pass to convert scf.for, scf.if and loop.terminator
10// ops into standard CFG ops.
11//
12//===----------------------------------------------------------------------===//
13
15
21#include "mlir/IR/Builders.h"
22#include "mlir/IR/MLIRContext.h"
26
27namespace mlir {
28#define GEN_PASS_DEF_SCFTOCONTROLFLOWPASS
29#include "mlir/Conversion/Passes.h.inc"
30} // namespace mlir
31
32using namespace mlir;
33using namespace mlir::scf;
34
35namespace {
36
37struct SCFToControlFlowPass
38 : public impl::SCFToControlFlowPassBase<SCFToControlFlowPass> {
39 using Base::Base;
40 void runOnOperation() override;
41};
42
43// Create a CFG subgraph for the loop around its body blocks (if the body
44// contained other loops, they have been already lowered to a flow of blocks).
45// Maintain the invariants that a CFG subgraph created for any loop has a single
46// entry and a single exit, and that the entry/exit blocks are respectively
47// first/last blocks in the parent region. The original loop operation is
48// replaced by the initialization operations that set up the initial value of
49// the loop induction variable (%iv) and computes the loop bounds that are loop-
50// invariant for affine loops. The operations following the original scf.for
51// are split out into a separate continuation (exit) block. A condition block is
52// created before the continuation block. It checks the exit condition of the
53// loop and branches either to the continuation block, or to the first block of
54// the body. The condition block takes as arguments the values of the induction
55// variable followed by loop-carried values. Since it dominates both the body
56// blocks and the continuation block, loop-carried values are visible in all of
57// those blocks. Induction variable modification is appended to the last block
58// of the body (which is the exit block from the body subgraph thanks to the
59// invariant we maintain) along with a branch that loops back to the condition
60// block. Loop-carried values are the loop terminator operands, which are
61// forwarded to the branch.
62//
63// +---------------------------------+
64// | <code before the ForOp> |
65// | <definitions of %init...> |
66// | <compute initial %iv value> |
67// | cf.br cond(%iv, %init...) |
68// +---------------------------------+
69// |
70// -------| |
71// | v v
72// | +--------------------------------+
73// | | cond(%iv, %init...): |
74// | | <compare %iv to upper bound> |
75// | | cf.cond_br %r, body, end |
76// | +--------------------------------+
77// | | |
78// | | -------------|
79// | v |
80// | +--------------------------------+ |
81// | | body-first: | |
82// | | <%init visible by dominance> | |
83// | | <body contents> | |
84// | +--------------------------------+ |
85// | | |
86// | ... |
87// | | |
88// | +--------------------------------+ |
89// | | body-last: | |
90// | | <body contents> | |
91// | | <operands of yield = %yields>| |
92// | | %new_iv =<add step to %iv> | |
93// | | cf.br cond(%new_iv, %yields) | |
94// | +--------------------------------+ |
95// | | |
96// |----------- |--------------------
97// v
98// +--------------------------------+
99// | end: |
100// | <code after the ForOp> |
101// | <%init visible by dominance> |
102// +--------------------------------+
103//
104struct ForLowering : public OpRewritePattern<ForOp> {
105 using OpRewritePattern<ForOp>::OpRewritePattern;
106
107 LogicalResult matchAndRewrite(ForOp forOp,
108 PatternRewriter &rewriter) const override;
109};
110
111// Create a CFG subgraph for the scf.if operation (including its "then" and
112// optional "else" operation blocks). We maintain the invariants that the
113// subgraph has a single entry and a single exit point, and that the entry/exit
114// blocks are respectively the first/last block of the enclosing region. The
115// operations following the scf.if are split into a continuation (subgraph
116// exit) block. The condition is lowered to a chain of blocks that implement the
117// short-circuit scheme. The "scf.if" operation is replaced with a conditional
118// branch to either the first block of the "then" region, or to the first block
119// of the "else" region. In these blocks, "scf.yield" is unconditional branches
120// to the post-dominating block. When the "scf.if" does not return values, the
121// post-dominating block is the same as the continuation block. When it returns
122// values, the post-dominating block is a new block with arguments that
123// correspond to the values returned by the "scf.if" that unconditionally
124// branches to the continuation block. This allows block arguments to dominate
125// any uses of the hitherto "scf.if" results that they replaced. (Inserting a
126// new block allows us to avoid modifying the argument list of an existing
127// block, which is illegal in a conversion pattern). When the "else" region is
128// empty, which is only allowed for "scf.if"s that don't return values, the
129// condition branches directly to the continuation block.
130//
131// CFG for a scf.if with else and without results.
132//
133// +--------------------------------+
134// | <code before the IfOp> |
135// | cf.cond_br %cond, %then, %else |
136// +--------------------------------+
137// | |
138// | --------------|
139// v |
140// +--------------------------------+ |
141// | then: | |
142// | <then contents> | |
143// | cf.br continue | |
144// +--------------------------------+ |
145// | |
146// |---------- |-------------
147// | V
148// | +--------------------------------+
149// | | else: |
150// | | <else contents> |
151// | | cf.br continue |
152// | +--------------------------------+
153// | |
154// ------| |
155// v v
156// +--------------------------------+
157// | continue: |
158// | <code after the IfOp> |
159// +--------------------------------+
160//
161// CFG for a scf.if with results.
162//
163// +--------------------------------+
164// | <code before the IfOp> |
165// | cf.cond_br %cond, %then, %else |
166// +--------------------------------+
167// | |
168// | --------------|
169// v |
170// +--------------------------------+ |
171// | then: | |
172// | <then contents> | |
173// | cf.br dom(%args...) | |
174// +--------------------------------+ |
175// | |
176// |---------- |-------------
177// | V
178// | +--------------------------------+
179// | | else: |
180// | | <else contents> |
181// | | cf.br dom(%args...) |
182// | +--------------------------------+
183// | |
184// ------| |
185// v v
186// +--------------------------------+
187// | dom(%args...): |
188// | cf.br continue |
189// +--------------------------------+
190// |
191// v
192// +--------------------------------+
193// | continue: |
194// | <code after the IfOp> |
195// +--------------------------------+
196//
197struct IfLowering : public OpRewritePattern<IfOp> {
198 using OpRewritePattern<IfOp>::OpRewritePattern;
199
200 LogicalResult matchAndRewrite(IfOp ifOp,
201 PatternRewriter &rewriter) const override;
202};
203
204struct ExecuteRegionLowering : public OpRewritePattern<ExecuteRegionOp> {
205 using OpRewritePattern<ExecuteRegionOp>::OpRewritePattern;
206
207 LogicalResult matchAndRewrite(ExecuteRegionOp op,
208 PatternRewriter &rewriter) const override;
209};
210
211struct ParallelLowering : public OpRewritePattern<mlir::scf::ParallelOp> {
212 using OpRewritePattern<mlir::scf::ParallelOp>::OpRewritePattern;
213
214 LogicalResult matchAndRewrite(mlir::scf::ParallelOp parallelOp,
215 PatternRewriter &rewriter) const override;
216};
217
218/// Create a CFG subgraph for this loop construct. The regions of the loop need
219/// not be a single block anymore (for example, if other SCF constructs that
220/// they contain have been already converted to CFG), but need to be single-exit
221/// from the last block of each region. The operations following the original
222/// WhileOp are split into a new continuation block. Both regions of the WhileOp
223/// are inlined, and their terminators are rewritten to organize the control
224/// flow implementing the loop as follows.
225///
226/// +---------------------------------+
227/// | <code before the WhileOp> |
228/// | cf.br ^before(%operands...) |
229/// +---------------------------------+
230/// |
231/// -------| |
232/// | v v
233/// | +--------------------------------+
234/// | | ^before(%bargs...): |
235/// | | %vals... = <some payload> |
236/// | +--------------------------------+
237/// | |
238/// | ...
239/// | |
240/// | +--------------------------------+
241/// | | ^before-last:
242/// | | %cond = <compute condition> |
243/// | | cf.cond_br %cond, |
244/// | | ^after(%vals...), ^cont |
245/// | +--------------------------------+
246/// | | |
247/// | | -------------|
248/// | v |
249/// | +--------------------------------+ |
250/// | | ^after(%aargs...): | |
251/// | | <body contents> | |
252/// | +--------------------------------+ |
253/// | | |
254/// | ... |
255/// | | |
256/// | +--------------------------------+ |
257/// | | ^after-last: | |
258/// | | %yields... = <some payload> | |
259/// | | cf.br ^before(%yields...) | |
260/// | +--------------------------------+ |
261/// | | |
262/// |----------- |--------------------
263/// v
264/// +--------------------------------+
265/// | ^cont: |
266/// | <code after the WhileOp> |
267/// | <%vals from 'before' region |
268/// | visible by dominance> |
269/// +--------------------------------+
270///
271/// Values are communicated between ex-regions (the groups of blocks that used
272/// to form a region before inlining) through block arguments of their
273/// entry blocks, which are visible in all other dominated blocks. Similarly,
274/// the results of the WhileOp are defined in the 'before' region, which is
275/// required to have a single existing block, and are therefore accessible in
276/// the continuation block due to dominance.
277struct WhileLowering : public OpRewritePattern<WhileOp> {
278 using OpRewritePattern<WhileOp>::OpRewritePattern;
279
280 LogicalResult matchAndRewrite(WhileOp whileOp,
281 PatternRewriter &rewriter) const override;
282};
283
284/// Optimized version of the above for the case of the "after" region merely
285/// forwarding its arguments back to the "before" region (i.e., a "do-while"
286/// loop). This avoid inlining the "after" region completely and branches back
287/// to the "before" entry instead.
288struct DoWhileLowering : public OpRewritePattern<WhileOp> {
289 using OpRewritePattern<WhileOp>::OpRewritePattern;
290
291 LogicalResult matchAndRewrite(WhileOp whileOp,
292 PatternRewriter &rewriter) const override;
293};
294
295/// Lower an `scf.index_switch` operation to a `cf.switch` operation.
296struct IndexSwitchLowering : public OpRewritePattern<IndexSwitchOp> {
297 using Base::Base;
298
299 LogicalResult matchAndRewrite(IndexSwitchOp op,
300 PatternRewriter &rewriter) const override;
301};
302
303/// Lower an `scf.forall` operation to an `scf.parallel` op, assuming that it
304/// has no shared outputs. Ops with shared outputs should be bufferized first.
305/// Specialized lowerings for `scf.forall` (e.g., for GPUs) exist in other
306/// dialects/passes.
307struct ForallLowering : public OpRewritePattern<mlir::scf::ForallOp> {
308 using OpRewritePattern<mlir::scf::ForallOp>::OpRewritePattern;
309
310 LogicalResult matchAndRewrite(mlir::scf::ForallOp forallOp,
311 PatternRewriter &rewriter) const override;
312};
313
314} // namespace
315
316static void copyLLVMDialectAttrs(Operation *from, Operation *to) {
318 llvm::copy_if(from->getDiscardableAttrs(), std::back_inserter(llvmAttrs),
319 [](auto attr) {
320 return isa<LLVM::LLVMDialect>(attr.getValue().getDialect());
321 });
322 to->setDiscardableAttrs(llvmAttrs);
323}
324
325static void propagateLoopAttrs(Operation *scfOp, Operation *brOp) {
326 // Let the CondBranchOp carry the LLVM attributes from the ForOp, such as the
327 // llvm.loop_annotation attribute.
328 // LLVM requires the loop metadata to be attached on the "latch" block. Which
329 // is the back-edge to the header block (conditionBlock)
330 copyLLVMDialectAttrs(scfOp, brOp);
331}
332
333LogicalResult ForLowering::matchAndRewrite(ForOp forOp,
334 PatternRewriter &rewriter) const {
335 Location loc = forOp.getLoc();
336
337 // Start by splitting the block containing the 'scf.for' into two parts.
338 // The part before will get the init code, the part after will be the end
339 // point.
340 auto *initBlock = rewriter.getInsertionBlock();
341 auto initPosition = rewriter.getInsertionPoint();
342 auto *endBlock = rewriter.splitBlock(initBlock, initPosition);
343
344 // Use the first block of the loop body as the condition block since it is the
345 // block that has the induction variable and loop-carried values as arguments.
346 // Split out all operations from the first block into a new block. Move all
347 // body blocks from the loop body region to the region containing the loop.
348 auto *conditionBlock = &forOp.getRegion().front();
349 auto *firstBodyBlock =
350 rewriter.splitBlock(conditionBlock, conditionBlock->begin());
351 auto *lastBodyBlock = &forOp.getRegion().back();
352 rewriter.inlineRegionBefore(forOp.getRegion(), endBlock);
353 auto iv = conditionBlock->getArgument(0);
354
355 // Append the induction variable stepping logic to the last body block and
356 // branch back to the condition block. Loop-carried values are taken from
357 // operands of the loop terminator.
358 Operation *terminator = lastBodyBlock->getTerminator();
359 rewriter.setInsertionPointToEnd(lastBodyBlock);
360 auto step = forOp.getStep();
361 // `scf.for` requires `LB + n*Step` to be representable in the induction
362 // variable type, so the increment cannot wrap in the signed (or, with
363 // `unsignedCmp`, unsigned) interpretation of the bounds.
364 arith::IntegerOverflowFlags overflowFlags =
365 forOp.getUnsignedCmp() ? arith::IntegerOverflowFlags::nuw
366 : arith::IntegerOverflowFlags::nsw;
367 auto stepped =
368 arith::AddIOp::create(rewriter, loc, iv, step, overflowFlags).getResult();
369 if (!stepped)
370 return failure();
371
372 SmallVector<Value, 8> loopCarried;
373 loopCarried.push_back(stepped);
374 loopCarried.append(terminator->operand_begin(), terminator->operand_end());
375 auto branchOp =
376 cf::BranchOp::create(rewriter, loc, conditionBlock, loopCarried);
377
378 propagateLoopAttrs(forOp, branchOp);
379 rewriter.eraseOp(terminator);
380
381 // Compute loop bounds before branching to the condition.
382 rewriter.setInsertionPointToEnd(initBlock);
383 Value lowerBound = forOp.getLowerBound();
384 Value upperBound = forOp.getUpperBound();
385 if (!lowerBound || !upperBound)
386 return failure();
387
388 // The initial values of loop-carried values is obtained from the operands
389 // of the loop operation.
390 SmallVector<Value, 8> destOperands;
391 destOperands.push_back(lowerBound);
392 llvm::append_range(destOperands, forOp.getInitArgs());
393 cf::BranchOp::create(rewriter, loc, conditionBlock, destOperands);
394
395 // With the body block done, we can fill in the condition block.
396 rewriter.setInsertionPointToEnd(conditionBlock);
397 arith::CmpIPredicate predicate = forOp.getUnsignedCmp()
398 ? arith::CmpIPredicate::ult
399 : arith::CmpIPredicate::slt;
400 auto comparison =
401 arith::CmpIOp::create(rewriter, loc, predicate, iv, upperBound);
402
403 cf::CondBranchOp::create(rewriter, loc, comparison, firstBodyBlock,
404 ArrayRef<Value>(), endBlock, ArrayRef<Value>());
405
406 // The result of the loop operation is the values of the condition block
407 // arguments except the induction variable on the last iteration.
408 rewriter.replaceOp(forOp, conditionBlock->getArguments().drop_front());
409 return success();
410}
411
412LogicalResult IfLowering::matchAndRewrite(IfOp ifOp,
413 PatternRewriter &rewriter) const {
414 auto loc = ifOp.getLoc();
415
416 // Start by splitting the block containing the 'scf.if' into two parts.
417 // The part before will contain the condition, the part after will be the
418 // continuation point.
419 auto *condBlock = rewriter.getInsertionBlock();
420 auto opPosition = rewriter.getInsertionPoint();
421 auto *remainingOpsBlock = rewriter.splitBlock(condBlock, opPosition);
422 Block *continueBlock;
423 if (ifOp.getNumResults() == 0) {
424 continueBlock = remainingOpsBlock;
425 } else {
426 continueBlock =
427 rewriter.createBlock(remainingOpsBlock, ifOp.getResultTypes(),
428 SmallVector<Location>(ifOp.getNumResults(), loc));
429 cf::BranchOp::create(rewriter, loc, remainingOpsBlock);
430 }
431
432 // Move blocks from the "then" region to the region containing 'scf.if',
433 // place it before the continuation block, and branch to it.
434 auto &thenRegion = ifOp.getThenRegion();
435 auto *thenBlock = &thenRegion.front();
436 Operation *thenTerminator = thenRegion.back().getTerminator();
437 ValueRange thenTerminatorOperands = thenTerminator->getOperands();
438 rewriter.setInsertionPointToEnd(&thenRegion.back());
439 cf::BranchOp::create(rewriter, loc, continueBlock, thenTerminatorOperands);
440 rewriter.eraseOp(thenTerminator);
441 rewriter.inlineRegionBefore(thenRegion, continueBlock);
442
443 // Move blocks from the "else" region (if present) to the region containing
444 // 'scf.if', place it before the continuation block and branch to it. It
445 // will be placed after the "then" regions.
446 auto *elseBlock = continueBlock;
447 auto &elseRegion = ifOp.getElseRegion();
448 if (!elseRegion.empty()) {
449 elseBlock = &elseRegion.front();
450 Operation *elseTerminator = elseRegion.back().getTerminator();
451 ValueRange elseTerminatorOperands = elseTerminator->getOperands();
452 rewriter.setInsertionPointToEnd(&elseRegion.back());
453 cf::BranchOp::create(rewriter, loc, continueBlock, elseTerminatorOperands);
454 rewriter.eraseOp(elseTerminator);
455 rewriter.inlineRegionBefore(elseRegion, continueBlock);
456 }
457
458 rewriter.setInsertionPointToEnd(condBlock);
459 cf::CondBranchOp::create(rewriter, loc, ifOp.getCondition(), thenBlock,
460 /*trueArgs=*/ArrayRef<Value>(), elseBlock,
461 /*falseArgs=*/ArrayRef<Value>());
462
463 // Ok, we're done!
464 rewriter.replaceOp(ifOp, continueBlock->getArguments());
465 return success();
466}
467
468LogicalResult
469ExecuteRegionLowering::matchAndRewrite(ExecuteRegionOp op,
470 PatternRewriter &rewriter) const {
471 auto loc = op.getLoc();
472
473 auto *condBlock = rewriter.getInsertionBlock();
474 auto opPosition = rewriter.getInsertionPoint();
475 auto *remainingOpsBlock = rewriter.splitBlock(condBlock, opPosition);
476
477 auto &region = op.getRegion();
478 rewriter.setInsertionPointToEnd(condBlock);
479 cf::BranchOp::create(rewriter, loc, &region.front());
480
481 for (Block &block : region) {
482 if (auto terminator = dyn_cast<scf::YieldOp>(block.getTerminator())) {
483 ValueRange terminatorOperands = terminator->getOperands();
484 rewriter.setInsertionPointToEnd(&block);
485 cf::BranchOp::create(rewriter, terminator.getLoc(), remainingOpsBlock,
486 terminatorOperands);
487 rewriter.eraseOp(terminator);
488 }
489 }
490
491 rewriter.inlineRegionBefore(region, remainingOpsBlock);
492
493 SmallVector<Value> vals;
494 SmallVector<Location> argLocs(op.getNumResults(), op->getLoc());
495 for (auto arg :
496 remainingOpsBlock->addArguments(op->getResultTypes(), argLocs))
497 vals.push_back(arg);
498 rewriter.replaceOp(op, vals);
499 return success();
500}
501
502LogicalResult
503ParallelLowering::matchAndRewrite(ParallelOp parallelOp,
504 PatternRewriter &rewriter) const {
505 Location loc = parallelOp.getLoc();
506 auto reductionOp = dyn_cast<ReduceOp>(parallelOp.getBody()->getTerminator());
507 if (!reductionOp) {
508 return failure();
509 }
510
511 // For a parallel loop, we essentially need to create an n-dimensional loop
512 // nest. We do this by translating to scf.for ops and have those lowered in
513 // a further rewrite. If a parallel loop contains reductions (and thus returns
514 // values), forward the initial values for the reductions down the loop
515 // hierarchy and bubble up the results by modifying the "yield" terminator.
516 SmallVector<Value, 4> iterArgs = llvm::to_vector<4>(parallelOp.getInitVals());
517 SmallVector<Value, 4> ivs;
518 ivs.reserve(parallelOp.getNumLoops());
519 bool first = true;
520 SmallVector<Value, 4> loopResults(iterArgs);
521 ForOp innermostForOp;
522 for (auto [iv, lower, upper, step] :
523 llvm::zip(parallelOp.getInductionVars(), parallelOp.getLowerBound(),
524 parallelOp.getUpperBound(), parallelOp.getStep())) {
525 ForOp forOp =
526 ForOp::create(rewriter, loc, lower, upper, step, iterArgs,
527 /*bodyBuilder=*/nullptr, parallelOp.getUnsignedCmp());
528 innermostForOp = forOp;
529 ivs.push_back(forOp.getInductionVar());
530 auto iterRange = forOp.getRegionIterArgs();
531 iterArgs.assign(iterRange.begin(), iterRange.end());
532
533 if (first) {
534 // Store the results of the outermost loop that will be used to replace
535 // the results of the parallel loop when it is fully rewritten.
536 loopResults.assign(forOp.result_begin(), forOp.result_end());
537 first = false;
538 } else if (!forOp.getResults().empty()) {
539 // A loop is constructed with an empty "yield" terminator if there are
540 // no results.
541 rewriter.setInsertionPointToEnd(rewriter.getInsertionBlock());
542 scf::YieldOp::create(rewriter, loc, forOp.getResults());
543 }
544
545 rewriter.setInsertionPointToStart(forOp.getBody());
546 }
547
548 // Serializing into the for nest would drop attributes such as
549 // llvm.loop_annotation. The loop above emits one scf.for per dimension, so
550 // the attributes go to the innermost one, which runs the body.
551 if (innermostForOp)
552 copyLLVMDialectAttrs(parallelOp, innermostForOp);
553
554 // First, merge reduction blocks into the main region.
555 SmallVector<Value> yieldOperands;
556 yieldOperands.reserve(parallelOp.getNumResults());
557 for (int64_t i = 0, e = parallelOp.getNumResults(); i < e; ++i) {
558 Block &reductionBody = reductionOp.getReductions()[i].front();
559 Value arg = iterArgs[yieldOperands.size()];
560 yieldOperands.push_back(
561 cast<ReduceReturnOp>(reductionBody.getTerminator()).getResult());
562 rewriter.eraseOp(reductionBody.getTerminator());
563 rewriter.inlineBlockBefore(&reductionBody, reductionOp,
564 {arg, reductionOp.getOperands()[i]});
565 }
566 rewriter.eraseOp(reductionOp);
567
568 // Then merge the loop body without the terminator.
569 Block *newBody = rewriter.getInsertionBlock();
570 if (newBody->empty())
571 rewriter.mergeBlocks(parallelOp.getBody(), newBody, ivs);
572 else
573 rewriter.inlineBlockBefore(parallelOp.getBody(), newBody->getTerminator(),
574 ivs);
575
576 // Finally, create the terminator if required (for loops with no results, it
577 // has been already created in loop construction).
578 if (!yieldOperands.empty()) {
579 rewriter.setInsertionPointToEnd(rewriter.getInsertionBlock());
580 scf::YieldOp::create(rewriter, loc, yieldOperands);
581 }
582
583 rewriter.replaceOp(parallelOp, loopResults);
584
585 return success();
586}
587
588LogicalResult WhileLowering::matchAndRewrite(WhileOp whileOp,
589 PatternRewriter &rewriter) const {
590 OpBuilder::InsertionGuard guard(rewriter);
591 Location loc = whileOp.getLoc();
592
593 // Split the current block before the WhileOp to create the inlining point.
594 Block *currentBlock = rewriter.getInsertionBlock();
595 Block *continuation =
596 rewriter.splitBlock(currentBlock, rewriter.getInsertionPoint());
597
598 // Inline both regions.
599 Block *after = whileOp.getAfterBody();
600 Block *before = whileOp.getBeforeBody();
601 rewriter.inlineRegionBefore(whileOp.getAfter(), continuation);
602 rewriter.inlineRegionBefore(whileOp.getBefore(), after);
603
604 // Branch to the "before" region.
605 rewriter.setInsertionPointToEnd(currentBlock);
606 cf::BranchOp::create(rewriter, loc, before, whileOp.getInits());
607
608 // Replace terminators with branches. Assuming bodies are SESE, which holds
609 // given only the patterns from this file, we only need to look at the last
610 // block. This should be reconsidered if we allow break/continue in SCF.
611 rewriter.setInsertionPointToEnd(before);
612 auto condOp = cast<ConditionOp>(before->getTerminator());
613 SmallVector<Value> args = llvm::to_vector(condOp.getArgs());
614 rewriter.replaceOpWithNewOp<cf::CondBranchOp>(condOp, condOp.getCondition(),
615 after, condOp.getArgs(),
616 continuation, ValueRange());
617
618 rewriter.setInsertionPointToEnd(after);
619 auto yieldOp = cast<scf::YieldOp>(after->getTerminator());
620 auto latch = rewriter.replaceOpWithNewOp<cf::BranchOp>(yieldOp, before,
621 yieldOp.getResults());
622
623 propagateLoopAttrs(whileOp, latch);
624 // Replace the op with values "yielded" from the "before" region, which are
625 // visible by dominance.
626 rewriter.replaceOp(whileOp, args);
627
628 return success();
629}
630
631LogicalResult
632DoWhileLowering::matchAndRewrite(WhileOp whileOp,
633 PatternRewriter &rewriter) const {
634 Block &afterBlock = *whileOp.getAfterBody();
635 if (!llvm::hasSingleElement(afterBlock))
636 return rewriter.notifyMatchFailure(whileOp,
637 "do-while simplification applicable "
638 "only if 'after' region has no payload");
639
640 auto yield = dyn_cast<scf::YieldOp>(&afterBlock.front());
641 if (!yield || yield.getResults() != afterBlock.getArguments())
642 return rewriter.notifyMatchFailure(whileOp,
643 "do-while simplification applicable "
644 "only to forwarding 'after' regions");
645
646 // Split the current block before the WhileOp to create the inlining point.
647 OpBuilder::InsertionGuard guard(rewriter);
648 Block *currentBlock = rewriter.getInsertionBlock();
649 Block *continuation =
650 rewriter.splitBlock(currentBlock, rewriter.getInsertionPoint());
651
652 // Only the "before" region should be inlined.
653 Block *before = whileOp.getBeforeBody();
654 rewriter.inlineRegionBefore(whileOp.getBefore(), continuation);
655
656 // Branch to the "before" region.
657 rewriter.setInsertionPointToEnd(currentBlock);
658 cf::BranchOp::create(rewriter, whileOp.getLoc(), before, whileOp.getInits());
659
660 // Loop around the "before" region based on condition.
661 rewriter.setInsertionPointToEnd(before);
662 auto condOp = cast<ConditionOp>(before->getTerminator());
663 auto latch = cf::CondBranchOp::create(
664 rewriter, condOp.getLoc(), condOp.getCondition(), before,
665 condOp.getArgs(), continuation, ValueRange());
666
667 propagateLoopAttrs(whileOp, latch);
668 // Replace the op with values "yielded" from the "before" region, which are
669 // visible by dominance.
670 rewriter.replaceOp(whileOp, condOp.getArgs());
671
672 // Erase the condition op.
673 rewriter.eraseOp(condOp);
674 return success();
675}
676
677LogicalResult
678IndexSwitchLowering::matchAndRewrite(IndexSwitchOp op,
679 PatternRewriter &rewriter) const {
680 // Split the block at the op.
681 Block *condBlock = rewriter.getInsertionBlock();
682 Block *continueBlock = rewriter.splitBlock(condBlock, Block::iterator(op));
683
684 // Create the arguments on the continue block with which to replace the
685 // results of the op.
686 SmallVector<Value> results;
687 results.reserve(op.getNumResults());
688 for (Type resultType : op.getResultTypes())
689 results.push_back(continueBlock->addArgument(resultType, op.getLoc()));
690
691 // Handle the regions.
692 auto convertRegion = [&](Region &region) -> FailureOr<Block *> {
693 Block *block = &region.front();
694
695 // Convert the yield terminator to a branch to the continue block.
696 auto yield = cast<scf::YieldOp>(block->getTerminator());
697 rewriter.setInsertionPoint(yield);
698 rewriter.replaceOpWithNewOp<cf::BranchOp>(yield, continueBlock,
699 yield.getOperands());
700
701 // Inline the region.
702 rewriter.inlineRegionBefore(region, continueBlock);
703 return block;
704 };
705
706 // Convert the case regions.
707 SmallVector<Block *> caseSuccessors;
708 SmallVector<APInt> caseValues;
709 caseSuccessors.reserve(op.getCases().size());
710 caseValues.reserve(op.getCases().size());
711 for (auto [region, value] : llvm::zip(op.getCaseRegions(), op.getCases())) {
712 FailureOr<Block *> block = convertRegion(region);
713 if (failed(block))
714 return failure();
715 caseSuccessors.push_back(*block);
716 caseValues.push_back(APInt(64, value));
717 }
718
719 // Convert the default region.
720 FailureOr<Block *> defaultBlock = convertRegion(op.getDefaultRegion());
721 if (failed(defaultBlock))
722 return failure();
723
724 // Create the switch.
725 rewriter.setInsertionPointToEnd(condBlock);
726 SmallVector<ValueRange> caseOperands(caseSuccessors.size(), {});
727
728 // Cast switch index to i64 to avoid truncation for large case values.
729 Value caseValue = arith::IndexCastOp::create(
730 rewriter, op.getLoc(), rewriter.getI64Type(), op.getArg());
731
732 cf::SwitchOp::create(rewriter, op.getLoc(), caseValue, *defaultBlock,
733 ValueRange(), caseValues, caseSuccessors, caseOperands);
734 rewriter.replaceOp(op, continueBlock->getArguments());
735 return success();
736}
737
738LogicalResult ForallLowering::matchAndRewrite(ForallOp forallOp,
739 PatternRewriter &rewriter) const {
740 return scf::forallToParallelLoop(rewriter, forallOp);
741}
742
744 RewritePatternSet &patterns) {
745 patterns.add<ForallLowering, ForLowering, IfLowering, ParallelLowering,
746 WhileLowering, ExecuteRegionLowering, IndexSwitchLowering>(
747 patterns.getContext());
748 patterns.add<DoWhileLowering>(patterns.getContext(), /*benefit=*/2);
749}
750
751void SCFToControlFlowPass::runOnOperation() {
752 RewritePatternSet patterns(&getContext());
754
755 // Configure conversion to lower out SCF operations.
757 target.addIllegalOp<scf::ForallOp, scf::ForOp, scf::IfOp, scf::IndexSwitchOp,
758 scf::ParallelOp, scf::WhileOp, scf::ExecuteRegionOp>();
759 target.markUnknownOpDynamicallyLegal([](Operation *) { return true; });
760 ConversionConfig config;
761 config.allowPatternRollback = allowPatternRollback;
762 if (failed(applyPartialConversion(getOperation(), target, std::move(patterns),
763 config)))
764 signalPassFailure();
765}
return success()
b getContext())
static void propagateLoopAttrs(Operation *scfOp, Operation *brOp)
static void copyLLVMDialectAttrs(Operation *from, Operation *to)
OpListType::iterator iterator
Definition Block.h:165
bool empty()
Definition Block.h:173
Operation & front()
Definition Block.h:178
Operation & back()
Definition Block.h:177
Operation * getTerminator()
Get the terminator operation of this block.
Definition Block.cpp:249
BlockArgument addArgument(Type type, Location loc)
Add one value to the argument list.
Definition Block.cpp:158
BlockArgListType getArguments()
Definition Block.h:112
IntegerType getI64Type()
Definition Builders.cpp:73
Block::iterator getInsertionPoint() const
Returns the current insertion point of the builder.
Definition Builders.h:448
Block * createBlock(Region *parent, Region::iterator insertPt={}, TypeRange argTypes={}, ArrayRef< Location > locs={})
Add new block with 'argTypes' arguments and set the insertion point to the end of it.
Definition Builders.cpp:439
void setInsertionPointToStart(Block *block)
Sets the insertion point to the start of the specified block.
Definition Builders.h:434
void setInsertionPoint(Block *block, Block::iterator insertPoint)
Set the insertion point to the specified location.
Definition Builders.h:401
void setInsertionPointToEnd(Block *block)
Sets the insertion point to the end of the specified block.
Definition Builders.h:439
Block * getInsertionBlock() const
Return the block the current insertion point belongs to.
Definition Builders.h:445
Operation is the basic unit of execution within MLIR.
Definition Operation.h:87
operand_iterator operand_begin()
Definition Operation.h:399
Location getLoc()
The source location the operation was defined or derived from.
Definition Operation.h:240
operand_iterator operand_end()
Definition Operation.h:400
auto getDiscardableAttrs()
Return a range of all of discardable attributes on this operation.
Definition Operation.h:538
operand_range getOperands()
Returns an iterator on the underlying Value's.
Definition Operation.h:403
void setDiscardableAttrs(DictionaryAttr newAttrs)
Set the discardable attribute dictionary on this operation.
Definition Operation.h:575
A special type of RewriterBase that coordinates the application of a rewrite pattern on the current I...
MLIRContext * getContext() const
RewritePatternSet & add(ConstructorArg &&arg, ConstructorArgs &&...args)
Add an instance of each of the pattern types 'Ts' to the pattern list with the given arguments.
Block * splitBlock(Block *block, Block::iterator before)
Split the operations starting at "before" (inclusive) out of the given block into a new block,...
virtual void replaceOp(Operation *op, ValueRange newValues)
Replace the results of the given (original) operation with the specified list of values (replacements...
virtual void eraseOp(Operation *op)
This method erases an operation that is known to have no uses.
virtual void inlineBlockBefore(Block *source, Block *dest, Block::iterator before, ValueRange argValues={})
Inline the operations of block 'source' into block 'dest' before the given position.
void mergeBlocks(Block *source, Block *dest, ValueRange argValues={})
Inline the operations of block 'source' into the end of block 'dest'.
std::enable_if_t<!std::is_convertible< CallbackT, Twine >::value, LogicalResult > notifyMatchFailure(Location loc, CallbackT &&reasonCallback)
Used to notify the listener that the IR failed to be rewritten because of a match failure,...
void inlineRegionBefore(Region &region, Region &parent, Region::iterator before)
Move the blocks that belong to "region" before the given position in another region "parent".
OpTy replaceOpWithNewOp(Operation *op, Args &&...args)
Replace the results of the given (original) op with a new op that is created without verification (re...
detail::InFlightRemark failed(Location loc, RemarkOpts opts)
Report an optimization remark that failed.
Definition Remarks.h:734
LogicalResult forallToParallelLoop(RewriterBase &rewriter, ForallOp forallOp, ParallelOp *result=nullptr)
Try converting scf.forall into an scf.parallel loop.
Include the generated interface declarations.
void populateSCFToControlFlowConversionPatterns(RewritePatternSet &patterns)
Collect a set of patterns to convert SCF operations to CFG branch-based operations within the Control...
LogicalResult matchAndRewrite(WhileOp whileOp, OpAdaptor adaptor, ConversionPatternRewriter &rewriter) const override
OpRewritePattern is a wrapper around RewritePattern that allows for matching and rewriting against an...
OpRewritePattern(MLIRContext *context, PatternBenefit benefit=1, ArrayRef< StringRef > generatedNames={})