-
Notifications
You must be signed in to change notification settings - Fork 14.6k
[mlir][IR] Adjust insertion block when splitting blocks / moving ops #150819
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
@llvm/pr-subscribers-mlir-gpu @llvm/pr-subscribers-mlir Author: Matthias Springer (matthias-springer) ChangesSimilar to #146955, adjust the insertion block when splitting a block or moving an operation. This is the ensure that the insertion point is not left in an invalid state. Full diff: https://github.com/llvm/llvm-project/pull/150819.diff 2 Files Affected:
diff --git a/mlir/include/mlir/IR/PatternMatch.h b/mlir/include/mlir/IR/PatternMatch.h
index b5a93a0c5a898..fa87d6987c52c 100644
--- a/mlir/include/mlir/IR/PatternMatch.h
+++ b/mlir/include/mlir/IR/PatternMatch.h
@@ -576,24 +576,39 @@ class RewriterBase : public OpBuilder {
/// Split the operations starting at "before" (inclusive) out of the given
/// block into a new block, and return it.
+ ///
+ /// If the current insertion point is before the split point, the insertion
+ /// point is adjusted to the new block.
Block *splitBlock(Block *block, Block::iterator before);
/// Unlink this operation from its current block and insert it right before
/// `existingOp` which may be in the same or another block in the same
/// function.
+ ///
+ /// If the insertion point is before the moved operation, the insertion block
+ /// is adjusted to the block of `existingOp`.
void moveOpBefore(Operation *op, Operation *existingOp);
/// Unlink this operation from its current block and insert it right before
/// `iterator` in the specified block.
+ ///
+ /// If the insertion point is before the moved operation, the insertion block
+ /// is adjusted to the specified block.
void moveOpBefore(Operation *op, Block *block, Block::iterator iterator);
/// Unlink this operation from its current block and insert it right after
/// `existingOp` which may be in the same or another block in the same
/// function.
+ ///
+ /// If the insertion point is before the moved operation, the insertion block
+ /// is adjusted to the block of `existingOp`.
void moveOpAfter(Operation *op, Operation *existingOp);
/// Unlink this operation from its current block and insert it right after
/// `iterator` in the specified block.
+ ///
+ /// If the insertion point is before the moved operation, the insertion block
+ /// is adjusted to the specified block.
void moveOpAfter(Operation *op, Block *block, Block::iterator iterator);
/// Unlink this block and insert it right before `existingBlock`.
diff --git a/mlir/lib/IR/PatternMatch.cpp b/mlir/lib/IR/PatternMatch.cpp
index 9332f55bd9393..016569718275e 100644
--- a/mlir/lib/IR/PatternMatch.cpp
+++ b/mlir/lib/IR/PatternMatch.cpp
@@ -9,6 +9,7 @@
#include "mlir/IR/PatternMatch.h"
#include "mlir/IR/Iterators.h"
#include "mlir/IR/RegionKindInterface.h"
+#include "llvm/ADT/ScopeExit.h"
#include "llvm/ADT/SmallPtrSet.h"
using namespace mlir;
@@ -348,14 +349,21 @@ void RewriterBase::mergeBlocks(Block *source, Block *dest,
/// Split the operations starting at "before" (inclusive) out of the given
/// block into a new block, and return it.
Block *RewriterBase::splitBlock(Block *block, Block::iterator before) {
+ Block *newBlock;
+ auto adjustInsertionPoint = llvm::make_scope_exit([&]() {
+ // If the current insertion point is before the split point, adjust the
+ // insertion point to the new block.
+ if (getInsertionPoint() == before)
+ setInsertionPoint(newBlock, before);
+ });
+
// Fast path: If no listener is attached, split the block directly.
if (!listener)
- return block->splitBlock(before);
+ return newBlock = block->splitBlock(before);
// `createBlock` sets the insertion point at the beginning of the new block.
InsertionGuard g(*this);
- Block *newBlock =
- createBlock(block->getParent(), std::next(block->getIterator()));
+ newBlock = createBlock(block->getParent(), std::next(block->getIterator()));
// If `before` points to end of the block, no ops should be moved.
if (before == block->end())
@@ -413,6 +421,12 @@ void RewriterBase::moveOpBefore(Operation *op, Block *block,
Block *currentBlock = op->getBlock();
Block::iterator nextIterator = std::next(op->getIterator());
op->moveBefore(block, iterator);
+
+ // If the current insertion point is before the moved operation, we may have
+ // to adjust the insertion block.
+ if (getInsertionPoint() == op->getIterator())
+ setInsertionPoint(block, op->getIterator());
+
if (listener)
listener->notifyOperationInserted(
op, /*previous=*/InsertPoint(currentBlock, nextIterator));
|
7adbc4c
to
98fdd7c
Compare
98fdd7c
to
36065f5
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
This PR improves the robustness of MLIR's rewriter infrastructure by ensuring that insertion points remain valid when splitting blocks or moving operations. The changes prevent the insertion point from being left in an invalid state during these transformations.
Key changes:
- Add insertion point adjustment logic to
splitBlock()
andmoveOpBefore()
methods - Introduce a new
Block::isBeforeInBlock()
method for iterator comparison - Replace duplicate iterator comparison logic with calls to the new centralized method
Reviewed Changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 2 comments.
Show a summary per file
File | Description |
---|---|
mlir/lib/IR/PatternMatch.cpp | Implements insertion point adjustment in splitBlock() and moveOpBefore() methods |
mlir/include/mlir/IR/PatternMatch.h | Updates documentation for move/split methods to describe insertion point behavior |
mlir/lib/IR/Block.cpp | Implements new isBeforeInBlock() method for iterator comparison |
mlir/include/mlir/IR/Block.h | Declares the new isBeforeInBlock() method |
mlir/lib/IR/Dominance.cpp | Replaces local iterator comparison logic with calls to Block::isBeforeInBlock() |
mlir/lib/Dialect/GPU/Transforms/AllReduceLowering.cpp | Adds explicit insertion point adjustment after block splitting |
if (!listener) | ||
return block->splitBlock(before); | ||
return newBlock = block->splitBlock(before); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[nitpick] The assignment within the return statement makes the code harder to read. Consider separating the assignment from the return statement for clarity.
Copilot uses AI. Check for mistakes.
// of the new block. | ||
setInsertionPointToEnd(newBlock); | ||
} else if (moveIpToNewBlock) { | ||
setInsertionPoint(newBlock, getInsertionPoint()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using getInsertionPoint()
as the second argument may reference an iterator from the original block, which could be invalid after the split. The iterator should be adjusted to reference the corresponding position in the new block.
Copilot uses AI. Check for mistakes.
36065f5
to
b7bd595
Compare
/// Return if the iterator `a` is before `b`. Both iterators must point into | ||
/// this block. | ||
bool isBeforeInBlock(iterator a, iterator b); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
/// Return if the iterator `a` is before `b`. Both iterators must point into | |
/// this block. | |
bool isBeforeInBlock(iterator a, iterator b); | |
/// Return if the iterator 'a' is before 'b'. Both iterators must point into | |
/// this block. | |
bool isBeforeInBlock(iterator a, iterator b); |
@@ -576,24 +576,39 @@ class RewriterBase : public OpBuilder { | |||
|
|||
/// Split the operations starting at "before" (inclusive) out of the given | |||
/// block into a new block, and return it. | |||
/// | |||
/// If the current insertion point is before the split point, the insertion | |||
/// point is adjusted to the new block. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this could be made clearer in that the insertion point is set to the exact same location in the new block.
Maybe something like:
/// point is adjusted to the new block. | |
/// | |
/// The insertion point is updated to insert before the same operation as prior to the split. | |
/// If the insertion point was at the end of 'block', the new insertion point is at the end of the returned block. |
/// | ||
/// If the insertion point is before the moved operation, the insertion block | ||
/// is adjusted to the block of `existingOp`. | ||
void moveOpBefore(Operation *op, Operation *existingOp); | ||
|
||
/// Unlink this operation from its current block and insert it right before | ||
/// `iterator` in the specified block. | ||
/// | ||
/// If the insertion point is before the moved operation, the insertion block | ||
/// is adjusted to the specified block. | ||
void moveOpBefore(Operation *op, Block *block, Block::iterator iterator); | ||
|
||
/// Unlink this operation from its current block and insert it right after | ||
/// `existingOp` which may be in the same or another block in the same | ||
/// function. | ||
/// | ||
/// If the insertion point is before the moved operation, the insertion block | ||
/// is adjusted to the block of `existingOp`. | ||
void moveOpAfter(Operation *op, Operation *existingOp); | ||
|
||
/// Unlink this operation from its current block and insert it right after | ||
/// `iterator` in the specified block. | ||
/// | ||
/// If the insertion point is before the moved operation, the insertion block | ||
/// is adjusted to the specified block. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am not fully confident this is the best behaviour for these operations but I fear this depends on ones iternal model of the insertion point.
In my mind, erasing an operation and moving an operation from the POV of the current insertion point are not much different: In both cases the erased/moved op disappears from the current insertion point and arguably the insertion point shouldn't care what happens to it after.
My expected behaviour would then be similar to eraseOp
, which is that the insertion point remains exactly where it was, relatively speaking, and doesn't magically "travel" with the operation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree, it is a bit odd that the insertion point "jumps", potentially even into a different block.
// If the current insertion point is at or after the split point, adjust the | ||
// insertion point to the new block. | ||
bool moveIpToNewBlock = getBlock() == block && | ||
!block->isBeforeInBlock(getInsertionPoint(), before); | ||
auto adjustInsertionPoint = llvm::make_scope_exit([&]() { | ||
if (getInsertionPoint() == block->end()) { | ||
// If the insertion point is at the end of the block, move it to the end | ||
// of the new block. | ||
setInsertionPointToEnd(newBlock); | ||
} else if (moveIpToNewBlock) { | ||
setInsertionPoint(newBlock, getInsertionPoint()); | ||
} | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there an easy way we can test the logic here?
@@ -68,6 +68,16 @@ void Block::erase() { | |||
getParent()->getBlocks().erase(this); | |||
} | |||
|
|||
bool Block::isBeforeInBlock(iterator a, iterator b) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you add an assertion that they are in the same block here?
Similar to #146955, adjust the insertion block when splitting a block or moving an operation. This is the ensure that the insertion point is not left in an invalid state.