-
Notifications
You must be signed in to change notification settings - Fork 13.6k
[mlir][IR] Add InsertPoint::after(ValueRange)
#114940
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
[mlir][IR] Add InsertPoint::after(ValueRange)
#114940
Conversation
@llvm/pr-subscribers-mlir Author: Matthias Springer (matthias-springer) ChangesThis commit adds a helper function to This functionality will be used in the merged 1:1 / 1:N dialect conversion to determine where to insert a TODO: Add test case. Depends on #114928. Full diff: https://github.com/llvm/llvm-project/pull/114940.diff 2 Files Affected:
diff --git a/mlir/include/mlir/IR/Builders.h b/mlir/include/mlir/IR/Builders.h
index 04a8bddc3cd59a..44079b4003de0c 100644
--- a/mlir/include/mlir/IR/Builders.h
+++ b/mlir/include/mlir/IR/Builders.h
@@ -334,6 +334,17 @@ class OpBuilder : public Builder {
/// This class represents a saved insertion point.
class InsertPoint {
public:
+ /// Finds the closest insertion point where all given values are defined
+ /// and can be used without a dominance violation. Returns an "empty"
+ /// insertion point if there is no such insertion point.
+ static InsertPoint findClosest(ArrayRef<Value> values);
+
+ /// Creates a new insertion point right after the given value. If the given
+ /// value is a block argument, the insertion point points to the beginning
+ /// of the block. Otherwise, it points to the place right after the op that
+ /// defines the value.
+ static InsertPoint after(Value value);
+
/// Creates a new insertion point which doesn't point to anything.
InsertPoint() = default;
diff --git a/mlir/lib/IR/Builders.cpp b/mlir/lib/IR/Builders.cpp
index 5397fbabc5c95e..e610b08df71cb1 100644
--- a/mlir/lib/IR/Builders.cpp
+++ b/mlir/lib/IR/Builders.cpp
@@ -641,3 +641,69 @@ void OpBuilder::cloneRegionBefore(Region ®ion, Region &parent,
void OpBuilder::cloneRegionBefore(Region ®ion, Block *before) {
cloneRegionBefore(region, *before->getParent(), before->getIterator());
}
+
+//===----------------------------------------------------------------------===//
+// InsertPoint
+//===----------------------------------------------------------------------===//
+
+OpBuilder::InsertPoint OpBuilder::InsertPoint::after(Value value) {
+ if (auto blockArg = dyn_cast<BlockArgument>(value))
+ return InsertPoint(blockArg.getOwner(), blockArg.getOwner()->begin());
+ Operation *op = value.getDefiningOp();
+ return InsertPoint(op->getBlock(), ++op->getIterator());
+}
+
+/// Helper function that returns "true" if `a` is a proper ancestor of `b`.
+static bool isAncestor(Block *a, Block *b) {
+ return a->getParentOp()->isProperAncestor(b->getParentOp());
+}
+
+OpBuilder::InsertPoint OpBuilder::InsertPoint::findClosest(ArrayRef<Value> values) {
+ // Compute the insertion point after the first value.
+ assert(!values.empty() && "expected at least one value");
+ InsertPoint result = InsertPoint::after(values.front());
+
+ // Check all other values and update the insertion point as needed.
+ for (Value v : values.drop_front()) {
+ InsertPoint pt = InsertPoint::after(v);
+
+ if (pt.getBlock() == result.getBlock()) {
+ // Both values belong to the same block. Modify the iterator (but keep
+ // the block) if needed: take the later one of the two insertion points.
+ Block *block = pt.getBlock();
+ if (pt.getPoint() == block->end()) {
+ // `pt` points to the end of the block: take `pt`.
+ result = pt;
+ continue;
+ } else if (result.getPoint() == block->end()) {
+ // `result` points to the end of the block: nothing to do.
+ continue;
+ }
+ // Neither `pt` nor `result` point to the end of the block, so both
+ // iterators point to an operation. Set `result` to the later one of the
+ // two insertion point.
+ if (result.getPoint()->isBeforeInBlock(&*pt.getPoint()))
+ result = pt;
+ continue;
+ }
+
+ if (isAncestor(result.getBlock(), pt.getBlock())) {
+ // `result` is an ancestor of `pt`. Therefore, `pt` is a valid insertion
+ // point for `v` and all previous values.
+ result = pt;
+ continue;
+ }
+
+ if (isAncestor(pt.getBlock(), result.getBlock())) {
+ // `pt` is an ancestor of `result`. Therefore, `result` is a valid
+ // insertion point for `v` and all previous values.
+ continue;
+ }
+
+ // `pt` and `result` are in different subtrees: neither is an ancestor of
+ // the other. In that case, there is no suitable insertion point.
+ return InsertPoint();
+ }
+
+ return result;
+}
|
@llvm/pr-subscribers-mlir-core Author: Matthias Springer (matthias-springer) ChangesThis commit adds a helper function to This functionality will be used in the merged 1:1 / 1:N dialect conversion to determine where to insert a TODO: Add test case. Depends on #114928. Full diff: https://github.com/llvm/llvm-project/pull/114940.diff 2 Files Affected:
diff --git a/mlir/include/mlir/IR/Builders.h b/mlir/include/mlir/IR/Builders.h
index 04a8bddc3cd59a..44079b4003de0c 100644
--- a/mlir/include/mlir/IR/Builders.h
+++ b/mlir/include/mlir/IR/Builders.h
@@ -334,6 +334,17 @@ class OpBuilder : public Builder {
/// This class represents a saved insertion point.
class InsertPoint {
public:
+ /// Finds the closest insertion point where all given values are defined
+ /// and can be used without a dominance violation. Returns an "empty"
+ /// insertion point if there is no such insertion point.
+ static InsertPoint findClosest(ArrayRef<Value> values);
+
+ /// Creates a new insertion point right after the given value. If the given
+ /// value is a block argument, the insertion point points to the beginning
+ /// of the block. Otherwise, it points to the place right after the op that
+ /// defines the value.
+ static InsertPoint after(Value value);
+
/// Creates a new insertion point which doesn't point to anything.
InsertPoint() = default;
diff --git a/mlir/lib/IR/Builders.cpp b/mlir/lib/IR/Builders.cpp
index 5397fbabc5c95e..e610b08df71cb1 100644
--- a/mlir/lib/IR/Builders.cpp
+++ b/mlir/lib/IR/Builders.cpp
@@ -641,3 +641,69 @@ void OpBuilder::cloneRegionBefore(Region ®ion, Region &parent,
void OpBuilder::cloneRegionBefore(Region ®ion, Block *before) {
cloneRegionBefore(region, *before->getParent(), before->getIterator());
}
+
+//===----------------------------------------------------------------------===//
+// InsertPoint
+//===----------------------------------------------------------------------===//
+
+OpBuilder::InsertPoint OpBuilder::InsertPoint::after(Value value) {
+ if (auto blockArg = dyn_cast<BlockArgument>(value))
+ return InsertPoint(blockArg.getOwner(), blockArg.getOwner()->begin());
+ Operation *op = value.getDefiningOp();
+ return InsertPoint(op->getBlock(), ++op->getIterator());
+}
+
+/// Helper function that returns "true" if `a` is a proper ancestor of `b`.
+static bool isAncestor(Block *a, Block *b) {
+ return a->getParentOp()->isProperAncestor(b->getParentOp());
+}
+
+OpBuilder::InsertPoint OpBuilder::InsertPoint::findClosest(ArrayRef<Value> values) {
+ // Compute the insertion point after the first value.
+ assert(!values.empty() && "expected at least one value");
+ InsertPoint result = InsertPoint::after(values.front());
+
+ // Check all other values and update the insertion point as needed.
+ for (Value v : values.drop_front()) {
+ InsertPoint pt = InsertPoint::after(v);
+
+ if (pt.getBlock() == result.getBlock()) {
+ // Both values belong to the same block. Modify the iterator (but keep
+ // the block) if needed: take the later one of the two insertion points.
+ Block *block = pt.getBlock();
+ if (pt.getPoint() == block->end()) {
+ // `pt` points to the end of the block: take `pt`.
+ result = pt;
+ continue;
+ } else if (result.getPoint() == block->end()) {
+ // `result` points to the end of the block: nothing to do.
+ continue;
+ }
+ // Neither `pt` nor `result` point to the end of the block, so both
+ // iterators point to an operation. Set `result` to the later one of the
+ // two insertion point.
+ if (result.getPoint()->isBeforeInBlock(&*pt.getPoint()))
+ result = pt;
+ continue;
+ }
+
+ if (isAncestor(result.getBlock(), pt.getBlock())) {
+ // `result` is an ancestor of `pt`. Therefore, `pt` is a valid insertion
+ // point for `v` and all previous values.
+ result = pt;
+ continue;
+ }
+
+ if (isAncestor(pt.getBlock(), result.getBlock())) {
+ // `pt` is an ancestor of `result`. Therefore, `result` is a valid
+ // insertion point for `v` and all previous values.
+ continue;
+ }
+
+ // `pt` and `result` are in different subtrees: neither is an ancestor of
+ // the other. In that case, there is no suitable insertion point.
+ return InsertPoint();
+ }
+
+ return result;
+}
|
I could put this helper function into |
4881106
to
08be950
Compare
✅ With the latest revision this PR passed the C/C++ code formatter. |
I'm trying to implement this in a different way: based on dominance, as you suggested. By adding a new And this will no longer depend on #114928. |
mlir/include/mlir/IR/Builders.h
Outdated
@@ -334,6 +334,18 @@ class OpBuilder : public Builder { | |||
/// This class represents a saved insertion point. | |||
class InsertPoint { | |||
public: | |||
/// Finds the closest insertion point where all given values are defined |
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.
It's not clear to me that this is a well defined problem: what does "closest means" here?
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.
Possible candidates are the positions (Block::iterator
) right after the definition of each value. Out of these, there should be only one unique insertion point that post-dominates all the others.
mlir/lib/IR/Builders.cpp
Outdated
} | ||
|
||
// `pt` and `result` are in different subtrees: neither is an ancestor of | ||
// the other. In that case, there is no suitable insertion point. |
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.
Wouldn't the right insertion point be the post-dominator 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.
Yes. Post-dominator Block::iterator
to be precise. (Adding support for that in #115587.)
08be950
to
a18a6c0
Compare
OpBuilder::setInsertionPointAfterValues
caea72f
to
e890126
Compare
a18a6c0
to
f716eaa
Compare
e890126
to
943bd95
Compare
…ation The implementations of `DominanceInfo::properlyDominates` and `PostDominanceInfo::properlyPostDominates` are almost identical: only one line of code is different. Define the function in `DominanceInfoBase` to avoid the code duplication. Note: This commit is not marked as NFC because `PostDominanceInfo::properlyPostDominates` now also has an `enclosingOpOk` argument.
The `properlyDominates` implementations for blocks and ops are very similar. This commit replaces them with a single implementation that operates on block iterators. That implementation can be used to implement both `properlyDominates` variants. Note: A subsequent commit will add a new public `properlyDominates` overload that accepts block iterators. That functionality can then be used to find a valid insertion point at which a range of values is defined (by utilizing post dominance). Depends on #115433.
f716eaa
to
df99520
Compare
OpBuilder::setInsertionPointAfterValues
InsertPoint::after(ValueRange)
df99520
to
f1dbd8a
Compare
f1dbd8a
to
8a49434
Compare
/// There may be multiple suitable insertion points. This function chooses | ||
/// an insertion right after one of the given values. | ||
/// | ||
/// Note: Some of the given values may already have gone out of scope at the |
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.
Note: if there exists an insertion point that post-dominates all definitions and where all values are in scope, this function is guaranteed to find it.
Yes, my comment was geared towards the fact that several of the usages are already conflating reachability with dominance (for example the original usage in the followup IRBuilder PR). My concern is/was around the general utility of such a function, given that it cannot be used in most situations people would reach for it (i.e. dominance outside of purely structured control flow) so it wasn't clear the utility in adding it to Block. That being said I don't have an issue with this singular function, given that it's a tiny amount of graph traversal code. I would clearly mark in the comment that this function is not a replacement for dominance in any reasonable situation (given that a successor block can easily reach a predecessor in a situation such as a loop). |
57bd391
to
c56dc33
Compare
I think this comment is referring to #114928. I updated the comment on that PR. |
I don't have any immediate use case for this PR anymore. |
This commit adds a helper function
InsertPoint::after
. The function computes an insertion point that post-dominates the definition of all given values.This functionality will be used in the merged 1:1 / 1:N dialect conversion to determine where to insert a
builtin.unrealized_conversion_cast
op. To ensure that the same cast can be "reused" for multiple consumer ops, the cast should be inserted as early as possible. (Alternatively, multiple cast ops could be inserted, right before each consumer op.)TODO: Add test case.
Depends on #115587.