|
14 | 14 | #include "mlir/IR/IRMapping.h"
|
15 | 15 | #include "mlir/IR/IntegerSet.h"
|
16 | 16 | #include "mlir/IR/Matchers.h"
|
| 17 | +#include "mlir/IR/RegionGraphTraits.h" |
17 | 18 | #include "mlir/IR/SymbolTable.h"
|
| 19 | +#include "llvm/ADT/PostOrderIterator.h" |
| 20 | +#include "llvm/ADT/SetVector.h" |
| 21 | +#include "llvm/ADT/SmallPtrSet.h" |
18 | 22 | #include "llvm/ADT/SmallVectorExtras.h"
|
19 | 23 | #include "llvm/Support/raw_ostream.h"
|
20 | 24 |
|
@@ -525,22 +529,95 @@ LogicalResult OpBuilder::tryFold(Operation *op,
|
525 | 529 | return success();
|
526 | 530 | }
|
527 | 531 |
|
| 532 | +static void notifyOperationCloned(Operation *op, OpBuilder::Listener *listener); |
| 533 | + |
| 534 | +/// Notify the listener that the given range of regions was cloned. |
| 535 | +/// |
| 536 | +/// Blocks and operations are enumerated in an order in which they could have |
| 537 | +/// been created separately (without using the `clone` API): Within a region, |
| 538 | +/// blocks are notified according to their successor relationship. Within a |
| 539 | +/// block, operations are notified in forward mode. This ensures that defining |
| 540 | +/// ops are notified before ops that use their results (unless there are |
| 541 | +/// cycles/graph regions). |
| 542 | +static void notifyRegionsCloned(iterator_range<Region::iterator> range, |
| 543 | + OpBuilder::Listener *listener) { |
| 544 | + // Maintain a set of blocks that were not notified yet. This is needed because |
| 545 | + // the inverse_post_order iterator does not enumerate dead blocks. |
| 546 | + llvm::SetVector<Block *> remainingBlocks; |
| 547 | + // The order in which the set is initialized does not matter for correctness. |
| 548 | + // For better performance, "leaf" blocks with no successors should be starting |
| 549 | + // point for the block traversal. (Then there are fewer iterations of the |
| 550 | + // "while" loop.) |
| 551 | + for (Block &b : llvm::reverse(range)) |
| 552 | + remainingBlocks.insert(&b); |
| 553 | + // Set of visited blocks that is shared among all inverse_post_order |
| 554 | + // iterations. This is to avoid that the same block is enumerated multiple |
| 555 | + // times. |
| 556 | + llvm::SmallPtrSet<Block *, 4> visited; |
| 557 | + while (!remainingBlocks.empty()) { |
| 558 | + // Enumerate predecessors before successors. I.e., reverse post-order. |
| 559 | + for (Block *b : |
| 560 | + llvm::inverse_post_order_ext(remainingBlocks.front(), visited)) { |
| 561 | + auto it = llvm::find(remainingBlocks, b); |
| 562 | + assert(it != remainingBlocks.end() && |
| 563 | + "expected that only remaining blocks are visited"); |
| 564 | + listener->notifyBlockCreated(b); |
| 565 | + remainingBlocks.erase(it); |
| 566 | + for (Operation &op : *b) |
| 567 | + notifyOperationCloned(&op, listener); |
| 568 | + } |
| 569 | + } |
| 570 | +} |
| 571 | + |
| 572 | +/// Notify the listener that the given op was cloned. |
| 573 | +static void notifyOperationCloned(Operation *op, |
| 574 | + OpBuilder::Listener *listener) { |
| 575 | + listener->notifyOperationInserted(op); |
| 576 | + for (Region &r : op->getRegions()) |
| 577 | + notifyRegionsCloned(r.getBlocks(), listener); |
| 578 | +} |
| 579 | + |
528 | 580 | Operation *OpBuilder::clone(Operation &op, IRMapping &mapper) {
|
| 581 | + // TODO: The listener notifications should be interleaved with `clone`. |
529 | 582 | Operation *newOp = op.clone(mapper);
|
530 |
| - // The `insert` call below handles the notification for inserting `newOp` |
531 |
| - // itself. But if `newOp` has any regions, we need to notify the listener |
532 |
| - // about any ops that got inserted inside those regions as part of cloning. |
533 |
| - if (listener) { |
534 |
| - auto walkFn = [&](Operation *walkedOp) { |
535 |
| - listener->notifyOperationInserted(walkedOp); |
536 |
| - }; |
537 |
| - for (Region ®ion : newOp->getRegions()) |
538 |
| - region.walk(walkFn); |
539 |
| - } |
| 583 | + |
| 584 | + // Fast path: If no listener is attached, the op can be inserted directly. |
| 585 | + if (!listener) |
| 586 | + return insert(newOp); |
| 587 | + |
| 588 | + // The `insert` call below handles the notification for inserting `newOp`. |
| 589 | + // Just notify about nested op/block insertion. |
| 590 | + for (Region &r : newOp->getRegions()) |
| 591 | + notifyRegionsCloned(r.getBlocks(), listener); |
540 | 592 | return insert(newOp);
|
541 | 593 | }
|
542 | 594 |
|
543 | 595 | Operation *OpBuilder::clone(Operation &op) {
|
544 | 596 | IRMapping mapper;
|
545 | 597 | return clone(op, mapper);
|
546 | 598 | }
|
| 599 | + |
| 600 | +void OpBuilder::cloneRegionBefore(Region ®ion, Region &parent, |
| 601 | + Region::iterator before, IRMapping &mapping) { |
| 602 | + // TODO: The listener notifications should be interleaved with `clone`. |
| 603 | + region.cloneInto(&parent, before, mapping); |
| 604 | + |
| 605 | + // Fast path: If no listener is attached, there is no more work to do. |
| 606 | + if (!listener) |
| 607 | + return; |
| 608 | + |
| 609 | + // Notify about op/block insertion. |
| 610 | + Region::iterator clonedBeginIt = |
| 611 | + mapping.lookup(®ion.front())->getIterator(); |
| 612 | + notifyRegionsCloned(llvm::make_range(clonedBeginIt, before), listener); |
| 613 | +} |
| 614 | + |
| 615 | +void OpBuilder::cloneRegionBefore(Region ®ion, Region &parent, |
| 616 | + Region::iterator before) { |
| 617 | + IRMapping mapping; |
| 618 | + cloneRegionBefore(region, parent, before, mapping); |
| 619 | +} |
| 620 | + |
| 621 | +void OpBuilder::cloneRegionBefore(Region ®ion, Block *before) { |
| 622 | + cloneRegionBefore(region, *before->getParent(), before->getIterator()); |
| 623 | +} |
0 commit comments