From 8bc07a5ef3409b412e046fe1aef5cbcea9d262e7 Mon Sep 17 00:00:00 2001 From: Matthias Springer Date: Fri, 8 Nov 2024 07:52:27 +0100 Subject: [PATCH 1/4] [mlir][IR][NFC] `DominanceInfo`: Minor code cleanups --- mlir/include/mlir/IR/Dominance.h | 11 +---------- mlir/lib/IR/Dominance.cpp | 8 ++++---- 2 files changed, 5 insertions(+), 14 deletions(-) diff --git a/mlir/include/mlir/IR/Dominance.h b/mlir/include/mlir/IR/Dominance.h index 66b9456533ae0..2947bdc21dfeb 100644 --- a/mlir/include/mlir/IR/Dominance.h +++ b/mlir/include/mlir/IR/Dominance.h @@ -147,9 +147,7 @@ class DominanceInfo : public detail::DominanceInfoBase { /// The `enclosingOpOk` flag says whether we should return true if the B op /// is enclosed by a region on A. bool properlyDominates(Operation *a, Operation *b, - bool enclosingOpOk = true) const { - return properlyDominatesImpl(a, b, enclosingOpOk); - } + bool enclosingOpOk = true) const; /// Return true if operation A dominates operation B, i.e. if A and B are the /// same operation or A properly dominates B. @@ -187,13 +185,6 @@ class DominanceInfo : public detail::DominanceInfoBase { bool properlyDominates(Block *a, Block *b) const { return super::properlyDominates(a, b); } - -private: - // Return true if operation A properly dominates operation B. The - /// 'enclosingOpOk' flag says whether we should return true if the b op is - /// enclosed by a region on 'A'. - bool properlyDominatesImpl(Operation *a, Operation *b, - bool enclosingOpOk) const; }; /// A class for computing basic postdominance information. diff --git a/mlir/lib/IR/Dominance.cpp b/mlir/lib/IR/Dominance.cpp index 62477a823acaa..c9b8ee69c2457 100644 --- a/mlir/lib/IR/Dominance.cpp +++ b/mlir/lib/IR/Dominance.cpp @@ -230,7 +230,7 @@ bool DominanceInfoBase::properlyDominates(Block *a, Block *b) const { if (regionA != b->getParent()) { b = regionA ? regionA->findAncestorBlockInRegion(*b) : nullptr; // If we could not find a valid block b then it is a not a dominator. - if (b == nullptr) + if (!b) return false; // Check to see if the ancestor of `b` is the same block as `a`. A properly @@ -266,8 +266,8 @@ template class detail::DominanceInfoBase; /// Return true if operation `a` properly dominates operation `b`. The /// 'enclosingOpOk' flag says whether we should return true if the `b` op is /// enclosed by a region on 'a'. -bool DominanceInfo::properlyDominatesImpl(Operation *a, Operation *b, - bool enclosingOpOk) const { +bool DominanceInfo::properlyDominates(Operation *a, Operation *b, + bool enclosingOpOk) const { Block *aBlock = a->getBlock(), *bBlock = b->getBlock(); assert(aBlock && bBlock && "operations must be in a block"); @@ -319,7 +319,7 @@ bool DominanceInfo::properlyDominates(Value a, Operation *b) const { // `a` properlyDominates `b` if the operation defining `a` properlyDominates // `b`, but `a` does not itself enclose `b` in one of its regions. - return properlyDominatesImpl(a.getDefiningOp(), b, /*enclosingOpOk=*/false); + return properlyDominates(a.getDefiningOp(), b, /*enclosingOpOk=*/false); } //===----------------------------------------------------------------------===// From 213a01bb92eeab460fd82d881f53e96768e161ca Mon Sep 17 00:00:00 2001 From: Matthias Springer Date: Fri, 8 Nov 2024 08:12:08 +0100 Subject: [PATCH 2/4] [mlir][IR] `DominanceInfo`: Deduplicate `properlyDominates` implementation 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. --- mlir/include/mlir/IR/Dominance.h | 21 ++++-- mlir/lib/IR/Dominance.cpp | 111 +++++++++---------------------- 2 files changed, 48 insertions(+), 84 deletions(-) diff --git a/mlir/include/mlir/IR/Dominance.h b/mlir/include/mlir/IR/Dominance.h index 2947bdc21dfeb..16d17b9c0f3d0 100644 --- a/mlir/include/mlir/IR/Dominance.h +++ b/mlir/include/mlir/IR/Dominance.h @@ -113,8 +113,12 @@ class DominanceInfoBase { llvm::PointerIntPair getDominanceInfo(Region *region, bool needsDomTree) const; - /// Return true if the specified block A properly dominates block B. - bool properlyDominates(Block *a, Block *b) const; + /// Return "true" if the specified block A properly (post)dominates block B. + bool properlyDominatesImpl(Block *a, Block *b) const; + + /// Return "true" if the specified op A properly (post)dominates op B. + bool properlyDominatesImpl(Operation *a, Operation *b, + bool enclosingOpOk = true) const; /// A mapping of regions to their base dominator tree and a cached /// "hasSSADominance" bit. This map does not contain dominator trees for @@ -147,7 +151,9 @@ class DominanceInfo : public detail::DominanceInfoBase { /// The `enclosingOpOk` flag says whether we should return true if the B op /// is enclosed by a region on A. bool properlyDominates(Operation *a, Operation *b, - bool enclosingOpOk = true) const; + bool enclosingOpOk = true) const { + return super::properlyDominatesImpl(a, b, enclosingOpOk); + } /// Return true if operation A dominates operation B, i.e. if A and B are the /// same operation or A properly dominates B. @@ -183,7 +189,7 @@ class DominanceInfo : public detail::DominanceInfoBase { /// dominance" of ops, the single block is considered to properly dominate /// itself in a graph region. bool properlyDominates(Block *a, Block *b) const { - return super::properlyDominates(a, b); + return super::properlyDominatesImpl(a, b); } }; @@ -193,7 +199,10 @@ class PostDominanceInfo : public detail::DominanceInfoBase { using super::super; /// Return true if operation A properly postdominates operation B. - bool properlyPostDominates(Operation *a, Operation *b) const; + bool properlyPostDominates(Operation *a, Operation *b, + bool enclosingOpOk = true) const { + return super::properlyDominatesImpl(a, b, enclosingOpOk); + } /// Return true if operation A postdominates operation B. bool postDominates(Operation *a, Operation *b) const { @@ -202,7 +211,7 @@ class PostDominanceInfo : public detail::DominanceInfoBase { /// Return true if the specified block A properly postdominates block B. bool properlyPostDominates(Block *a, Block *b) const { - return super::properlyDominates(a, b); + return super::properlyDominatesImpl(a, b); } /// Return true if the specified block A postdominates block B. diff --git a/mlir/lib/IR/Dominance.cpp b/mlir/lib/IR/Dominance.cpp index c9b8ee69c2457..406e0f2d62d64 100644 --- a/mlir/lib/IR/Dominance.cpp +++ b/mlir/lib/IR/Dominance.cpp @@ -215,7 +215,8 @@ DominanceInfoBase::findNearestCommonDominator(Block *a, /// Return true if the specified block A properly dominates block B. template -bool DominanceInfoBase::properlyDominates(Block *a, Block *b) const { +bool DominanceInfoBase::properlyDominatesImpl(Block *a, + Block *b) const { assert(a && b && "null blocks not allowed"); // A block dominates, but does not properly dominate, itself unless this @@ -243,36 +244,14 @@ bool DominanceInfoBase::properlyDominates(Block *a, Block *b) const { return getDomTree(regionA).properlyDominates(a, b); } -/// Return true if the specified block is reachable from the entry block of -/// its region. template -bool DominanceInfoBase::isReachableFromEntry(Block *a) const { - // If this is the first block in its region, then it is obviously reachable. - Region *region = a->getParent(); - if (®ion->front() == a) - return true; - - // Otherwise this is some block in a multi-block region. Check DomTree. - return getDomTree(region).isReachableFromEntry(a); -} - -template class detail::DominanceInfoBase; -template class detail::DominanceInfoBase; - -//===----------------------------------------------------------------------===// -// DominanceInfo -//===----------------------------------------------------------------------===// - -/// Return true if operation `a` properly dominates operation `b`. The -/// 'enclosingOpOk' flag says whether we should return true if the `b` op is -/// enclosed by a region on 'a'. -bool DominanceInfo::properlyDominates(Operation *a, Operation *b, - bool enclosingOpOk) const { +bool DominanceInfoBase::properlyDominatesImpl( + Operation *a, Operation *b, bool enclosingOpOk) const { Block *aBlock = a->getBlock(), *bBlock = b->getBlock(); assert(aBlock && bBlock && "operations must be in a block"); - // An operation dominates, but does not properly dominate, itself unless this - // is a graph region. + // An operation (pos)dominates, but does not properly (pos)dominate, itself + // unless this is a graph region. if (a == b) return !hasSSADominance(aBlock); @@ -280,14 +259,14 @@ bool DominanceInfo::properlyDominates(Operation *a, Operation *b, Region *aRegion = aBlock->getParent(); if (aRegion != bBlock->getParent()) { // Scoot up b's region tree until we find an operation in A's region that - // encloses it. If this fails, then we know there is no post-dom relation. + // encloses it. If this fails, then we know there is no (post)dom relation. b = aRegion ? aRegion->findAncestorOpInRegion(*b) : nullptr; if (!b) return false; bBlock = b->getBlock(); assert(bBlock->getParent() == aRegion); - // If 'a' encloses 'b', then we consider it to dominate. + // If 'a' encloses 'b', then we consider it to (post)dominate. if (a == b && enclosingOpOk) return true; } @@ -297,17 +276,39 @@ bool DominanceInfo::properlyDominates(Operation *a, Operation *b, // Dominance changes based on the region type. In a region with SSA // dominance, uses inside the same block must follow defs. In other // regions kinds, uses and defs can come in any order inside a block. - if (hasSSADominance(aBlock)) { - // If the blocks are the same, then check if b is before a in the block. + if (!hasSSADominance(aBlock)) + return true; + if constexpr (IsPostDom) { + return b->isBeforeInBlock(a); + } else { return a->isBeforeInBlock(b); } - return true; } // If the blocks are different, use DomTree to resolve the query. return getDomTree(aRegion).properlyDominates(aBlock, bBlock); } +/// Return true if the specified block is reachable from the entry block of +/// its region. +template +bool DominanceInfoBase::isReachableFromEntry(Block *a) const { + // If this is the first block in its region, then it is obviously reachable. + Region *region = a->getParent(); + if (®ion->front() == a) + return true; + + // Otherwise this is some block in a multi-block region. Check DomTree. + return getDomTree(region).isReachableFromEntry(a); +} + +template class detail::DominanceInfoBase; +template class detail::DominanceInfoBase; + +//===----------------------------------------------------------------------===// +// DominanceInfo +//===----------------------------------------------------------------------===// + /// Return true if the `a` value properly dominates operation `b`, i.e if the /// operation that defines `a` properlyDominates `b` and the operation that /// defines `a` does not contain `b`. @@ -321,49 +322,3 @@ bool DominanceInfo::properlyDominates(Value a, Operation *b) const { // `b`, but `a` does not itself enclose `b` in one of its regions. return properlyDominates(a.getDefiningOp(), b, /*enclosingOpOk=*/false); } - -//===----------------------------------------------------------------------===// -// PostDominanceInfo -//===----------------------------------------------------------------------===// - -/// Returns true if statement 'a' properly postdominates statement b. -bool PostDominanceInfo::properlyPostDominates(Operation *a, - Operation *b) const { - auto *aBlock = a->getBlock(), *bBlock = b->getBlock(); - assert(aBlock && bBlock && "operations must be in a block"); - - // An instruction postDominates, but does not properlyPostDominate, itself - // unless this is a graph region. - if (a == b) - return !hasSSADominance(aBlock); - - // If these ops are in different regions, then normalize one into the other. - Region *aRegion = aBlock->getParent(); - if (aRegion != bBlock->getParent()) { - // Scoot up b's region tree until we find an operation in A's region that - // encloses it. If this fails, then we know there is no post-dom relation. - b = aRegion ? aRegion->findAncestorOpInRegion(*b) : nullptr; - if (!b) - return false; - bBlock = b->getBlock(); - assert(bBlock->getParent() == aRegion); - - // If 'a' encloses 'b', then we consider it to postdominate. - if (a == b) - return true; - } - - // Ok, they are in the same region. If they are in the same block, check if b - // is before a in the block. - if (aBlock == bBlock) { - // Dominance changes based on the region type. - if (hasSSADominance(aBlock)) { - // If the blocks are the same, then check if b is before a in the block. - return b->isBeforeInBlock(a); - } - return true; - } - - // If the blocks are different, check if a's block post dominates b's. - return getDomTree(aRegion).properlyDominates(aBlock, bBlock); -} From 57bd3919c4976f01fb0b6a15928545744e25d0cb Mon Sep 17 00:00:00 2001 From: Matthias Springer Date: Sat, 9 Nov 2024 07:13:07 +0100 Subject: [PATCH 3/4] [mlir][IR][NFC] `DominanceInfo`: Share same impl for block/op dominance 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. --- mlir/include/mlir/IR/Dominance.h | 28 +++---- mlir/lib/IR/Dominance.cpp | 124 ++++++++++++++++++++----------- 2 files changed, 92 insertions(+), 60 deletions(-) diff --git a/mlir/include/mlir/IR/Dominance.h b/mlir/include/mlir/IR/Dominance.h index 16d17b9c0f3d0..63504cad211a4 100644 --- a/mlir/include/mlir/IR/Dominance.h +++ b/mlir/include/mlir/IR/Dominance.h @@ -113,12 +113,12 @@ class DominanceInfoBase { llvm::PointerIntPair getDominanceInfo(Region *region, bool needsDomTree) const; - /// Return "true" if the specified block A properly (post)dominates block B. - bool properlyDominatesImpl(Block *a, Block *b) const; - - /// Return "true" if the specified op A properly (post)dominates op B. - bool properlyDominatesImpl(Operation *a, Operation *b, - bool enclosingOpOk = true) const; + /// Return "true" if block iterator A properly (post)dominates block iterator + /// B. If `enclosingOk` is set, A is considered to (post)dominate B if A + /// encloses B. + bool properlyDominatesImpl(Block *aBlock, Block::iterator aIt, Block *bBlock, + Block::iterator bIt, + bool enclosingOk = true) const; /// A mapping of regions to their base dominator tree and a cached /// "hasSSADominance" bit. This map does not contain dominator trees for @@ -151,9 +151,7 @@ class DominanceInfo : public detail::DominanceInfoBase { /// The `enclosingOpOk` flag says whether we should return true if the B op /// is enclosed by a region on A. bool properlyDominates(Operation *a, Operation *b, - bool enclosingOpOk = true) const { - return super::properlyDominatesImpl(a, b, enclosingOpOk); - } + bool enclosingOpOk = true) const; /// Return true if operation A dominates operation B, i.e. if A and B are the /// same operation or A properly dominates B. @@ -188,9 +186,7 @@ class DominanceInfo : public detail::DominanceInfoBase { /// Graph regions have only a single block. To be consistent with "proper /// dominance" of ops, the single block is considered to properly dominate /// itself in a graph region. - bool properlyDominates(Block *a, Block *b) const { - return super::properlyDominatesImpl(a, b); - } + bool properlyDominates(Block *a, Block *b) const; }; /// A class for computing basic postdominance information. @@ -200,9 +196,7 @@ class PostDominanceInfo : public detail::DominanceInfoBase { /// Return true if operation A properly postdominates operation B. bool properlyPostDominates(Operation *a, Operation *b, - bool enclosingOpOk = true) const { - return super::properlyDominatesImpl(a, b, enclosingOpOk); - } + bool enclosingOpOk = true) const; /// Return true if operation A postdominates operation B. bool postDominates(Operation *a, Operation *b) const { @@ -210,9 +204,7 @@ class PostDominanceInfo : public detail::DominanceInfoBase { } /// Return true if the specified block A properly postdominates block B. - bool properlyPostDominates(Block *a, Block *b) const { - return super::properlyDominatesImpl(a, b); - } + bool properlyPostDominates(Block *a, Block *b) const; /// Return true if the specified block A postdominates block B. bool postDominates(Block *a, Block *b) const { diff --git a/mlir/lib/IR/Dominance.cpp b/mlir/lib/IR/Dominance.cpp index 406e0f2d62d64..1c54e09d29b9b 100644 --- a/mlir/lib/IR/Dominance.cpp +++ b/mlir/lib/IR/Dominance.cpp @@ -213,61 +213,73 @@ DominanceInfoBase::findNearestCommonDominator(Block *a, return getDomTree(a->getParent()).findNearestCommonDominator(a, b); } -/// Return true if the specified block A properly dominates block B. -template -bool DominanceInfoBase::properlyDominatesImpl(Block *a, - Block *b) const { - assert(a && b && "null blocks not allowed"); +/// Returns the given block iterator if it lies within the region region. +/// Otherwise, otherwise finds the ancestor of the given block iterator that +/// lies within the given region. Returns and "empty" iterator if the latter +/// fails. +/// +/// Note: This is a variant of Region::findAncestorOpInRegion that operates on +/// block iterators instead of ops. +static std::pair +findAncestorIteratorInRegion(Region *r, Block *b, Block::iterator it) { + // Case 1: The iterator lies within the region region. + if (b->getParent() == r) + return std::make_pair(b, it); + + // Otherwise: Find ancestor iterator. Bail if we run out of parent ops. + Operation *parentOp = b->getParentOp(); + if (!parentOp) + return std::make_pair(static_cast(nullptr), Block::iterator()); + Operation *op = r->findAncestorOpInRegion(*parentOp); + if (!op) + return std::make_pair(static_cast(nullptr), Block::iterator()); + return std::make_pair(op->getBlock(), op->getIterator()); +} - // A block dominates, but does not properly dominate, itself unless this - // is a graph region. +/// Given two iterators into the same block, return "true" if `a` is before `b. +/// Note: This is a variant of Operation::isBeforeInBlock that operates on +/// block iterators instead of ops. +static bool isBeforeInBlock(Block *block, Block::iterator a, + Block::iterator b) { if (a == b) - return !hasSSADominance(a); - - // If both blocks are not in the same region, `a` properly dominates `b` if - // `b` is defined in an operation region that (recursively) ends up being - // dominated by `a`. Walk up the list of containers enclosing B. - Region *regionA = a->getParent(); - if (regionA != b->getParent()) { - b = regionA ? regionA->findAncestorBlockInRegion(*b) : nullptr; - // If we could not find a valid block b then it is a not a dominator. - if (!b) - return false; - - // Check to see if the ancestor of `b` is the same block as `a`. A properly - // dominates B if it contains an op that contains the B block. - if (a == b) - return true; - } - - // Otherwise, they are two different blocks in the same region, use DomTree. - return getDomTree(regionA).properlyDominates(a, b); + return false; + if (a == block->end()) + return false; + if (b == block->end()) + return true; + return a->isBeforeInBlock(&*b); } template bool DominanceInfoBase::properlyDominatesImpl( - Operation *a, Operation *b, bool enclosingOpOk) const { - Block *aBlock = a->getBlock(), *bBlock = b->getBlock(); - assert(aBlock && bBlock && "operations must be in a block"); + Block *aBlock, Block::iterator aIt, Block *bBlock, Block::iterator bIt, + bool enclosingOk) const { + assert(aBlock && bBlock && "expected non-null blocks"); - // An operation (pos)dominates, but does not properly (pos)dominate, itself - // unless this is a graph region. - if (a == b) + // A block iterator (post)dominates, but does not properly (post)dominate, + // itself unless this is a graph region. + if (aBlock == bBlock && aIt == bIt) return !hasSSADominance(aBlock); - // If these ops are in different regions, then normalize one into the other. + // If the iterators are in different regions, then normalize one into the + // other. Region *aRegion = aBlock->getParent(); if (aRegion != bBlock->getParent()) { - // Scoot up b's region tree until we find an operation in A's region that + // Scoot up b's region tree until we find a location in A's region that // encloses it. If this fails, then we know there is no (post)dom relation. - b = aRegion ? aRegion->findAncestorOpInRegion(*b) : nullptr; - if (!b) + if (!aRegion) { + bBlock = nullptr; + bIt = Block::iterator(); + } else { + std::tie(bBlock, bIt) = + findAncestorIteratorInRegion(aRegion, bBlock, bIt); + } + if (!bBlock) return false; - bBlock = b->getBlock(); - assert(bBlock->getParent() == aRegion); + assert(bBlock->getParent() == aRegion && "expected block in regionA"); // If 'a' encloses 'b', then we consider it to (post)dominate. - if (a == b && enclosingOpOk) + if (aBlock == bBlock && aIt == bIt && enclosingOk) return true; } @@ -279,9 +291,9 @@ bool DominanceInfoBase::properlyDominatesImpl( if (!hasSSADominance(aBlock)) return true; if constexpr (IsPostDom) { - return b->isBeforeInBlock(a); + return isBeforeInBlock(aBlock, bIt, aIt); } else { - return a->isBeforeInBlock(b); + return isBeforeInBlock(aBlock, aIt, bIt); } } @@ -309,6 +321,18 @@ template class detail::DominanceInfoBase; // DominanceInfo //===----------------------------------------------------------------------===// +bool DominanceInfo::properlyDominates(Operation *a, Operation *b, + bool enclosingOpOk) const { + return super::properlyDominatesImpl(a->getBlock(), a->getIterator(), + b->getBlock(), b->getIterator(), + enclosingOpOk); +} + +bool DominanceInfo::properlyDominates(Block *a, Block *b) const { + return super::properlyDominatesImpl(a, a->begin(), b, b->begin(), + /*enclosingOk=*/true); +} + /// Return true if the `a` value properly dominates operation `b`, i.e if the /// operation that defines `a` properlyDominates `b` and the operation that /// defines `a` does not contain `b`. @@ -322,3 +346,19 @@ bool DominanceInfo::properlyDominates(Value a, Operation *b) const { // `b`, but `a` does not itself enclose `b` in one of its regions. return properlyDominates(a.getDefiningOp(), b, /*enclosingOpOk=*/false); } + +//===----------------------------------------------------------------------===// +// PostDominanceInfo +//===----------------------------------------------------------------------===// + +bool PostDominanceInfo::properlyPostDominates(Operation *a, Operation *b, + bool enclosingOpOk) const { + return super::properlyDominatesImpl(a->getBlock(), a->getIterator(), + b->getBlock(), b->getIterator(), + enclosingOpOk); +} + +bool PostDominanceInfo::properlyPostDominates(Block *a, Block *b) const { + return super::properlyDominatesImpl(a, a->end(), b, b->end(), + /*enclosingOk=*/true); +} From 8a49434df62e394cd109f0189349b4d28dafa525 Mon Sep 17 00:00:00 2001 From: Matthias Springer Date: Sat, 9 Nov 2024 12:29:16 +0100 Subject: [PATCH 4/4] [mlir][IR] Add `OpBuilder::setInsertionPointAfterValues` --- mlir/include/mlir/IR/Builders.h | 14 +++++++++++ mlir/include/mlir/IR/Dominance.h | 23 ++++++++++++++++++ mlir/lib/IR/Builders.cpp | 41 ++++++++++++++++++++++++++++++++ 3 files changed, 78 insertions(+) diff --git a/mlir/include/mlir/IR/Builders.h b/mlir/include/mlir/IR/Builders.h index 6fb71ccefda15..7ef03b8717952 100644 --- a/mlir/include/mlir/IR/Builders.h +++ b/mlir/include/mlir/IR/Builders.h @@ -16,6 +16,7 @@ namespace mlir { class AffineExpr; +class PostDominanceInfo; class IRMapping; class UnknownLoc; class FileLineColLoc; @@ -341,6 +342,19 @@ class OpBuilder : public Builder { InsertPoint(Block *insertBlock, Block::iterator insertPt) : block(insertBlock), point(insertPt) {} + /// Compute an insertion point to a place that post-dominates the + /// definitions of all given values. Returns an "empty" insertion point if + /// no such insertion point exists. + /// + /// 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 + /// selected insertion point. (E.g., because they are defined in a nested + /// region or because they are not visible in an IsolatedFromAbove region.) + static InsertPoint after(ArrayRef values, + const PostDominanceInfo &domInfo); + /// Returns true if this insert point is set. bool isSet() const { return (block != nullptr); } diff --git a/mlir/include/mlir/IR/Dominance.h b/mlir/include/mlir/IR/Dominance.h index 63504cad211a4..be2dcec380b6c 100644 --- a/mlir/include/mlir/IR/Dominance.h +++ b/mlir/include/mlir/IR/Dominance.h @@ -187,6 +187,17 @@ class DominanceInfo : public detail::DominanceInfoBase { /// dominance" of ops, the single block is considered to properly dominate /// itself in a graph region. bool properlyDominates(Block *a, Block *b) const; + + bool properlyDominantes(Block *aBlock, Block::iterator aIt, Block *bBlock, + Block::iterator bIt, bool enclosingOk = true) const { + return super::properlyDominatesImpl(aBlock, aIt, bBlock, bIt, enclosingOk); + } + + bool dominantes(Block *aBlock, Block::iterator aIt, Block *bBlock, + Block::iterator bIt, bool enclosingOk = true) const { + return (aBlock == bBlock && aIt == bIt) || + super::properlyDominatesImpl(aBlock, aIt, bBlock, bIt, enclosingOk); + } }; /// A class for computing basic postdominance information. @@ -210,6 +221,18 @@ class PostDominanceInfo : public detail::DominanceInfoBase { bool postDominates(Block *a, Block *b) const { return a == b || properlyPostDominates(a, b); } + + bool properlyPostDominantes(Block *aBlock, Block::iterator aIt, Block *bBlock, + Block::iterator bIt, + bool enclosingOk = true) const { + return super::properlyDominatesImpl(aBlock, aIt, bBlock, bIt, enclosingOk); + } + + bool postDominantes(Block *aBlock, Block::iterator aIt, Block *bBlock, + Block::iterator bIt, bool enclosingOk = true) const { + return (aBlock == bBlock && aIt == bIt) || + super::properlyDominatesImpl(aBlock, aIt, bBlock, bIt, enclosingOk); + } }; } // namespace mlir diff --git a/mlir/lib/IR/Builders.cpp b/mlir/lib/IR/Builders.cpp index 5397fbabc5c95..4714c3cace6c7 100644 --- a/mlir/lib/IR/Builders.cpp +++ b/mlir/lib/IR/Builders.cpp @@ -11,6 +11,7 @@ #include "mlir/IR/AffineMap.h" #include "mlir/IR/BuiltinTypes.h" #include "mlir/IR/Dialect.h" +#include "mlir/IR/Dominance.h" #include "mlir/IR/IRMapping.h" #include "mlir/IR/IntegerSet.h" #include "mlir/IR/Matchers.h" @@ -641,3 +642,43 @@ void OpBuilder::cloneRegionBefore(Region ®ion, Region &parent, void OpBuilder::cloneRegionBefore(Region ®ion, Block *before) { cloneRegionBefore(region, *before->getParent(), before->getIterator()); } + +OpBuilder::InsertPoint +OpBuilder::InsertPoint::after(ArrayRef values, + const PostDominanceInfo &domInfo) { + // Helper function that computes the point after v's definition. + auto computeAfterIp = [](Value v) -> std::pair { + if (auto blockArg = dyn_cast(v)) + return std::make_pair(blockArg.getOwner(), blockArg.getOwner()->begin()); + Operation *op = v.getDefiningOp(); + return std::make_pair(op->getBlock(), ++op->getIterator()); + }; + + // Compute the insertion point after the first value is defined. + assert(!values.empty() && "expected at least one Value"); + auto [block, blockIt] = computeAfterIp(values.front()); + + // Check the other values one-by-one and update the insertion point if + // needed. + for (Value v : values.drop_front()) { + auto [candidateBlock, candidateBlockIt] = computeAfterIp(v); + if (domInfo.postDominantes(candidateBlock, candidateBlockIt, block, + blockIt)) { + // The point after v's definition post-dominates the current (and all + // previous) insertion points. Note: Post-dominance is transitive. + block = candidateBlock; + blockIt = candidateBlockIt; + continue; + } + + if (!domInfo.postDominantes(block, blockIt, candidateBlock, + candidateBlockIt)) { + // The point after v's definition and the current insertion point do not + // post-dominate each other. Therefore, there is no insertion point that + // post-dominates all values. + return InsertPoint(nullptr, Block::iterator()); + } + } + + return InsertPoint(block, blockIt); +}