2018-10-15 22:59:31 -07:00
|
|
|
//===- PatternMatch.cpp - Base classes for pattern match ------------------===//
|
|
|
|
//
|
2020-01-26 03:58:30 +00:00
|
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
2019-12-23 09:35:36 -08:00
|
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
2018-10-15 22:59:31 -07:00
|
|
|
//
|
2019-12-23 09:35:36 -08:00
|
|
|
//===----------------------------------------------------------------------===//
|
2018-10-15 22:59:31 -07:00
|
|
|
|
2018-10-25 16:44:04 -07:00
|
|
|
#include "mlir/IR/PatternMatch.h"
|
2024-01-03 20:37:19 -08:00
|
|
|
#include "mlir/Config/mlir-config.h"
|
2023-01-08 14:15:07 -08:00
|
|
|
#include "mlir/IR/IRMapping.h"
|
2023-09-20 08:45:46 +02:00
|
|
|
#include "mlir/IR/Iterators.h"
|
|
|
|
#include "mlir/IR/RegionKindInterface.h"
|
2024-04-04 13:44:24 -07:00
|
|
|
#include "llvm/ADT/SmallPtrSet.h"
|
[mlir] Remove 'valuesToRemoveIfDead' from PatternRewriter API
Summary:
Remove 'valuesToRemoveIfDead' from PatternRewriter API. The removal
functionality wasn't implemented and we decided [1] not to implement it in
favor of having more powerful DCE approaches.
[1] https://github.com/tensorflow/mlir/pull/212
Reviewers: rriddle, bondhugula
Reviewed By: rriddle
Subscribers: liufengdb, mehdi_amini, rriddle, jpienaar, burmako, shauheen, antiagainst, nicolasvasilache, arpith-jacob, mgester, lucyrfox, llvm-commits
Tags: #llvm
Differential Revision: https://reviews.llvm.org/D72545
2020-01-27 13:13:20 -08:00
|
|
|
|
2018-10-15 22:59:31 -07:00
|
|
|
using namespace mlir;
|
|
|
|
|
2020-10-26 17:23:41 -07:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// PatternBenefit
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2018-10-15 22:59:31 -07:00
|
|
|
PatternBenefit::PatternBenefit(unsigned benefit) : representation(benefit) {
|
|
|
|
assert(representation == benefit && benefit != ImpossibleToMatchSentinel &&
|
|
|
|
"This pattern match benefit is too large to represent");
|
|
|
|
}
|
|
|
|
|
|
|
|
unsigned short PatternBenefit::getBenefit() const {
|
2020-06-18 13:58:17 -07:00
|
|
|
assert(!isImpossibleToMatch() && "Pattern doesn't match");
|
2018-10-15 22:59:31 -07:00
|
|
|
return representation;
|
|
|
|
}
|
|
|
|
|
2018-10-21 19:03:29 -07:00
|
|
|
//===----------------------------------------------------------------------===//
|
2020-10-26 17:23:41 -07:00
|
|
|
// Pattern
|
2018-10-21 19:03:29 -07:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2021-03-23 13:44:14 -07:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// OperationName Root Constructors
|
2025-03-31 09:29:54 -07:00
|
|
|
//===----------------------------------------------------------------------===//
|
2021-03-23 13:44:14 -07:00
|
|
|
|
2018-11-11 15:56:49 -08:00
|
|
|
Pattern::Pattern(StringRef rootName, PatternBenefit benefit,
|
2021-03-23 13:44:14 -07:00
|
|
|
MLIRContext *context, ArrayRef<StringRef> generatedNames)
|
|
|
|
: Pattern(OperationName(rootName, context).getAsOpaquePointer(),
|
|
|
|
RootKind::OperationName, generatedNames, benefit, context) {}
|
|
|
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// MatchAnyOpTypeTag Root Constructors
|
2025-03-31 09:29:54 -07:00
|
|
|
//===----------------------------------------------------------------------===//
|
2021-03-23 13:44:14 -07:00
|
|
|
|
|
|
|
Pattern::Pattern(MatchAnyOpTypeTag tag, PatternBenefit benefit,
|
|
|
|
MLIRContext *context, ArrayRef<StringRef> generatedNames)
|
|
|
|
: Pattern(nullptr, RootKind::Any, generatedNames, benefit, context) {}
|
|
|
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// MatchInterfaceOpTypeTag Root Constructors
|
2025-03-31 09:29:54 -07:00
|
|
|
//===----------------------------------------------------------------------===//
|
2021-03-23 13:44:14 -07:00
|
|
|
|
|
|
|
Pattern::Pattern(MatchInterfaceOpTypeTag tag, TypeID interfaceID,
|
|
|
|
PatternBenefit benefit, MLIRContext *context,
|
|
|
|
ArrayRef<StringRef> generatedNames)
|
|
|
|
: Pattern(interfaceID.getAsOpaquePointer(), RootKind::InterfaceID,
|
|
|
|
generatedNames, benefit, context) {}
|
|
|
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// MatchTraitOpTypeTag Root Constructors
|
2025-03-31 09:29:54 -07:00
|
|
|
//===----------------------------------------------------------------------===//
|
2021-03-23 13:44:14 -07:00
|
|
|
|
|
|
|
Pattern::Pattern(MatchTraitOpTypeTag tag, TypeID traitID,
|
|
|
|
PatternBenefit benefit, MLIRContext *context,
|
|
|
|
ArrayRef<StringRef> generatedNames)
|
|
|
|
: Pattern(traitID.getAsOpaquePointer(), RootKind::TraitID, generatedNames,
|
|
|
|
benefit, context) {}
|
|
|
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// General Constructors
|
2025-03-31 09:29:54 -07:00
|
|
|
//===----------------------------------------------------------------------===//
|
2021-03-23 13:44:14 -07:00
|
|
|
|
|
|
|
Pattern::Pattern(const void *rootValue, RootKind rootKind,
|
|
|
|
ArrayRef<StringRef> generatedNames, PatternBenefit benefit,
|
2018-11-11 15:56:49 -08:00
|
|
|
MLIRContext *context)
|
2021-03-23 13:44:14 -07:00
|
|
|
: rootValue(rootValue), rootKind(rootKind), benefit(benefit),
|
|
|
|
contextAndHasBoundedRecursion(context, false) {
|
|
|
|
if (generatedNames.empty())
|
|
|
|
return;
|
2020-06-18 13:58:25 -07:00
|
|
|
generatedOps.reserve(generatedNames.size());
|
|
|
|
std::transform(generatedNames.begin(), generatedNames.end(),
|
|
|
|
std::back_inserter(generatedOps), [context](StringRef name) {
|
|
|
|
return OperationName(name, context);
|
|
|
|
});
|
|
|
|
}
|
2019-05-24 19:35:23 -07:00
|
|
|
|
2020-10-26 17:23:41 -07:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// RewritePattern
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
/// Out-of-line vtable anchor.
|
|
|
|
void RewritePattern::anchor() {}
|
|
|
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
2021-02-02 11:32:52 -08:00
|
|
|
// RewriterBase
|
2020-10-26 17:23:41 -07:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2023-02-22 09:12:24 +01:00
|
|
|
bool RewriterBase::Listener::classof(const OpBuilder::Listener *base) {
|
|
|
|
return base->getKind() == OpBuilder::ListenerBase::Kind::RewriterBaseListener;
|
|
|
|
}
|
|
|
|
|
2021-02-02 11:32:52 -08:00
|
|
|
RewriterBase::~RewriterBase() {
|
2018-10-21 19:03:29 -07:00
|
|
|
// Out of line to provide a vtable anchor for the class.
|
|
|
|
}
|
|
|
|
|
2024-04-02 10:53:57 +09:00
|
|
|
void RewriterBase::replaceAllOpUsesWith(Operation *from, ValueRange to) {
|
|
|
|
// Notify the listener that we're about to replace this op.
|
|
|
|
if (auto *rewriteListener = dyn_cast_if_present<Listener>(listener))
|
|
|
|
rewriteListener->notifyOperationReplaced(from, to);
|
|
|
|
|
|
|
|
replaceAllUsesWith(from->getResults(), to);
|
|
|
|
}
|
|
|
|
|
|
|
|
void RewriterBase::replaceAllOpUsesWith(Operation *from, Operation *to) {
|
|
|
|
// Notify the listener that we're about to replace this op.
|
|
|
|
if (auto *rewriteListener = dyn_cast_if_present<Listener>(listener))
|
|
|
|
rewriteListener->notifyOperationReplaced(from, to);
|
|
|
|
|
|
|
|
replaceAllUsesWith(from->getResults(), to->getResults());
|
|
|
|
}
|
|
|
|
|
2021-02-02 11:32:52 -08:00
|
|
|
/// This method replaces the results of the operation with the specified list of
|
|
|
|
/// values. The number of provided values must match the number of results of
|
2023-06-14 08:41:19 +02:00
|
|
|
/// the operation. The replaced op is erased.
|
2021-02-02 11:32:52 -08:00
|
|
|
void RewriterBase::replaceOp(Operation *op, ValueRange newValues) {
|
2023-03-06 09:24:39 +01:00
|
|
|
assert(op->getNumResults() == newValues.size() &&
|
|
|
|
"incorrect # of replacement values");
|
|
|
|
|
2024-03-07 10:26:22 +09:00
|
|
|
// Replace all result uses. Also notifies the listener of modifications.
|
2024-03-11 17:36:18 +09:00
|
|
|
replaceAllOpUsesWith(op, newValues);
|
2018-11-23 01:10:26 -08:00
|
|
|
|
2023-09-20 08:45:46 +02:00
|
|
|
// Erase op and notify listener.
|
2023-06-14 08:41:19 +02:00
|
|
|
eraseOp(op);
|
|
|
|
}
|
|
|
|
|
|
|
|
/// This method replaces the results of the operation with the specified new op
|
|
|
|
/// (replacement). The number of results of the two operations must match. The
|
|
|
|
/// replaced op is erased.
|
|
|
|
void RewriterBase::replaceOp(Operation *op, Operation *newOp) {
|
|
|
|
assert(op && newOp && "expected non-null op");
|
|
|
|
assert(op->getNumResults() == newOp->getNumResults() &&
|
|
|
|
"ops have different number of results");
|
|
|
|
|
2024-03-07 10:26:22 +09:00
|
|
|
// Replace all result uses. Also notifies the listener of modifications.
|
2024-03-11 17:36:18 +09:00
|
|
|
replaceAllOpUsesWith(op, newOp->getResults());
|
2023-06-14 08:41:19 +02:00
|
|
|
|
2023-09-20 08:45:46 +02:00
|
|
|
// Erase op and notify listener.
|
2023-06-14 08:41:19 +02:00
|
|
|
eraseOp(op);
|
2018-11-23 01:10:26 -08:00
|
|
|
}
|
|
|
|
|
2019-10-16 09:50:28 -07:00
|
|
|
/// This method erases an operation that is known to have no uses. The uses of
|
|
|
|
/// the given operation *must* be known to be dead.
|
2021-02-02 11:32:52 -08:00
|
|
|
void RewriterBase::eraseOp(Operation *op) {
|
2019-10-16 09:50:28 -07:00
|
|
|
assert(op->use_empty() && "expected 'op' to have no uses");
|
2023-09-20 08:45:46 +02:00
|
|
|
auto *rewriteListener = dyn_cast_if_present<Listener>(listener);
|
|
|
|
|
|
|
|
// Fast path: If no listener is attached, the op can be dropped in one go.
|
|
|
|
if (!rewriteListener) {
|
|
|
|
op->erase();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Helper function that erases a single op.
|
|
|
|
auto eraseSingleOp = [&](Operation *op) {
|
|
|
|
#ifndef NDEBUG
|
|
|
|
// All nested ops should have been erased already.
|
|
|
|
assert(
|
|
|
|
llvm::all_of(op->getRegions(), [&](Region &r) { return r.empty(); }) &&
|
|
|
|
"expected empty regions");
|
|
|
|
// All users should have been erased already if the op is in a region with
|
|
|
|
// SSA dominance.
|
|
|
|
if (!op->use_empty() && op->getParentOp())
|
|
|
|
assert(mayBeGraphRegion(*op->getParentRegion()) &&
|
|
|
|
"expected that op has no uses");
|
|
|
|
#endif // NDEBUG
|
2024-02-20 09:08:19 +01:00
|
|
|
rewriteListener->notifyOperationErased(op);
|
2023-09-20 08:45:46 +02:00
|
|
|
|
|
|
|
// Explicitly drop all uses in case the op is in a graph region.
|
|
|
|
op->dropAllUses();
|
|
|
|
op->erase();
|
|
|
|
};
|
|
|
|
|
|
|
|
// Nested ops must be erased one-by-one, so that listeners have a consistent
|
|
|
|
// view of the IR every time a notification is triggered. Users must be
|
|
|
|
// erased before definitions. I.e., post-order, reverse dominance.
|
|
|
|
std::function<void(Operation *)> eraseTree = [&](Operation *op) {
|
|
|
|
// Erase nested ops.
|
|
|
|
for (Region &r : llvm::reverse(op->getRegions())) {
|
|
|
|
// Erase all blocks in the right order. Successors should be erased
|
|
|
|
// before predecessors because successor blocks may use values defined
|
|
|
|
// in predecessor blocks. A post-order traversal of blocks within a
|
|
|
|
// region visits successors before predecessors. Repeat the traversal
|
|
|
|
// until the region is empty. (The block graph could be disconnected.)
|
|
|
|
while (!r.empty()) {
|
|
|
|
SmallVector<Block *> erasedBlocks;
|
2024-03-05 15:33:49 +08:00
|
|
|
// Some blocks may have invalid successor, use a set including nullptr
|
|
|
|
// to avoid null pointer.
|
|
|
|
llvm::SmallPtrSet<Block *, 4> visited{nullptr};
|
|
|
|
for (Block *b : llvm::post_order_ext(&r.front(), visited)) {
|
2023-09-20 08:45:46 +02:00
|
|
|
// Visit ops in reverse order.
|
|
|
|
for (Operation &op :
|
|
|
|
llvm::make_early_inc_range(ReverseIterator::makeIterable(*b)))
|
|
|
|
eraseTree(&op);
|
|
|
|
// Do not erase the block immediately. This is not supprted by the
|
|
|
|
// post_order iterator.
|
|
|
|
erasedBlocks.push_back(b);
|
|
|
|
}
|
|
|
|
for (Block *b : erasedBlocks) {
|
|
|
|
// Explicitly drop all uses in case there is a cycle in the block
|
|
|
|
// graph.
|
|
|
|
for (BlockArgument bbArg : b->getArguments())
|
|
|
|
bbArg.dropAllUses();
|
|
|
|
b->dropAllUses();
|
2024-01-21 10:06:53 +01:00
|
|
|
eraseBlock(b);
|
2023-09-20 08:45:46 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Then erase the enclosing op.
|
|
|
|
eraseSingleOp(op);
|
|
|
|
};
|
|
|
|
|
|
|
|
eraseTree(op);
|
2019-10-16 09:50:28 -07:00
|
|
|
}
|
|
|
|
|
2021-02-02 11:32:52 -08:00
|
|
|
void RewriterBase::eraseBlock(Block *block) {
|
2024-01-21 10:06:53 +01:00
|
|
|
assert(block->use_empty() && "expected 'block' to have no uses");
|
|
|
|
|
2020-03-31 01:15:40 +05:30
|
|
|
for (auto &op : llvm::make_early_inc_range(llvm::reverse(*block))) {
|
|
|
|
assert(op.use_empty() && "expected 'op' to have no uses");
|
|
|
|
eraseOp(&op);
|
|
|
|
}
|
2024-01-21 10:06:53 +01:00
|
|
|
|
|
|
|
// Notify the listener that the block is about to be removed.
|
|
|
|
if (auto *rewriteListener = dyn_cast_if_present<Listener>(listener))
|
2024-02-20 09:08:19 +01:00
|
|
|
rewriteListener->notifyBlockErased(block);
|
2024-01-21 10:06:53 +01:00
|
|
|
|
2020-03-31 01:15:40 +05:30
|
|
|
block->erase();
|
|
|
|
}
|
|
|
|
|
2024-01-17 11:08:59 +01:00
|
|
|
void RewriterBase::finalizeOpModification(Operation *op) {
|
2023-02-22 10:41:22 +01:00
|
|
|
// Notify the listener that the operation was modified.
|
|
|
|
if (auto *rewriteListener = dyn_cast_if_present<Listener>(listener))
|
|
|
|
rewriteListener->notifyOperationModified(op);
|
|
|
|
}
|
|
|
|
|
2024-04-04 13:44:24 -07:00
|
|
|
void RewriterBase::replaceAllUsesExcept(
|
|
|
|
Value from, Value to, const SmallPtrSetImpl<Operation *> &preservedUsers) {
|
|
|
|
return replaceUsesWithIf(from, to, [&](OpOperand &use) {
|
|
|
|
Operation *user = use.getOwner();
|
|
|
|
return !preservedUsers.contains(user);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2023-03-06 09:24:39 +01:00
|
|
|
void RewriterBase::replaceUsesWithIf(Value from, Value to,
|
2024-03-07 10:26:22 +09:00
|
|
|
function_ref<bool(OpOperand &)> functor,
|
|
|
|
bool *allUsesReplaced) {
|
|
|
|
bool allReplaced = true;
|
2022-12-06 07:30:28 +00:00
|
|
|
for (OpOperand &operand : llvm::make_early_inc_range(from.getUses())) {
|
2024-03-07 10:26:22 +09:00
|
|
|
bool replace = functor(operand);
|
|
|
|
if (replace)
|
2024-01-17 11:08:59 +01:00
|
|
|
modifyOpInPlace(operand.getOwner(), [&]() { operand.set(to); });
|
2024-03-07 10:26:22 +09:00
|
|
|
allReplaced &= replace;
|
|
|
|
}
|
|
|
|
if (allUsesReplaced)
|
|
|
|
*allUsesReplaced = allReplaced;
|
|
|
|
}
|
|
|
|
|
|
|
|
void RewriterBase::replaceUsesWithIf(ValueRange from, ValueRange to,
|
|
|
|
function_ref<bool(OpOperand &)> functor,
|
|
|
|
bool *allUsesReplaced) {
|
|
|
|
assert(from.size() == to.size() && "incorrect number of replacements");
|
|
|
|
bool allReplaced = true;
|
|
|
|
for (auto it : llvm::zip_equal(from, to)) {
|
|
|
|
bool r;
|
|
|
|
replaceUsesWithIf(std::get<0>(it), std::get<1>(it), functor,
|
|
|
|
/*allUsesReplaced=*/&r);
|
|
|
|
allReplaced &= r;
|
2022-12-06 07:30:28 +00:00
|
|
|
}
|
2024-03-07 10:26:22 +09:00
|
|
|
if (allUsesReplaced)
|
|
|
|
*allUsesReplaced = allReplaced;
|
2022-12-06 07:30:28 +00:00
|
|
|
}
|
|
|
|
|
2023-03-06 12:38:18 +01:00
|
|
|
void RewriterBase::inlineBlockBefore(Block *source, Block *dest,
|
|
|
|
Block::iterator before,
|
|
|
|
ValueRange argValues) {
|
|
|
|
assert(argValues.size() == source->getNumArguments() &&
|
|
|
|
"incorrect # of argument replacement values");
|
|
|
|
|
|
|
|
// The source block will be deleted, so it should not have any users (i.e.,
|
|
|
|
// there should be no predecessors).
|
2020-08-19 16:07:42 -07:00
|
|
|
assert(source->hasNoPredecessors() &&
|
|
|
|
"expected 'source' to have no predecessors");
|
|
|
|
|
2023-03-06 12:38:18 +01:00
|
|
|
if (dest->end() != before) {
|
|
|
|
// The source block will be inserted in the middle of the dest block, so
|
|
|
|
// the source block should have no successors. Otherwise, the remainder of
|
|
|
|
// the dest block would be unreachable.
|
|
|
|
assert(source->hasNoSuccessors() &&
|
|
|
|
"expected 'source' to have no successors");
|
|
|
|
} else {
|
|
|
|
// The source block will be inserted at the end of the dest block, so the
|
|
|
|
// dest block should have no successors. Otherwise, the inserted operations
|
|
|
|
// will be unreachable.
|
|
|
|
assert(dest->hasNoSuccessors() && "expected 'dest' to have no successors");
|
|
|
|
}
|
2020-08-19 16:07:42 -07:00
|
|
|
|
2023-03-06 12:38:18 +01:00
|
|
|
// Replace all of the successor arguments with the provided values.
|
|
|
|
for (auto it : llvm::zip(source->getArguments(), argValues))
|
|
|
|
replaceAllUsesWith(std::get<0>(it), std::get<1>(it));
|
2020-08-19 16:07:42 -07:00
|
|
|
|
2023-03-06 12:38:18 +01:00
|
|
|
// Move operations from the source block to the dest block and erase the
|
|
|
|
// source block.
|
2024-01-31 14:40:38 +01:00
|
|
|
if (!listener) {
|
|
|
|
// Fast path: If no listener is attached, move all operations at once.
|
|
|
|
dest->getOperations().splice(before, source->getOperations());
|
|
|
|
} else {
|
|
|
|
while (!source->empty())
|
|
|
|
moveOpBefore(&source->front(), dest, before);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Erase the source block.
|
|
|
|
assert(source->empty() && "expected 'source' to be empty");
|
2024-01-21 10:06:53 +01:00
|
|
|
eraseBlock(source);
|
2023-03-06 12:38:18 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void RewriterBase::inlineBlockBefore(Block *source, Operation *op,
|
|
|
|
ValueRange argValues) {
|
|
|
|
inlineBlockBefore(source, op->getBlock(), op->getIterator(), argValues);
|
|
|
|
}
|
|
|
|
|
|
|
|
void RewriterBase::mergeBlocks(Block *source, Block *dest,
|
|
|
|
ValueRange argValues) {
|
|
|
|
inlineBlockBefore(source, dest, dest->end(), argValues);
|
2020-08-19 16:07:42 -07:00
|
|
|
}
|
|
|
|
|
2019-11-05 11:57:03 -08:00
|
|
|
/// Split the operations starting at "before" (inclusive) out of the given
|
|
|
|
/// block into a new block, and return it.
|
2021-02-02 11:32:52 -08:00
|
|
|
Block *RewriterBase::splitBlock(Block *block, Block::iterator before) {
|
2024-01-31 14:56:26 +01:00
|
|
|
// Fast path: If no listener is attached, split the block directly.
|
|
|
|
if (!listener)
|
|
|
|
return 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()));
|
|
|
|
|
|
|
|
// If `before` points to end of the block, no ops should be moved.
|
|
|
|
if (before == block->end())
|
|
|
|
return newBlock;
|
|
|
|
|
|
|
|
// Move ops one-by-one from the end of `block` to the beginning of `newBlock`.
|
|
|
|
// Stop when the operation pointed to by `before` has been moved.
|
|
|
|
while (before->getBlock() != newBlock)
|
|
|
|
moveOpBefore(&block->back(), newBlock, newBlock->begin());
|
|
|
|
|
|
|
|
return newBlock;
|
2019-11-05 11:57:03 -08:00
|
|
|
}
|
|
|
|
|
2019-06-11 08:33:18 -07:00
|
|
|
/// Move the blocks that belong to "region" before the given position in
|
|
|
|
/// another region. The two regions must be different. The caller is in
|
|
|
|
/// charge to update create the operation transferring the control flow to the
|
|
|
|
/// region and pass it the correct block arguments.
|
2021-02-02 11:32:52 -08:00
|
|
|
void RewriterBase::inlineRegionBefore(Region ®ion, Region &parent,
|
|
|
|
Region::iterator before) {
|
2024-01-26 10:46:58 +01:00
|
|
|
// Fast path: If no listener is attached, move all blocks at once.
|
|
|
|
if (!listener) {
|
|
|
|
parent.getBlocks().splice(before, region.getBlocks());
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Move blocks from the beginning of the region one-by-one.
|
2024-01-31 11:25:11 +01:00
|
|
|
while (!region.empty())
|
|
|
|
moveBlockBefore(®ion.front(), &parent, before);
|
2019-06-21 03:26:39 -07:00
|
|
|
}
|
2021-02-02 11:32:52 -08:00
|
|
|
void RewriterBase::inlineRegionBefore(Region ®ion, Block *before) {
|
2019-06-21 03:26:39 -07:00
|
|
|
inlineRegionBefore(region, *before->getParent(), before->getIterator());
|
2019-06-11 08:33:18 -07:00
|
|
|
}
|
|
|
|
|
2024-01-31 11:25:11 +01:00
|
|
|
void RewriterBase::moveBlockBefore(Block *block, Block *anotherBlock) {
|
|
|
|
moveBlockBefore(block, anotherBlock->getParent(),
|
|
|
|
anotherBlock->getIterator());
|
|
|
|
}
|
|
|
|
|
|
|
|
void RewriterBase::moveBlockBefore(Block *block, Region *region,
|
|
|
|
Region::iterator iterator) {
|
|
|
|
Region *currentRegion = block->getParent();
|
|
|
|
Region::iterator nextIterator = std::next(block->getIterator());
|
|
|
|
block->moveBefore(region, iterator);
|
|
|
|
if (listener)
|
|
|
|
listener->notifyBlockInserted(block, /*previous=*/currentRegion,
|
|
|
|
/*previousIt=*/nextIterator);
|
|
|
|
}
|
|
|
|
|
2024-01-25 11:01:28 +01:00
|
|
|
void RewriterBase::moveOpBefore(Operation *op, Operation *existingOp) {
|
|
|
|
moveOpBefore(op, existingOp->getBlock(), existingOp->getIterator());
|
|
|
|
}
|
|
|
|
|
|
|
|
void RewriterBase::moveOpBefore(Operation *op, Block *block,
|
|
|
|
Block::iterator iterator) {
|
|
|
|
Block *currentBlock = op->getBlock();
|
2024-01-31 11:25:11 +01:00
|
|
|
Block::iterator nextIterator = std::next(op->getIterator());
|
2024-01-25 11:01:28 +01:00
|
|
|
op->moveBefore(block, iterator);
|
|
|
|
if (listener)
|
|
|
|
listener->notifyOperationInserted(
|
2024-01-31 11:25:11 +01:00
|
|
|
op, /*previous=*/InsertPoint(currentBlock, nextIterator));
|
2024-01-25 11:01:28 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void RewriterBase::moveOpAfter(Operation *op, Operation *existingOp) {
|
|
|
|
moveOpAfter(op, existingOp->getBlock(), existingOp->getIterator());
|
|
|
|
}
|
|
|
|
|
|
|
|
void RewriterBase::moveOpAfter(Operation *op, Block *block,
|
|
|
|
Block::iterator iterator) {
|
2024-01-31 11:25:11 +01:00
|
|
|
assert(iterator != block->end() && "cannot move after end of block");
|
|
|
|
moveOpBefore(op, block, std::next(iterator));
|
2024-01-25 11:01:28 +01:00
|
|
|
}
|