10 #include "mlir/Config/mlir-config.h"
19 #include "llvm/ADT/ScopeExit.h"
20 #include "llvm/ADT/SetVector.h"
21 #include "llvm/ADT/SmallPtrSet.h"
22 #include "llvm/Support/Debug.h"
23 #include "llvm/Support/FormatVariadic.h"
24 #include "llvm/Support/SaveAndRestore.h"
25 #include "llvm/Support/ScopedPrinter.h"
31 #define DEBUG_TYPE "dialect-conversion"
34 template <
typename... Args>
35 static void logSuccess(llvm::ScopedPrinter &os, StringRef fmt, Args &&...args) {
38 os.startLine() <<
"} -> SUCCESS";
40 os.getOStream() <<
" : "
41 << llvm::formatv(fmt.data(), std::forward<Args>(args)...);
42 os.getOStream() <<
"\n";
47 template <
typename... Args>
48 static void logFailure(llvm::ScopedPrinter &os, StringRef fmt, Args &&...args) {
51 os.startLine() <<
"} -> FAILURE : "
52 << llvm::formatv(fmt.data(), std::forward<Args>(args)...)
62 if (
OpResult inputRes = dyn_cast<OpResult>(value))
63 insertPt = ++inputRes.getOwner()->getIterator();
70 assert(!vals.empty() &&
"expected at least one value");
73 for (
Value v : vals.drop_front()) {
87 assert(dom &&
"unable to find valid insertion point");
105 struct ValueVectorMapInfo {
108 static ::llvm::hash_code getHashValue(
const ValueVector &val) {
109 return ::llvm::hash_combine_range(val.begin(), val.end());
118 struct ConversionValueMapping {
121 bool isMappedTo(
Value value)
const {
return mappedTo.contains(value); }
140 template <
typename T>
141 struct IsValueVector : std::is_same<std::decay_t<T>, ValueVector> {};
144 template <
typename OldVal,
typename NewVal>
145 std::enable_if_t<IsValueVector<OldVal>::value && IsValueVector<NewVal>::value>
146 map(OldVal &&oldVal, NewVal &&newVal) {
150 assert(next != oldVal &&
"inserting cyclic mapping");
151 auto it = mapping.find(next);
152 if (it == mapping.end())
157 mappedTo.insert_range(newVal);
159 mapping[std::forward<OldVal>(oldVal)] = std::forward<NewVal>(newVal);
163 template <
typename OldVal,
typename NewVal>
164 std::enable_if_t<!IsValueVector<OldVal>::value ||
165 !IsValueVector<NewVal>::value>
166 map(OldVal &&oldVal, NewVal &&newVal) {
167 if constexpr (IsValueVector<OldVal>{}) {
168 map(std::forward<OldVal>(oldVal),
ValueVector{newVal});
169 }
else if constexpr (IsValueVector<NewVal>{}) {
170 map(
ValueVector{oldVal}, std::forward<NewVal>(newVal));
176 void map(
Value oldVal, SmallVector<Value> &&newVal) {
181 void erase(
const ValueVector &value) { mapping.erase(value); }
193 ConversionValueMapping::lookupOrDefault(
Value from,
202 desiredValue = current;
206 for (
Value v : current) {
207 auto it = mapping.find({v});
208 if (it != mapping.end()) {
209 llvm::append_range(next, it->second);
214 if (next != current) {
216 current = std::move(next);
228 auto it = mapping.find(current);
229 if (it == mapping.end()) {
233 current = it->second;
239 return !desiredValue.empty() ? std::move(desiredValue) : std::move(current);
244 ValueVector result = lookupOrDefault(from, desiredTypes);
257 struct RewriterState {
258 RewriterState(
unsigned numRewrites,
unsigned numIgnoredOperations,
259 unsigned numReplacedOps)
260 : numRewrites(numRewrites), numIgnoredOperations(numIgnoredOperations),
261 numReplacedOps(numReplacedOps) {}
264 unsigned numRewrites;
267 unsigned numIgnoredOperations;
270 unsigned numReplacedOps;
302 UnresolvedMaterialization
305 virtual ~IRRewrite() =
default;
308 virtual void rollback() = 0;
327 Kind getKind()
const {
return kind; }
329 static bool classof(
const IRRewrite *
rewrite) {
return true; }
333 :
kind(
kind), rewriterImpl(rewriterImpl) {}
342 class BlockRewrite :
public IRRewrite {
345 Block *getBlock()
const {
return block; }
347 static bool classof(
const IRRewrite *
rewrite) {
348 return rewrite->getKind() >= Kind::CreateBlock &&
349 rewrite->getKind() <= Kind::ReplaceBlockArg;
355 : IRRewrite(
kind, rewriterImpl), block(block) {}
364 class CreateBlockRewrite :
public BlockRewrite {
367 : BlockRewrite(
Kind::CreateBlock, rewriterImpl, block) {}
369 static bool classof(
const IRRewrite *
rewrite) {
370 return rewrite->getKind() == Kind::CreateBlock;
376 listener->notifyBlockInserted(block, {}, {});
379 void rollback()
override {
382 auto &blockOps = block->getOperations();
383 while (!blockOps.empty())
384 blockOps.remove(blockOps.begin());
385 block->dropAllUses();
386 if (block->getParent())
397 class EraseBlockRewrite :
public BlockRewrite {
400 : BlockRewrite(
Kind::EraseBlock, rewriterImpl, block),
401 region(block->getParent()), insertBeforeBlock(block->getNextNode()) {}
403 static bool classof(
const IRRewrite *
rewrite) {
404 return rewrite->getKind() == Kind::EraseBlock;
407 ~EraseBlockRewrite()
override {
409 "rewrite was neither rolled back nor committed/cleaned up");
412 void rollback()
override {
415 assert(block &&
"expected block");
416 auto &blockList = region->getBlocks();
420 blockList.insert(before, block);
426 assert(block &&
"expected block");
427 assert(block->empty() &&
"expected empty block");
431 dyn_cast_or_null<RewriterBase::Listener>(rewriter.
getListener()))
432 listener->notifyBlockErased(block);
437 block->dropAllDefinedValueUses();
448 Block *insertBeforeBlock;
454 class InlineBlockRewrite :
public BlockRewrite {
458 : BlockRewrite(
Kind::InlineBlock, rewriterImpl, block),
459 sourceBlock(sourceBlock),
460 firstInlinedInst(sourceBlock->empty() ? nullptr
461 : &sourceBlock->front()),
462 lastInlinedInst(sourceBlock->empty() ? nullptr : &sourceBlock->back()) {
468 assert(!getConfig().listener &&
469 "InlineBlockRewrite not supported if listener is attached");
472 static bool classof(
const IRRewrite *
rewrite) {
473 return rewrite->getKind() == Kind::InlineBlock;
476 void rollback()
override {
479 if (firstInlinedInst) {
480 assert(lastInlinedInst &&
"expected operation");
500 class MoveBlockRewrite :
public BlockRewrite {
504 : BlockRewrite(
Kind::MoveBlock, rewriterImpl, block), region(region),
505 insertBeforeBlock(insertBeforeBlock) {}
507 static bool classof(
const IRRewrite *
rewrite) {
508 return rewrite->getKind() == Kind::MoveBlock;
516 listener->notifyBlockInserted(block, region,
521 void rollback()
override {
534 Block *insertBeforeBlock;
538 class BlockTypeConversionRewrite :
public BlockRewrite {
542 : BlockRewrite(
Kind::BlockTypeConversion, rewriterImpl, origBlock),
543 newBlock(newBlock) {}
545 static bool classof(
const IRRewrite *
rewrite) {
546 return rewrite->getKind() == Kind::BlockTypeConversion;
549 Block *getOrigBlock()
const {
return block; }
551 Block *getNewBlock()
const {
return newBlock; }
555 void rollback()
override;
565 class ReplaceBlockArgRewrite :
public BlockRewrite {
570 : BlockRewrite(
Kind::ReplaceBlockArg, rewriterImpl, block), arg(arg),
571 converter(converter) {}
573 static bool classof(
const IRRewrite *
rewrite) {
574 return rewrite->getKind() == Kind::ReplaceBlockArg;
579 void rollback()
override;
589 class OperationRewrite :
public IRRewrite {
592 Operation *getOperation()
const {
return op; }
594 static bool classof(
const IRRewrite *
rewrite) {
595 return rewrite->getKind() >= Kind::MoveOperation &&
596 rewrite->getKind() <= Kind::UnresolvedMaterialization;
602 : IRRewrite(
kind, rewriterImpl), op(op) {}
609 class MoveOperationRewrite :
public OperationRewrite {
613 : OperationRewrite(
Kind::MoveOperation, rewriterImpl, op), block(block),
614 insertBeforeOp(insertBeforeOp) {}
616 static bool classof(
const IRRewrite *
rewrite) {
617 return rewrite->getKind() == Kind::MoveOperation;
625 listener->notifyOperationInserted(
631 void rollback()
override {
635 block->
getOperations().splice(before, op->getBlock()->getOperations(), op);
649 class ModifyOperationRewrite :
public OperationRewrite {
653 : OperationRewrite(
Kind::ModifyOperation, rewriterImpl, op),
654 name(op->getName()), loc(op->getLoc()), attrs(op->getAttrDictionary()),
655 operands(op->operand_begin(), op->operand_end()),
656 successors(op->successor_begin(), op->successor_end()) {
661 name.initOpProperties(propCopy, prop);
665 static bool classof(
const IRRewrite *
rewrite) {
666 return rewrite->getKind() == Kind::ModifyOperation;
669 ~ModifyOperationRewrite()
override {
670 assert(!propertiesStorage &&
671 "rewrite was neither committed nor rolled back");
677 dyn_cast_or_null<RewriterBase::Listener>(rewriter.
getListener()))
678 listener->notifyOperationModified(op);
680 if (propertiesStorage) {
684 name.destroyOpProperties(propCopy);
685 operator delete(propertiesStorage);
686 propertiesStorage =
nullptr;
690 void rollback()
override {
696 if (propertiesStorage) {
699 name.destroyOpProperties(propCopy);
700 operator delete(propertiesStorage);
701 propertiesStorage =
nullptr;
708 DictionaryAttr attrs;
709 SmallVector<Value, 8> operands;
710 SmallVector<Block *, 2> successors;
711 void *propertiesStorage =
nullptr;
718 class ReplaceOperationRewrite :
public OperationRewrite {
722 : OperationRewrite(
Kind::ReplaceOperation, rewriterImpl, op),
723 converter(converter) {}
725 static bool classof(
const IRRewrite *
rewrite) {
726 return rewrite->getKind() == Kind::ReplaceOperation;
731 void rollback()
override;
741 class CreateOperationRewrite :
public OperationRewrite {
745 : OperationRewrite(
Kind::CreateOperation, rewriterImpl, op) {}
747 static bool classof(
const IRRewrite *
rewrite) {
748 return rewrite->getKind() == Kind::CreateOperation;
754 listener->notifyOperationInserted(op, {});
757 void rollback()
override;
761 enum MaterializationKind {
774 class UnresolvedMaterializationRewrite :
public OperationRewrite {
777 UnrealizedConversionCastOp op,
779 MaterializationKind
kind,
Type originalType,
782 static bool classof(
const IRRewrite *
rewrite) {
783 return rewrite->getKind() == Kind::UnresolvedMaterialization;
786 void rollback()
override;
788 UnrealizedConversionCastOp getOperation()
const {
789 return cast<UnrealizedConversionCastOp>(op);
794 return converterAndKind.getPointer();
798 MaterializationKind getMaterializationKind()
const {
799 return converterAndKind.getInt();
803 Type getOriginalType()
const {
return originalType; }
808 llvm::PointerIntPair<const TypeConverter *, 2, MaterializationKind>
821 #if MLIR_ENABLE_EXPENSIVE_PATTERN_API_CHECKS
824 template <
typename RewriteTy,
typename R>
825 static bool hasRewrite(R &&rewrites,
Operation *op) {
826 return any_of(std::forward<R>(rewrites), [&](
auto &
rewrite) {
827 auto *rewriteTy = dyn_cast<RewriteTy>(
rewrite.get());
828 return rewriteTy && rewriteTy->getOperation() == op;
834 template <
typename RewriteTy,
typename R>
835 static bool hasRewrite(R &&rewrites,
Block *block) {
836 return any_of(std::forward<R>(rewrites), [&](
auto &
rewrite) {
837 auto *rewriteTy = dyn_cast<RewriteTy>(
rewrite.get());
838 return rewriteTy && rewriteTy->getBlock() == block;
858 RewriterState getCurrentState();
862 void applyRewrites();
865 void resetState(RewriterState state);
869 template <
typename RewriteTy,
typename... Args>
872 std::make_unique<RewriteTy>(*
this, std::forward<Args>(args)...));
877 void undoRewrites(
unsigned numRewritesToKeep = 0);
883 LogicalResult remapValues(StringRef valueDiagTag,
884 std::optional<Location> inputLoc,
911 Block *applySignatureConversion(
934 UnrealizedConversionCastOp *castOp =
nullptr);
941 Value findOrBuildReplacementValue(
Value value,
949 void notifyOperationInserted(
Operation *op,
957 void notifyBlockIsBeingErased(
Block *block);
960 void notifyBlockInserted(
Block *block,
Region *previous,
964 void notifyBlockBeingInlined(
Block *block,
Block *srcBlock,
994 if (wasErased(block))
996 assert(block->
empty() &&
"expected empty block");
1001 bool wasErased(
void *ptr)
const {
return erased.contains(ptr); }
1065 llvm::ScopedPrinter logger{llvm::dbgs()};
1072 return rewriterImpl.
config;
1075 void BlockTypeConversionRewrite::commit(
RewriterBase &rewriter) {
1079 if (
auto *listener =
1080 dyn_cast_or_null<RewriterBase::Listener>(rewriter.
getListener()))
1081 for (
Operation *op : getNewBlock()->getUsers())
1082 listener->notifyOperationModified(op);
1085 void BlockTypeConversionRewrite::rollback() {
1086 getNewBlock()->replaceAllUsesWith(getOrigBlock());
1089 void ReplaceBlockArgRewrite::commit(
RewriterBase &rewriter) {
1094 if (isa<BlockArgument>(repl)) {
1102 Operation *replOp = cast<OpResult>(repl).getOwner();
1110 void ReplaceBlockArgRewrite::rollback() { rewriterImpl.
mapping.erase({arg}); }
1112 void ReplaceOperationRewrite::commit(
RewriterBase &rewriter) {
1114 dyn_cast_or_null<RewriterBase::Listener>(rewriter.
getListener());
1117 SmallVector<Value> replacements =
1119 return rewriterImpl.findOrBuildReplacementValue(result, converter);
1124 listener->notifyOperationReplaced(op, replacements);
1127 for (
auto [result, newValue] :
1128 llvm::zip_equal(op->
getResults(), replacements))
1134 if (getConfig().unlegalizedOps)
1135 getConfig().unlegalizedOps->erase(op);
1141 [&](
Operation *op) { listener->notifyOperationErased(op); });
1149 void ReplaceOperationRewrite::rollback() {
1151 rewriterImpl.
mapping.erase({result});
1154 void ReplaceOperationRewrite::cleanup(
RewriterBase &rewriter) {
1158 void CreateOperationRewrite::rollback() {
1160 while (!region.getBlocks().empty())
1161 region.getBlocks().remove(region.getBlocks().begin());
1167 UnresolvedMaterializationRewrite::UnresolvedMaterializationRewrite(
1171 : OperationRewrite(
Kind::UnresolvedMaterialization, rewriterImpl, op),
1172 converterAndKind(converter,
kind), originalType(originalType),
1173 mappedValues(std::move(mappedValues)) {
1174 assert((!originalType || kind == MaterializationKind::Target) &&
1175 "original type is valid only for target materializations");
1179 void UnresolvedMaterializationRewrite::rollback() {
1180 if (!mappedValues.empty())
1181 rewriterImpl.
mapping.erase(mappedValues);
1191 for (
size_t i = 0; i <
rewrites.size(); ++i)
1212 while (
ignoredOps.size() != state.numIgnoredOperations)
1215 while (
replacedOps.size() != state.numReplacedOps)
1221 llvm::reverse(llvm::drop_begin(
rewrites, numRewritesToKeep)))
1223 rewrites.resize(numRewritesToKeep);
1227 StringRef valueDiagTag, std::optional<Location> inputLoc,
1230 remapped.reserve(llvm::size(values));
1233 Value operand = it.value();
1241 remapped.push_back(
mapping.lookupOrDefault(operand));
1249 diag <<
"unable to convert type for " << valueDiagTag <<
" #"
1250 << it.index() <<
", type was " << origType;
1255 if (legalTypes.empty()) {
1256 remapped.push_back({});
1265 remapped.push_back(std::move(repl));
1270 repl =
mapping.lookupOrDefault(operand);
1273 repl, repl, legalTypes,
1275 remapped.push_back(castValues);
1299 if (region->
empty())
1304 llvm::make_early_inc_range(llvm::drop_begin(*region, 1))) {
1306 std::optional<TypeConverter::SignatureConversion> conversion =
1316 if (entryConversion)
1319 std::optional<TypeConverter::SignatureConversion> conversion =
1331 #if MLIR_ENABLE_EXPENSIVE_PATTERN_API_CHECKS
1333 if (hasRewrite<BlockTypeConversionRewrite>(
rewrites, block))
1334 llvm::report_fatal_error(
"block was already converted");
1348 for (
unsigned i = 0; i < origArgCount; ++i) {
1350 if (!inputMap || inputMap->replacedWithValues())
1353 for (
unsigned j = 0;
j < inputMap->size; ++
j)
1354 newLocs[inputMap->inputNo +
j] = origLoc;
1361 convertedTypes, newLocs);
1371 appendRewrite<InlineBlockRewrite>(newBlock, block, newBlock->
end());
1374 while (!block->
empty())
1381 for (
unsigned i = 0; i != origArgCount; ++i) {
1385 std::optional<TypeConverter::SignatureConversion::InputMapping> inputMap =
1391 MaterializationKind::Source,
1394 origArgType,
Type(), converter);
1395 appendRewrite<ReplaceBlockArgRewrite>(block, origArg, converter);
1399 if (inputMap->replacedWithValues()) {
1401 assert(inputMap->size == 0 &&
1402 "invalid to provide a replacement value when the argument isn't "
1404 mapping.map(origArg, inputMap->replacementValues);
1405 appendRewrite<ReplaceBlockArgRewrite>(block, origArg, converter);
1411 newBlock->
getArguments().slice(inputMap->inputNo, inputMap->size);
1412 ValueVector replArgVals = llvm::to_vector_of<Value, 1>(replArgs);
1413 mapping.map(origArg, std::move(replArgVals));
1414 appendRewrite<ReplaceBlockArgRewrite>(block, origArg, converter);
1417 appendRewrite<BlockTypeConversionRewrite>(block, newBlock);
1436 UnrealizedConversionCastOp *castOp) {
1437 assert((!originalType ||
kind == MaterializationKind::Target) &&
1438 "original type is valid only for target materializations");
1439 assert(
TypeRange(inputs) != outputTypes &&
1440 "materialization is not necessary");
1444 OpBuilder builder(outputTypes.front().getContext());
1447 builder.
create<UnrealizedConversionCastOp>(loc, outputTypes, inputs);
1448 if (!valuesToMap.empty())
1449 mapping.map(valuesToMap, convertOp.getResults());
1451 *castOp = convertOp;
1452 appendRewrite<UnresolvedMaterializationRewrite>(
1453 convertOp, converter,
kind, originalType, std::move(valuesToMap));
1454 return convertOp.getResults();
1464 return repl.front();
1471 [&](
Operation *op) { return replacedOps.contains(op); }) &&
1478 repl =
mapping.lookupOrNull(value);
1512 logger.startLine() <<
"** Insert : '" << op->
getName() <<
"'(" << op
1516 "attempting to insert into a block within a replaced/erased op");
1518 if (!previous.
isSet()) {
1520 appendRewrite<CreateOperationRewrite>(op);
1526 appendRewrite<MoveOperationRewrite>(op, previous.
getBlock(), prevOp);
1532 assert(!
ignoredOps.contains(op) &&
"operation was already replaced");
1536 bool isUnresolvedMaterialization =
false;
1537 if (
auto castOp = dyn_cast<UnrealizedConversionCastOp>(op))
1539 isUnresolvedMaterialization =
true;
1542 for (
auto [repl, result] : llvm::zip_equal(newValues, op->
getResults())) {
1545 if (isUnresolvedMaterialization) {
1565 assert(!isUnresolvedMaterialization &&
1566 "attempting to replace an unresolved materialization");
1572 mapping.map(
static_cast<Value>(result), std::move(repl));
1581 appendRewrite<EraseBlockRewrite>(block);
1587 "attempting to insert into a region within a replaced/erased op");
1592 logger.startLine() <<
"** Insert Block into : '" << parent->
getName()
1593 <<
"'(" << parent <<
")\n";
1596 <<
"** Insert Block into detached Region (nullptr parent op)'";
1602 appendRewrite<CreateBlockRewrite>(block);
1605 Block *prevBlock = previousIt == previous->
end() ? nullptr : &*previousIt;
1606 appendRewrite<MoveBlockRewrite>(block, previous, prevBlock);
1611 appendRewrite<InlineBlockRewrite>(block, srcBlock, before);
1618 reasonCallback(
diag);
1619 logger.startLine() <<
"** Failure : " <<
diag.str() <<
"\n";
1629 ConversionPatternRewriter::ConversionPatternRewriter(
1633 setListener(
impl.get());
1639 assert(op && newOp &&
"expected non-null op");
1645 "incorrect # of replacement values");
1647 impl->logger.startLine()
1648 <<
"** Replace : '" << op->
getName() <<
"'(" << op <<
")\n";
1654 impl->notifyOpReplaced(op, std::move(newVals));
1660 "incorrect # of replacement values");
1662 impl->logger.startLine()
1663 <<
"** Replace : '" << op->
getName() <<
"'(" << op <<
")\n";
1665 impl->notifyOpReplaced(op, std::move(newValues));
1670 impl->logger.startLine()
1671 <<
"** Erase : '" << op->
getName() <<
"'(" << op <<
")\n";
1674 impl->notifyOpReplaced(op, std::move(nullRepls));
1679 "attempting to erase a block within a replaced/erased op");
1689 impl->notifyBlockIsBeingErased(block);
1697 "attempting to apply a signature conversion to a block within a "
1698 "replaced/erased op");
1699 return impl->applySignatureConversion(*
this, block, converter, conversion);
1706 "attempting to apply a signature conversion to a block within a "
1707 "replaced/erased op");
1708 return impl->convertRegionTypes(*
this, region, converter, entryConversion);
1714 impl->logger.startLine() <<
"** Replace Argument : '" << from <<
"'";
1716 impl->logger.getOStream() <<
" (in region of '" << parentOp->getName()
1717 <<
"' (" << parentOp <<
")\n";
1719 impl->logger.getOStream() <<
" (unlinked block)\n";
1722 impl->appendRewrite<ReplaceBlockArgRewrite>(from.
getOwner(), from,
1723 impl->currentTypeConverter);
1724 impl->mapping.map(
impl->mapping.lookupOrDefault(from), to);
1729 if (failed(
impl->remapValues(
"value", std::nullopt, *
this, key,
1732 assert(remappedValues.front().size() == 1 &&
"1:N conversion not supported");
1733 return remappedValues.front().front();
1742 if (failed(
impl->remapValues(
"value", std::nullopt, *
this, keys,
1745 for (
const auto &values : remapped) {
1746 assert(values.size() == 1 &&
"1:N conversion not supported");
1747 results.push_back(values.front());
1757 "incorrect # of argument replacement values");
1759 "attempting to inline a block from a replaced/erased op");
1761 "attempting to inline a block into a replaced/erased op");
1762 auto opIgnored = [&](
Operation *op) {
return impl->isOpIgnored(op); };
1765 assert(llvm::all_of(source->
getUsers(), opIgnored) &&
1766 "expected 'source' to have no predecessors");
1775 bool fastPath = !
impl->config.listener;
1778 impl->notifyBlockBeingInlined(dest, source, before);
1781 for (
auto it : llvm::zip(source->
getArguments(), argValues))
1782 replaceUsesOfBlockArgument(std::get<0>(it), std::get<1>(it));
1789 while (!source->
empty())
1790 moveOpBefore(&source->
front(), dest, before);
1798 assert(!
impl->wasOpReplaced(op) &&
1799 "attempting to modify a replaced/erased op");
1801 impl->pendingRootUpdates.insert(op);
1803 impl->appendRewrite<ModifyOperationRewrite>(op);
1807 assert(!
impl->wasOpReplaced(op) &&
1808 "attempting to modify a replaced/erased op");
1813 assert(
impl->pendingRootUpdates.erase(op) &&
1814 "operation did not have a pending in-place update");
1820 assert(
impl->pendingRootUpdates.erase(op) &&
1821 "operation did not have a pending in-place update");
1824 auto it = llvm::find_if(
1825 llvm::reverse(
impl->rewrites), [&](std::unique_ptr<IRRewrite> &
rewrite) {
1826 auto *modifyRewrite = dyn_cast<ModifyOperationRewrite>(rewrite.get());
1827 return modifyRewrite && modifyRewrite->getOperation() == op;
1829 assert(it !=
impl->rewrites.rend() &&
"no root update started on op");
1831 int updateIdx = std::prev(
impl->rewrites.rend()) - it;
1832 impl->rewrites.erase(
impl->rewrites.begin() + updateIdx);
1846 oneToOneOperands.reserve(operands.size());
1848 if (operand.size() != 1)
1849 llvm::report_fatal_error(
"pattern '" + getDebugName() +
1850 "' does not support 1:N conversion");
1851 oneToOneOperands.push_back(operand.front());
1853 return oneToOneOperands;
1860 auto &rewriterImpl = dialectRewriter.getImpl();
1864 getTypeConverter());
1873 llvm::to_vector_of<ValueRange>(remapped);
1874 return matchAndRewrite(op, remappedAsRange, dialectRewriter);
1886 class OperationLegalizer {
1906 LogicalResult legalizeWithFold(
Operation *op,
1911 LogicalResult legalizeWithPattern(
Operation *op,
1922 RewriterState &curState);
1926 legalizePatternBlockRewrites(
Operation *op,
1929 RewriterState &state, RewriterState &newState);
1930 LogicalResult legalizePatternCreatedOperations(
1932 RewriterState &state, RewriterState &newState);
1935 RewriterState &state,
1936 RewriterState &newState);
1946 void buildLegalizationGraph(
1947 LegalizationPatterns &anyOpLegalizerPatterns,
1958 void computeLegalizationGraphBenefit(
1959 LegalizationPatterns &anyOpLegalizerPatterns,
1964 unsigned computeOpLegalizationDepth(
1971 unsigned applyCostModelToPatterns(
1997 LegalizationPatterns anyOpLegalizerPatterns;
1999 buildLegalizationGraph(anyOpLegalizerPatterns, legalizerPatterns);
2000 computeLegalizationGraphBenefit(anyOpLegalizerPatterns, legalizerPatterns);
2003 bool OperationLegalizer::isIllegal(
Operation *op)
const {
2004 return target.isIllegal(op);
2008 OperationLegalizer::legalize(
Operation *op,
2011 const char *logLineComment =
2012 "//===-------------------------------------------===//\n";
2017 logger.getOStream() <<
"\n";
2018 logger.startLine() << logLineComment;
2019 logger.startLine() <<
"Legalizing operation : '" << op->
getName() <<
"'("
2025 op->print(logger.startLine(), OpPrintingFlags().printGenericOpForm());
2026 logger.getOStream() <<
"\n\n";
2031 if (
auto legalityInfo = target.isLegal(op)) {
2034 logger,
"operation marked legal by the target{0}",
2035 legalityInfo->isRecursivelyLegal
2036 ?
"; NOTE: operation is recursively legal; skipping internals"
2038 logger.startLine() << logLineComment;
2043 if (legalityInfo->isRecursivelyLegal) {
2056 logSuccess(logger,
"operation marked 'ignored' during conversion");
2057 logger.startLine() << logLineComment;
2065 if (succeeded(legalizeWithFold(op, rewriter))) {
2068 logger.startLine() << logLineComment;
2074 if (succeeded(legalizeWithPattern(op, rewriter))) {
2077 logger.startLine() << logLineComment;
2083 logFailure(logger,
"no matched legalization pattern");
2084 logger.startLine() << logLineComment;
2090 OperationLegalizer::legalizeWithFold(
Operation *op,
2092 auto &rewriterImpl = rewriter.
getImpl();
2096 rewriterImpl.
logger.startLine() <<
"* Fold {\n";
2097 rewriterImpl.
logger.indent();
2101 SmallVector<Value, 2> replacementValues;
2103 if (failed(rewriter.
tryFold(op, replacementValues))) {
2109 if (replacementValues.empty())
2110 return legalize(op, rewriter);
2113 for (
unsigned i = curState.numRewrites, e = rewriterImpl.
rewrites.size();
2116 dyn_cast<CreateOperationRewrite>(rewriterImpl.
rewrites[i].get());
2119 if (failed(legalize(createOp->getOperation(), rewriter))) {
2121 "failed to legalize generated constant '{0}'",
2122 createOp->getOperation()->getName()));
2129 rewriter.
replaceOp(op, replacementValues);
2136 OperationLegalizer::legalizeWithPattern(
Operation *op,
2138 auto &rewriterImpl = rewriter.
getImpl();
2141 auto canApply = [&](
const Pattern &pattern) {
2142 bool canApply = canApplyPattern(op, pattern, rewriter);
2143 if (canApply &&
config.listener)
2144 config.listener->notifyPatternBegin(pattern, op);
2150 auto onFailure = [&](
const Pattern &pattern) {
2156 diag <<
"Failed to apply pattern \"" << pattern.getDebugName()
2163 config.listener->notifyPatternEnd(pattern, failure());
2165 appliedPatterns.erase(&pattern);
2170 auto onSuccess = [&](
const Pattern &pattern) {
2172 auto result = legalizePatternResult(op, pattern, rewriter, curState);
2173 appliedPatterns.erase(&pattern);
2177 config.listener->notifyPatternEnd(pattern, result);
2182 return applicator.matchAndRewrite(op, rewriter, canApply, onFailure,
2186 bool OperationLegalizer::canApplyPattern(
Operation *op,
const Pattern &pattern,
2189 auto &os = rewriter.
getImpl().logger;
2190 os.getOStream() <<
"\n";
2191 os.startLine() <<
"* Pattern : '" << op->
getName() <<
" -> (";
2193 os.getOStream() <<
")' {\n";
2200 !appliedPatterns.insert(&pattern).second) {
2209 OperationLegalizer::legalizePatternResult(
Operation *op,
const Pattern &pattern,
2211 RewriterState &curState) {
2213 assert(
impl.pendingRootUpdates.empty() &&
"dangling root updates");
2215 #if MLIR_ENABLE_EXPENSIVE_PATTERN_API_CHECKS
2217 auto newRewrites = llvm::drop_begin(
impl.rewrites, curState.numRewrites);
2218 auto replacedRoot = [&] {
2219 return hasRewrite<ReplaceOperationRewrite>(newRewrites, op);
2221 auto updatedRootInPlace = [&] {
2222 return hasRewrite<ModifyOperationRewrite>(newRewrites, op);
2224 if (!replacedRoot() && !updatedRootInPlace())
2225 llvm::report_fatal_error(
"expected pattern to replace the root operation");
2229 RewriterState newState =
impl.getCurrentState();
2230 if (failed(legalizePatternBlockRewrites(op, rewriter,
impl, curState,
2232 failed(legalizePatternRootUpdates(rewriter,
impl, curState, newState)) ||
2233 failed(legalizePatternCreatedOperations(rewriter,
impl, curState,
2238 LLVM_DEBUG(
logSuccess(
impl.logger,
"pattern applied successfully"));
2242 LogicalResult OperationLegalizer::legalizePatternBlockRewrites(
2245 RewriterState &newState) {
2250 for (
int i = state.numRewrites, e = newState.numRewrites; i != e; ++i) {
2251 BlockRewrite *
rewrite = dyn_cast<BlockRewrite>(
impl.rewrites[i].get());
2255 if (isa<BlockTypeConversionRewrite, EraseBlockRewrite,
2256 ReplaceBlockArgRewrite>(
rewrite))
2265 if (
auto *converter =
impl.regionToConverter.lookup(block->
getParent())) {
2266 std::optional<TypeConverter::SignatureConversion> conversion =
2269 LLVM_DEBUG(
logFailure(
impl.logger,
"failed to convert types of moved "
2273 impl.applySignatureConversion(rewriter, block, converter, *conversion);
2281 if (operationsToIgnore.empty()) {
2282 for (
unsigned i = state.numRewrites, e =
impl.rewrites.size(); i != e;
2285 dyn_cast<CreateOperationRewrite>(
impl.rewrites[i].get());
2288 operationsToIgnore.insert(createOp->getOperation());
2293 if (operationsToIgnore.insert(parentOp).second &&
2294 failed(legalize(parentOp, rewriter))) {
2296 "operation '{0}'({1}) became illegal after rewrite",
2297 parentOp->
getName(), parentOp));
2304 LogicalResult OperationLegalizer::legalizePatternCreatedOperations(
2306 RewriterState &state, RewriterState &newState) {
2307 for (
int i = state.numRewrites, e = newState.numRewrites; i != e; ++i) {
2308 auto *createOp = dyn_cast<CreateOperationRewrite>(
impl.rewrites[i].get());
2311 Operation *op = createOp->getOperation();
2312 if (failed(legalize(op, rewriter))) {
2314 "failed to legalize generated operation '{0}'({1})",
2322 LogicalResult OperationLegalizer::legalizePatternRootUpdates(
2324 RewriterState &state, RewriterState &newState) {
2325 for (
int i = state.numRewrites, e = newState.numRewrites; i != e; ++i) {
2326 auto *
rewrite = dyn_cast<ModifyOperationRewrite>(
impl.rewrites[i].get());
2330 if (failed(legalize(op, rewriter))) {
2332 impl.logger,
"failed to legalize operation updated in-place '{0}'",
2344 void OperationLegalizer::buildLegalizationGraph(
2345 LegalizationPatterns &anyOpLegalizerPatterns,
2356 applicator.walkAllPatterns([&](
const Pattern &pattern) {
2357 std::optional<OperationName> root = pattern.
getRootKind();
2363 anyOpLegalizerPatterns.push_back(&pattern);
2368 if (target.getOpAction(*root) == LegalizationAction::Legal)
2373 invalidPatterns[*root].insert(&pattern);
2375 parentOps[op].insert(*root);
2378 patternWorklist.insert(&pattern);
2386 if (!anyOpLegalizerPatterns.empty()) {
2387 for (
const Pattern *pattern : patternWorklist)
2388 legalizerPatterns[*pattern->
getRootKind()].push_back(pattern);
2392 while (!patternWorklist.empty()) {
2393 auto *pattern = patternWorklist.pop_back_val();
2397 std::optional<LegalizationAction> action = target.getOpAction(op);
2398 return !legalizerPatterns.count(op) &&
2399 (!action || action == LegalizationAction::Illegal);
2405 legalizerPatterns[*pattern->
getRootKind()].push_back(pattern);
2406 invalidPatterns[*pattern->
getRootKind()].erase(pattern);
2410 for (
auto op : parentOps[*pattern->
getRootKind()])
2411 patternWorklist.set_union(invalidPatterns[op]);
2415 void OperationLegalizer::computeLegalizationGraphBenefit(
2416 LegalizationPatterns &anyOpLegalizerPatterns,
2422 for (
auto &opIt : legalizerPatterns)
2423 if (!minOpPatternDepth.count(opIt.first))
2424 computeOpLegalizationDepth(opIt.first, minOpPatternDepth,
2430 if (!anyOpLegalizerPatterns.empty())
2431 applyCostModelToPatterns(anyOpLegalizerPatterns, minOpPatternDepth,
2437 applicator.applyCostModel([&](
const Pattern &pattern) {
2439 if (std::optional<OperationName> rootName = pattern.
getRootKind())
2440 orderedPatternList = legalizerPatterns[*rootName];
2442 orderedPatternList = anyOpLegalizerPatterns;
2445 auto *it = llvm::find(orderedPatternList, &pattern);
2446 if (it == orderedPatternList.end())
2450 return PatternBenefit(std::distance(it, orderedPatternList.end()));
2454 unsigned OperationLegalizer::computeOpLegalizationDepth(
2458 auto depthIt = minOpPatternDepth.find(op);
2459 if (depthIt != minOpPatternDepth.end())
2460 return depthIt->second;
2464 auto opPatternsIt = legalizerPatterns.find(op);
2465 if (opPatternsIt == legalizerPatterns.end() || opPatternsIt->second.empty())
2474 unsigned minDepth = applyCostModelToPatterns(
2475 opPatternsIt->second, minOpPatternDepth, legalizerPatterns);
2476 minOpPatternDepth[op] = minDepth;
2480 unsigned OperationLegalizer::applyCostModelToPatterns(
2487 SmallVector<std::pair<const Pattern *, unsigned>, 4> patternsByDepth;
2488 patternsByDepth.reserve(
patterns.size());
2492 unsigned generatedOpDepth = computeOpLegalizationDepth(
2493 generatedOp, minOpPatternDepth, legalizerPatterns);
2494 depth =
std::max(depth, generatedOpDepth + 1);
2496 patternsByDepth.emplace_back(pattern, depth);
2499 minDepth =
std::min(minDepth, depth);
2504 if (patternsByDepth.size() == 1)
2508 std::stable_sort(patternsByDepth.begin(), patternsByDepth.end(),
2509 [](
const std::pair<const Pattern *, unsigned> &lhs,
2510 const std::pair<const Pattern *, unsigned> &rhs) {
2513 if (lhs.second != rhs.second)
2514 return lhs.second < rhs.second;
2517 auto lhsBenefit = lhs.first->getBenefit();
2518 auto rhsBenefit = rhs.first->getBenefit();
2519 return lhsBenefit > rhsBenefit;
2524 for (
auto &patternIt : patternsByDepth)
2525 patterns.push_back(patternIt.first);
2533 enum OpConversionMode {
2556 OpConversionMode mode)
2571 OperationLegalizer opLegalizer;
2574 OpConversionMode mode;
2581 if (failed(opLegalizer.legalize(op, rewriter))) {
2584 if (mode == OpConversionMode::Full)
2586 <<
"failed to legalize operation '" << op->
getName() <<
"'";
2590 if (mode == OpConversionMode::Partial) {
2591 if (opLegalizer.isIllegal(op))
2593 <<
"failed to legalize operation '" << op->
getName()
2594 <<
"' that was explicitly marked illegal";
2598 }
else if (mode == OpConversionMode::Analysis) {
2608 static LogicalResult
2610 UnresolvedMaterializationRewrite *
rewrite) {
2611 UnrealizedConversionCastOp op =
rewrite->getOperation();
2612 assert(!op.use_empty() &&
2613 "expected that dead materializations have already been DCE'd");
2619 SmallVector<Value> newMaterialization;
2620 switch (
rewrite->getMaterializationKind()) {
2621 case MaterializationKind::Target:
2623 rewriter, op->getLoc(), op.getResultTypes(), inputOperands,
2626 case MaterializationKind::Source:
2627 assert(op->getNumResults() == 1 &&
"expected single result");
2629 rewriter, op->getLoc(), op.getResultTypes().front(), inputOperands);
2631 newMaterialization.push_back(sourceMat);
2634 if (!newMaterialization.empty()) {
2636 ValueRange newMaterializationRange(newMaterialization);
2637 assert(
TypeRange(newMaterializationRange) == op.getResultTypes() &&
2638 "materialization callback produced value of incorrect type");
2640 rewriter.
replaceOp(op, newMaterialization);
2646 <<
"failed to legalize unresolved materialization "
2648 << inputOperands.
getTypes() <<
") to ("
2649 << op.getResultTypes()
2650 <<
") that remained live after conversion";
2651 diag.attachNote(op->getUsers().begin()->getLoc())
2652 <<
"see existing live user here: " << *op->getUsers().begin();
2663 for (
auto *op : ops) {
2666 toConvert.push_back(op);
2669 auto legalityInfo = target.
isLegal(op);
2670 if (legalityInfo && legalityInfo->isRecursivelyLegal)
2680 for (
auto *op : toConvert)
2681 if (failed(convert(rewriter, op)))
2691 for (
auto it : materializations) {
2694 allCastOps.push_back(it.first);
2706 for (UnrealizedConversionCastOp castOp : remainingCastOps) {
2707 auto it = materializations.find(castOp);
2708 assert(it != materializations.end() &&
"inconsistent state");
2731 auto enqueueOperands = [&](UnrealizedConversionCastOp castOp) {
2732 for (
Value v : castOp.getInputs())
2733 if (
auto inputCastOp = v.getDefiningOp<UnrealizedConversionCastOp>())
2734 worklist.insert(inputCastOp);
2741 [](UnrealizedConversionCastOp castOp) -> UnrealizedConversionCastOp {
2742 if (castOp.getInputs().empty())
2745 castOp.getInputs().front().getDefiningOp<UnrealizedConversionCastOp>();
2748 if (inputCastOp.getOutputs() != castOp.getInputs())
2754 while (!worklist.empty()) {
2755 UnrealizedConversionCastOp castOp = worklist.pop_back_val();
2756 if (castOp->use_empty()) {
2759 enqueueOperands(castOp);
2760 if (remainingCastOps)
2761 erasedOps.insert(castOp.getOperation());
2768 UnrealizedConversionCastOp nextCast = castOp;
2770 if (nextCast.getInputs().getTypes() == castOp.getResultTypes()) {
2774 enqueueOperands(castOp);
2775 castOp.replaceAllUsesWith(nextCast.getInputs());
2776 if (remainingCastOps)
2777 erasedOps.insert(castOp.getOperation());
2781 nextCast = getInputCast(nextCast);
2785 if (remainingCastOps)
2786 for (UnrealizedConversionCastOp op : castOps)
2787 if (!erasedOps.contains(op.getOperation()))
2788 remainingCastOps->push_back(op);
2797 assert(!types.empty() &&
"expected valid types");
2798 remapInput(origInputNo, argTypes.size(), types.size());
2803 assert(!types.empty() &&
2804 "1->0 type remappings don't need to be added explicitly");
2805 argTypes.append(types.begin(), types.end());
2809 unsigned newInputNo,
2810 unsigned newInputCount) {
2811 assert(!remappedInputs[origInputNo] &&
"input has already been remapped");
2812 assert(newInputCount != 0 &&
"expected valid input count");
2813 remappedInputs[origInputNo] =
2814 InputMapping{newInputNo, newInputCount, {}};
2819 assert(!remappedInputs[origInputNo] &&
"input has already been remapped");
2827 assert(t &&
"expected non-null type");
2830 std::shared_lock<decltype(cacheMutex)> cacheReadLock(cacheMutex,
2833 cacheReadLock.lock();
2834 auto existingIt = cachedDirectConversions.find(t);
2835 if (existingIt != cachedDirectConversions.end()) {
2836 if (existingIt->second)
2837 results.push_back(existingIt->second);
2838 return success(existingIt->second !=
nullptr);
2840 auto multiIt = cachedMultiConversions.find(t);
2841 if (multiIt != cachedMultiConversions.end()) {
2842 results.append(multiIt->second.begin(), multiIt->second.end());
2848 size_t currentCount = results.size();
2850 std::unique_lock<decltype(cacheMutex)> cacheWriteLock(cacheMutex,
2853 for (
const ConversionCallbackFn &converter : llvm::reverse(conversions)) {
2854 if (std::optional<LogicalResult> result = converter(t, results)) {
2856 cacheWriteLock.lock();
2857 if (!succeeded(*result)) {
2858 cachedDirectConversions.try_emplace(t,
nullptr);
2861 auto newTypes =
ArrayRef<Type>(results).drop_front(currentCount);
2862 if (newTypes.size() == 1)
2863 cachedDirectConversions.try_emplace(t, newTypes.front());
2865 cachedMultiConversions.try_emplace(t, llvm::to_vector<2>(newTypes));
2879 return results.size() == 1 ? results.front() :
nullptr;
2885 for (
Type type : types)
2899 return llvm::all_of(*region, [
this](
Block &block) {
2905 return isLegal(llvm::concat<const Type>(ty.getInputs(), ty.getResults()));
2917 if (convertedTypes.empty())
2921 result.
addInputs(inputNo, convertedTypes);
2927 unsigned origInputOffset)
const {
2928 for (
unsigned i = 0, e = types.size(); i != e; ++i)
2938 for (
const MaterializationCallbackFn &fn :
2939 llvm::reverse(argumentMaterializations))
2940 if (
Value result = fn(builder, resultType, inputs, loc))
2948 for (
const MaterializationCallbackFn &fn :
2949 llvm::reverse(sourceMaterializations))
2950 if (
Value result = fn(builder, resultType, inputs, loc))
2958 Type originalType)
const {
2960 builder, loc,
TypeRange(resultType), inputs, originalType);
2963 assert(result.size() == 1 &&
"expected single result");
2964 return result.front();
2969 Type originalType)
const {
2970 for (
const TargetMaterializationCallbackFn &fn :
2971 llvm::reverse(targetMaterializations)) {
2973 fn(builder, resultTypes, inputs, loc, originalType);
2977 "callback produced incorrect number of values or values with "
2984 std::optional<TypeConverter::SignatureConversion>
2988 return std::nullopt;
3011 return impl.getInt() == resultTag;
3015 return impl.getInt() == naTag;
3019 return impl.getInt() == abortTag;
3023 assert(hasResult() &&
"Cannot get result from N/A or abort");
3024 return impl.getPointer();
3027 std::optional<Attribute>
3029 for (
const TypeAttributeConversionCallbackFn &fn :
3030 llvm::reverse(typeAttributeConversions)) {
3035 return std::nullopt;
3037 return std::nullopt;
3047 FunctionType type = dyn_cast<FunctionType>(funcOp.getFunctionType());
3053 SmallVector<Type, 1> newResults;
3055 failed(typeConverter.
convertTypes(type.getResults(), newResults)) ||
3057 typeConverter, &result)))
3074 FunctionOpInterfaceSignatureConversion(StringRef functionLikeOpName,
3082 FunctionOpInterface funcOp = cast<FunctionOpInterface>(op);
3087 struct AnyFunctionOpInterfaceSignatureConversion
3099 FailureOr<Operation *>
3103 assert(op &&
"Invalid op");
3117 return rewriter.
create(newOp);
3123 patterns.add<FunctionOpInterfaceSignatureConversion>(
3124 functionLikeOpName,
patterns.getContext(), converter);
3129 patterns.add<AnyFunctionOpInterfaceSignatureConversion>(
3139 legalOperations[op].action = action;
3144 for (StringRef dialect : dialectNames)
3145 legalDialects[dialect] = action;
3149 -> std::optional<LegalizationAction> {
3150 std::optional<LegalizationInfo> info = getOpInfo(op);
3151 return info ? info->action : std::optional<LegalizationAction>();
3155 -> std::optional<LegalOpDetails> {
3156 std::optional<LegalizationInfo> info = getOpInfo(op->
getName());
3158 return std::nullopt;
3161 auto isOpLegal = [&] {
3163 if (info->action == LegalizationAction::Dynamic) {
3164 std::optional<bool> result = info->legalityFn(op);
3170 return info->action == LegalizationAction::Legal;
3173 return std::nullopt;
3177 if (info->isRecursivelyLegal) {
3178 auto legalityFnIt = opRecursiveLegalityFns.find(op->
getName());
3179 if (legalityFnIt != opRecursiveLegalityFns.end()) {
3181 legalityFnIt->second(op).value_or(
true);
3186 return legalityDetails;
3190 std::optional<LegalizationInfo> info = getOpInfo(op->
getName());
3194 if (info->action == LegalizationAction::Dynamic) {
3195 std::optional<bool> result = info->legalityFn(op);
3202 return info->action == LegalizationAction::Illegal;
3211 auto chain = [oldCl = std::move(oldCallback), newCl = std::move(newCallback)](
3213 if (std::optional<bool> result = newCl(op))
3221 void ConversionTarget::setLegalityCallback(
3222 OperationName name,
const DynamicLegalityCallbackFn &callback) {
3223 assert(callback &&
"expected valid legality callback");
3224 auto *infoIt = legalOperations.find(name);
3225 assert(infoIt != legalOperations.end() &&
3226 infoIt->second.action == LegalizationAction::Dynamic &&
3227 "expected operation to already be marked as dynamically legal");
3228 infoIt->second.legalityFn =
3234 auto *infoIt = legalOperations.find(name);
3235 assert(infoIt != legalOperations.end() &&
3236 infoIt->second.action != LegalizationAction::Illegal &&
3237 "expected operation to already be marked as legal");
3238 infoIt->second.isRecursivelyLegal =
true;
3241 std::move(opRecursiveLegalityFns[name]), callback);
3243 opRecursiveLegalityFns.erase(name);
3246 void ConversionTarget::setLegalityCallback(
3248 assert(callback &&
"expected valid legality callback");
3249 for (StringRef dialect : dialects)
3251 std::move(dialectLegalityFns[dialect]), callback);
3254 void ConversionTarget::setLegalityCallback(
3255 const DynamicLegalityCallbackFn &callback) {
3256 assert(callback &&
"expected valid legality callback");
3261 -> std::optional<LegalizationInfo> {
3263 const auto *it = legalOperations.find(op);
3264 if (it != legalOperations.end())
3267 auto dialectIt = legalDialects.find(op.getDialectNamespace());
3268 if (dialectIt != legalDialects.end()) {
3269 DynamicLegalityCallbackFn callback;
3270 auto dialectFn = dialectLegalityFns.find(op.getDialectNamespace());
3271 if (dialectFn != dialectLegalityFns.end())
3272 callback = dialectFn->second;
3273 return LegalizationInfo{dialectIt->second,
false,
3277 if (unknownLegalityFn)
3278 return LegalizationInfo{LegalizationAction::Dynamic,
3279 false, unknownLegalityFn};
3280 return std::nullopt;
3283 #if MLIR_ENABLE_PDL_IN_PATTERNMATCH
3289 auto &rewriterImpl =
3295 auto &rewriterImpl =
3302 static FailureOr<SmallVector<Value>>
3304 SmallVector<Value> mappedValues;
3307 return std::move(mappedValues);
3311 patterns.getPDLPatterns().registerRewriteFunction(
3316 if (failed(results))
3318 return results->front();
3320 patterns.getPDLPatterns().registerRewriteFunction(
3325 patterns.getPDLPatterns().registerRewriteFunction(
3328 auto &rewriterImpl =
3338 patterns.getPDLPatterns().registerRewriteFunction(
3341 TypeRange types) -> FailureOr<SmallVector<Type>> {
3342 auto &rewriterImpl =
3349 if (failed(converter->
convertTypes(types, remappedTypes)))
3351 return std::move(remappedTypes);
3368 OpConversionMode::Partial);
3387 OpConversionMode::Full);
3411 "expected top-level op to be isolated from above");
3414 "expected ops to have a common ancestor");
3423 for (
Operation *op : ops.drop_front()) {
3427 assert(commonAncestor &&
3428 "expected to find a common isolated from above ancestor");
3432 return commonAncestor;
3439 if (
config.legalizableOps)
3440 assert(
config.legalizableOps->empty() &&
"expected empty set");
3450 inverseOperationMap[it.second] = it.first;
3456 OpConversionMode::Analysis);
3457 LogicalResult status = opConverter.convertOperations(opsToConvert);
3461 if (
config.legalizableOps) {
3464 originalLegalizableOps.insert(inverseOperationMap[op]);
3465 *
config.legalizableOps = std::move(originalLegalizableOps);
3469 clonedAncestor->
erase();
static ConversionTarget::DynamicLegalityCallbackFn composeLegalityCallbacks(ConversionTarget::DynamicLegalityCallbackFn oldCallback, ConversionTarget::DynamicLegalityCallbackFn newCallback)
static FailureOr< SmallVector< Value > > pdllConvertValues(ConversionPatternRewriter &rewriter, ValueRange values)
Remap the given value using the rewriter and the type converter in the provided config.
static LogicalResult convertFuncOpTypes(FunctionOpInterface funcOp, const TypeConverter &typeConverter, ConversionPatternRewriter &rewriter)
static void logFailure(llvm::ScopedPrinter &os, StringRef fmt, Args &&...args)
A utility function to log a failure result for the given reason.
static LogicalResult legalizeUnresolvedMaterialization(RewriterBase &rewriter, UnresolvedMaterializationRewrite *rewrite)
static void logSuccess(llvm::ScopedPrinter &os, StringRef fmt, Args &&...args)
A utility function to log a successful result for the given reason.
SmallVector< Value, 1 > ValueVector
A vector of SSA values, optimized for the most common case of a single value.
static Operation * findCommonAncestor(ArrayRef< Operation * > ops)
Find a common IsolatedFromAbove ancestor of the given ops.
static OpBuilder::InsertPoint computeInsertPoint(Value value)
Helper function that computes an insertion point where the given value is defined and can be used wit...
union mlir::linalg::@1182::ArityGroupAndKind::Kind kind
static std::string diag(const llvm::Value &value)
static Value max(ImplicitLocOpBuilder &builder, Value value, Value bound)
static Value min(ImplicitLocOpBuilder &builder, Value value, Value bound)
static void rewrite(DataFlowSolver &solver, MLIRContext *context, MutableArrayRef< Region > initialRegions)
Rewrite the given regions using the computing analysis.
Attributes are known-constant values of operations.
This class represents an argument of a Block.
Block * getOwner() const
Returns the block that owns this argument.
Location getLoc() const
Return the location for this argument.
Block represents an ordered list of Operations.
OpListType::iterator iterator
ValueTypeRange< BlockArgListType > getArgumentTypes()
Return a range containing the types of the arguments for this block.
BlockArgument getArgument(unsigned i)
unsigned getNumArguments()
Region * getParent() const
Provide a 'getParent' method for ilist_node_with_parent methods.
void dropAllDefinedValueUses()
This drops all uses of values defined in this block or in the blocks of nested regions wherever the u...
OpListType & getOperations()
BlockArgListType getArguments()
Operation * getParentOp()
Returns the closest surrounding operation that contains this block.
MLIRContext * getContext() const
This class implements a pattern rewriter for use with ConversionPatterns.
void replaceOp(Operation *op, ValueRange newValues) override
Replace the given operation with the new values.
LogicalResult getRemappedValues(ValueRange keys, SmallVectorImpl< Value > &results)
Return the converted values that replace 'keys' with types defined by the type converter of the curre...
FailureOr< Block * > convertRegionTypes(Region *region, const TypeConverter &converter, TypeConverter::SignatureConversion *entryConversion=nullptr)
Apply a signature conversion to each block in the given region.
void inlineBlockBefore(Block *source, Block *dest, Block::iterator before, ValueRange argValues=std::nullopt) override
PatternRewriter hook for inlining the ops of a block into another block.
void replaceOpWithMultiple(Operation *op, SmallVector< SmallVector< Value >> &&newValues)
Replace the given operation with the new value ranges.
Block * applySignatureConversion(Block *block, TypeConverter::SignatureConversion &conversion, const TypeConverter *converter=nullptr)
Apply a signature conversion to given block.
void startOpModification(Operation *op) override
PatternRewriter hook for updating the given operation in-place.
void eraseOp(Operation *op) override
PatternRewriter hook for erasing a dead operation.
detail::ConversionPatternRewriterImpl & getImpl()
Return a reference to the internal implementation.
void eraseBlock(Block *block) override
PatternRewriter hook for erase all operations in a block.
void cancelOpModification(Operation *op) override
PatternRewriter hook for updating the given operation in-place.
Value getRemappedValue(Value key)
Return the converted value of 'key' with a type defined by the type converter of the currently execut...
void finalizeOpModification(Operation *op) override
PatternRewriter hook for updating the given operation in-place.
void replaceUsesOfBlockArgument(BlockArgument from, Value to)
Replace all the uses of the block argument from with value to.
~ConversionPatternRewriter() override
Base class for the conversion patterns.
SmallVector< Value > getOneToOneAdaptorOperands(ArrayRef< ValueRange > operands) const
Given an array of value ranges, which are the inputs to a 1:N adaptor, try to extract the single valu...
virtual LogicalResult matchAndRewrite(Operation *op, ArrayRef< Value > operands, ConversionPatternRewriter &rewriter) const
Hook for derived classes to implement combined matching and rewriting.
This class describes a specific conversion target.
void setDialectAction(ArrayRef< StringRef > dialectNames, LegalizationAction action)
Register a legality action for the given dialects.
void setOpAction(OperationName op, LegalizationAction action)
Register a legality action for the given operation.
std::optional< LegalOpDetails > isLegal(Operation *op) const
If the given operation instance is legal on this target, a structure containing legality information ...
std::optional< LegalizationAction > getOpAction(OperationName op) const
Get the legality action for the given operation.
LegalizationAction
This enumeration corresponds to the specific action to take when considering an operation legal for t...
void markOpRecursivelyLegal(OperationName name, const DynamicLegalityCallbackFn &callback)
Mark an operation, that must have either been set as Legal or DynamicallyLegal, as being recursively ...
std::function< std::optional< bool >(Operation *)> DynamicLegalityCallbackFn
The signature of the callback used to determine if an operation is dynamically legal on the target.
bool isIllegal(Operation *op) const
Returns true is operation instance is illegal on this target.
This class contains all of the information necessary to report a diagnostic to the DiagnosticEngine.
A class for computing basic dominance information.
bool dominates(Operation *a, Operation *b) const
Return true if operation A dominates operation B, i.e.
This class represents a frozen set of patterns that can be processed by a pattern applicator.
This is a utility class for mapping one set of IR entities to another.
const DenseMap< Operation *, Operation * > & getOperationMap() const
Return the held operation mapping.
auto lookup(T from) const
Lookup a mapped value within the map.
user_range getUsers() const
Returns a range of all users.
void replaceAllUsesWith(ValueT &&newValue)
Replace all uses of 'this' value with the new value, updating anything in the IR that uses 'this' to ...
This class coordinates rewriting a piece of IR outside of a pattern rewrite, providing a way to keep ...
This class represents a diagnostic that is inflight and set to be reported.
Location objects represent source locations information in MLIR.
This class defines the main interface for locations in MLIR and acts as a non-nullable wrapper around...
MLIRContext is the top-level object for a collection of MLIR operations.
bool isMultithreadingEnabled()
Return true if multi-threading is enabled by the context.
This class represents a saved insertion point.
Block::iterator getPoint() const
bool isSet() const
Returns true if this insert point is set.
RAII guard to reset the insertion point of the builder when destroyed.
This class helps build Operations.
void setInsertionPoint(Block *block, Block::iterator insertPoint)
Set the insertion point to the specified location.
Listener * getListener() const
Returns the current listener of this builder, or nullptr if this builder doesn't have a listener.
Block * createBlock(Region *parent, Region::iterator insertPt={}, TypeRange argTypes=std::nullopt, ArrayRef< Location > locs=std::nullopt)
Add new block with 'argTypes' arguments and set the insertion point to the end of it.
Operation * create(const OperationState &state)
Creates an operation given the fields represented as an OperationState.
LogicalResult tryFold(Operation *op, SmallVectorImpl< Value > &results)
Attempts to fold the given operation and places new results within results.
OpInterfaceConversionPattern is a wrapper around ConversionPattern that allows for matching and rewri...
OpInterfaceConversionPattern(MLIRContext *context, PatternBenefit benefit=1)
This class represents an operand of an operation.
This is a value defined by a result of an operation.
This class provides the API for ops that are known to be isolated from above.
Simple wrapper around a void* in order to express generically how to pass in op properties through AP...
This class implements the operand iterators for the Operation class.
type_range getTypes() const
Operation is the basic unit of execution within MLIR.
void setLoc(Location loc)
Set the source location the operation was defined or derived from.
bool hasTrait()
Returns true if the operation was registered with a particular trait, e.g.
void dropAllUses()
Drop all uses of results of this operation.
void setAttrs(DictionaryAttr newAttrs)
Set the attributes from a dictionary on this operation.
bool isBeforeInBlock(Operation *other)
Given an operation 'other' that is within the same parent block, return whether the current operation...
Operation * clone(IRMapping &mapper, CloneOptions options=CloneOptions::all())
Create a deep copy of this operation, remapping any operands that use values outside of the operation...
std::enable_if_t< llvm::function_traits< std::decay_t< FnT > >::num_args==1, RetT > walk(FnT &&callback)
Walk the operation by calling the callback for each nested operation (including this one),...
unsigned getNumRegions()
Returns the number of regions held by this operation.
Location getLoc()
The source location the operation was defined or derived from.
Operation * getParentOp()
Returns the closest surrounding operation that contains this operation or nullptr if this is a top-le...
ArrayRef< NamedAttribute > getAttrs()
Return all of the attributes on this operation.
InFlightDiagnostic emitError(const Twine &message={})
Emit an error about fatal conditions with this operation, reporting up to any diagnostic handlers tha...
Block * getBlock()
Returns the operation block that contains this operation.
Operation * getParentWithTrait()
Returns the closest surrounding parent operation with trait Trait.
MutableArrayRef< Region > getRegions()
Returns the regions held by this operation.
OperationName getName()
The name of an operation is the key identifier for it.
operand_type_range getOperandTypes()
result_type_range getResultTypes()
operand_range getOperands()
Returns an iterator on the underlying Value's.
void setSuccessor(Block *block, unsigned index)
bool isAncestor(Operation *other)
Return true if this operation is an ancestor of the other operation.
void setOperands(ValueRange operands)
Replace the current operands of this operation with the ones provided in 'operands'.
result_range getResults()
int getPropertiesStorageSize() const
Returns the properties storage size.
bool isProperAncestor(Operation *other)
Return true if this operation is a proper ancestor of the other operation.
OpaqueProperties getPropertiesStorage()
Returns the properties storage.
void erase()
Remove this operation from its parent block and delete it.
void copyProperties(OpaqueProperties rhs)
Copy properties from an existing other properties object.
unsigned getNumResults()
Return the number of results held by this operation.
void notifyRewriteEnd(PatternRewriter &rewriter) final
void notifyRewriteBegin(PatternRewriter &rewriter) final
Hooks that are invoked at the beginning and end of a rewrite of a matched pattern.
This class manages the application of a group of rewrite patterns, with a user-provided cost model.
This class represents the benefit of a pattern match in a unitless scheme that ranges from 0 (very li...
static PatternBenefit impossibleToMatch()
A special type of RewriterBase that coordinates the application of a rewrite pattern on the current I...
This class contains all of the data related to a pattern, but does not contain any methods or logic f...
bool hasBoundedRewriteRecursion() const
Returns true if this pattern is known to result in recursive application, i.e.
std::optional< OperationName > getRootKind() const
Return the root node that this pattern matches.
ArrayRef< OperationName > getGeneratedOps() const
Return a list of operations that may be generated when rewriting an operation instance with this patt...
This class contains a list of basic blocks and a link to the parent operation it is attached to.
Operation * getParentOp()
Return the parent operation this region is attached to.
BlockListType & getBlocks()
BlockListType::iterator iterator
This class coordinates the application of a rewrite on a set of IR, providing a way for clients to tr...
std::enable_if_t<!std::is_convertible< CallbackT, Twine >::value, LogicalResult > notifyMatchFailure(Location loc, CallbackT &&reasonCallback)
Used to notify the listener that the IR failed to be rewritten because of a match failure,...
virtual void eraseBlock(Block *block)
This method erases all operations in a block.
virtual void replaceOp(Operation *op, ValueRange newValues)
Replace the results of the given (original) operation with the specified list of values (replacements...
void replaceAllUsesWith(Value from, Value to)
Find uses of from and replace them with to.
virtual void finalizeOpModification(Operation *op)
This method is used to signal the end of an in-place modification of the given operation.
virtual void eraseOp(Operation *op)
This method erases an operation that is known to have no uses.
void replaceUsesWithIf(Value from, Value to, function_ref< bool(OpOperand &)> functor, bool *allUsesReplaced=nullptr)
Find uses of from and replace them with to if the functor returns true.
void moveOpBefore(Operation *op, Operation *existingOp)
Unlink this operation from its current block and insert it right before existingOp which may be in th...
void modifyOpInPlace(Operation *root, CallableT &&callable)
This method is a utility wrapper around an in-place modification of an operation.
The general result of a type attribute conversion callback, allowing for early termination.
Attribute getResult() const
static AttributeConversionResult abort()
static AttributeConversionResult na()
static AttributeConversionResult result(Attribute attr)
This class provides all of the information necessary to convert a type signature.
void addInputs(unsigned origInputNo, ArrayRef< Type > types)
Remap an input of the original signature with a new set of types.
std::optional< InputMapping > getInputMapping(unsigned input) const
Get the input mapping for the given argument.
ArrayRef< Type > getConvertedTypes() const
Return the argument types for the new signature.
void remapInput(unsigned origInputNo, ArrayRef< Value > replacements)
Remap an input of the original signature to replacements values.
std::optional< Attribute > convertTypeAttribute(Type type, Attribute attr) const
Convert an attribute present attr from within the type type using the registered conversion functions...
Value materializeSourceConversion(OpBuilder &builder, Location loc, Type resultType, ValueRange inputs) const
bool isLegal(Type type) const
Return true if the given type is legal for this type converter, i.e.
LogicalResult convertSignatureArgs(TypeRange types, SignatureConversion &result, unsigned origInputOffset=0) const
LogicalResult convertSignatureArg(unsigned inputNo, Type type, SignatureConversion &result) const
This method allows for converting a specific argument of a signature.
Value materializeArgumentConversion(OpBuilder &builder, Location loc, Type resultType, ValueRange inputs) const
Materialize a conversion from a set of types into one result type by generating a cast sequence of so...
LogicalResult convertType(Type t, SmallVectorImpl< Type > &results) const
Convert the given type.
std::optional< SignatureConversion > convertBlockSignature(Block *block) const
This function converts the type signature of the given block, by invoking 'convertSignatureArg' for e...
LogicalResult convertTypes(TypeRange types, SmallVectorImpl< Type > &results) const
Convert the given set of types, filling 'results' as necessary.
bool isSignatureLegal(FunctionType ty) const
Return true if the inputs and outputs of the given function type are legal.
Value materializeTargetConversion(OpBuilder &builder, Location loc, Type resultType, ValueRange inputs, Type originalType={}) const
This class provides an abstraction over the various different ranges of value types.
Instances of the Type class are uniqued, have an immutable identifier and an optional mutable compone...
MLIRContext * getContext() const
Return the MLIRContext in which this type was uniqued.
This class provides an abstraction over the different types of ranges over Values.
type_range getType() const
This class represents an instance of an SSA value in the MLIR system, representing a computable value...
Type getType() const
Return the type of this value.
Block * getParentBlock()
Return the Block in which this Value is defined.
user_range getUsers() const
Location getLoc() const
Return the location of this value.
static WalkResult advance()
Operation * getOwner() const
Return the owner of this operand.
constexpr void enumerate(std::tuple< Tys... > &tuple, CallbackT &&callback)
@ Full
Documents are synced by always sending the full content of the document.
Kind
An enumeration of the kinds of predicates.
Include the generated interface declarations.
void populateFunctionOpInterfaceTypeConversionPattern(StringRef functionLikeOpName, RewritePatternSet &patterns, const TypeConverter &converter)
Add a pattern to the given pattern list to convert the signature of a FunctionOpInterface op with the...
void populateAnyFunctionOpInterfaceTypeConversionPattern(RewritePatternSet &patterns, const TypeConverter &converter)
const FrozenRewritePatternSet GreedyRewriteConfig config
LogicalResult applyFullConversion(ArrayRef< Operation * > ops, const ConversionTarget &target, const FrozenRewritePatternSet &patterns, ConversionConfig config=ConversionConfig())
Apply a complete conversion on the given operations, and all nested operations.
FailureOr< Operation * > convertOpResultTypes(Operation *op, ValueRange operands, const TypeConverter &converter, ConversionPatternRewriter &rewriter)
Generic utility to convert op result types according to type converter without knowing exact op type.
const FrozenRewritePatternSet & patterns
LogicalResult applyAnalysisConversion(ArrayRef< Operation * > ops, ConversionTarget &target, const FrozenRewritePatternSet &patterns, ConversionConfig config=ConversionConfig())
Apply an analysis conversion on the given operations, and all nested operations.
void reconcileUnrealizedCasts(ArrayRef< UnrealizedConversionCastOp > castOps, SmallVectorImpl< UnrealizedConversionCastOp > *remainingCastOps=nullptr)
Try to reconcile all given UnrealizedConversionCastOps and store the left-over ops in remainingCastOp...
auto get(MLIRContext *context, Ts &&...params)
Helper method that injects context only if needed, this helps unify some of the attribute constructio...
void registerConversionPDLFunctions(RewritePatternSet &patterns)
Register the dialect conversion PDL functions with the given pattern set.
LogicalResult applyPartialConversion(ArrayRef< Operation * > ops, const ConversionTarget &target, const FrozenRewritePatternSet &patterns, ConversionConfig config=ConversionConfig())
Below we define several entry points for operation conversion.
Dialect conversion configuration.
RewriterBase::Listener * listener
An optional listener that is notified about all IR modifications in case dialect conversion succeeds.
function_ref< void(Diagnostic &)> notifyCallback
An optional callback used to notify about match failure diagnostics during the conversion.
DenseSet< Operation * > * legalizableOps
Analysis conversion only.
DenseSet< Operation * > * unlegalizedOps
Partial conversion only.
bool buildMaterializations
If set to "true", the dialect conversion attempts to build source/target materializations through the...
A structure containing additional information describing a specific legal operation instance.
bool isRecursivelyLegal
A flag that indicates if this operation is 'recursively' legal.
This iterator enumerates elements according to their dominance relationship.
LogicalResult convertOperations(ArrayRef< Operation * > ops)
Converts the given operations to the conversion target.
OperationConverter(const ConversionTarget &target, const FrozenRewritePatternSet &patterns, const ConversionConfig &config, OpConversionMode mode)
This represents an operation in an abstracted form, suitable for use with the builder APIs.
void addOperands(ValueRange newOperands)
void addAttributes(ArrayRef< NamedAttribute > newAttributes)
Add an array of named attributes.
void addTypes(ArrayRef< Type > newTypes)
A rewriter that keeps track of erased ops and blocks.
bool wasErased(void *ptr) const
SingleEraseRewriter(MLIRContext *context)
void eraseOp(Operation *op) override
Erase the given op (unless it was already erased).
void notifyBlockErased(Block *block) override
Notify the listener that the specified block is about to be erased.
void notifyOperationErased(Operation *op) override
Notify the listener that the specified operation is about to be erased.
void eraseBlock(Block *block) override
Erase the given block (unless it was already erased).
void notifyOperationInserted(Operation *op, OpBuilder::InsertPoint previous) override
Notify the listener that the specified operation was inserted.
Value findOrBuildReplacementValue(Value value, const TypeConverter *converter)
Find a replacement value for the given SSA value in the conversion value mapping.
ConversionPatternRewriterImpl(MLIRContext *ctx, const ConversionConfig &config)
DenseMap< Region *, const TypeConverter * > regionToConverter
A mapping of regions to type converters that should be used when converting the arguments of blocks w...
bool wasOpReplaced(Operation *op) const
Return "true" if the given operation was replaced or erased.
void notifyBlockInserted(Block *block, Region *previous, Region::iterator previousIt) override
Notifies that a block was inserted.
DenseMap< UnrealizedConversionCastOp, UnresolvedMaterializationRewrite * > unresolvedMaterializations
A mapping of all unresolved materializations (UnrealizedConversionCastOp) to the corresponding rewrit...
LogicalResult remapValues(StringRef valueDiagTag, std::optional< Location > inputLoc, PatternRewriter &rewriter, ValueRange values, SmallVector< ValueVector > &remapped)
Remap the given values to those with potentially different types.
void resetState(RewriterState state)
Reset the state of the rewriter to a previously saved point.
Block * applySignatureConversion(ConversionPatternRewriter &rewriter, Block *block, const TypeConverter *converter, TypeConverter::SignatureConversion &signatureConversion)
Apply the given signature conversion on the given block.
FailureOr< Block * > convertRegionTypes(ConversionPatternRewriter &rewriter, Region *region, const TypeConverter &converter, TypeConverter::SignatureConversion *entryConversion)
Convert the types of block arguments within the given region.
ConversionValueMapping mapping
void applyRewrites()
Apply all requested operation rewrites.
void undoRewrites(unsigned numRewritesToKeep=0)
Undo the rewrites (motions, splits) one by one in reverse order until "numRewritesToKeep" rewrites re...
RewriterState getCurrentState()
Return the current state of the rewriter.
llvm::ScopedPrinter logger
A logger used to emit diagnostics during the conversion process.
void notifyBlockBeingInlined(Block *block, Block *srcBlock, Block::iterator before)
Notifies that a block is being inlined into another block.
void appendRewrite(Args &&...args)
Append a rewrite.
SmallPtrSet< Operation *, 1 > pendingRootUpdates
A set of operations that have pending updates.
void notifyMatchFailure(Location loc, function_ref< void(Diagnostic &)> reasonCallback) override
Notifies that a pattern match failed for the given reason.
SingleEraseRewriter eraseRewriter
A rewriter that keeps track of ops/block that were already erased and skips duplicate op/block erasur...
MLIRContext * context
MLIR context.
bool isOpIgnored(Operation *op) const
Return "true" if the given operation is ignored, and does not need to be converted.
ValueRange buildUnresolvedMaterialization(MaterializationKind kind, OpBuilder::InsertPoint ip, Location loc, ValueVector valuesToMap, ValueRange inputs, TypeRange outputTypes, Type originalType, const TypeConverter *converter, UnrealizedConversionCastOp *castOp=nullptr)
Build an unresolved materialization operation given a range of output types and a list of input opera...
SetVector< Operation * > ignoredOps
A set of operations that should no longer be considered for legalization.
SmallVector< std::unique_ptr< IRRewrite > > rewrites
Ordered list of block operations (creations, splits, motions).
const ConversionConfig & config
Dialect conversion configuration.
void notifyOpReplaced(Operation *op, SmallVector< SmallVector< Value >> &&newValues)
Notifies that an op is about to be replaced with the given values.
SetVector< Operation * > replacedOps
A set of operations that were replaced/erased.
void notifyBlockIsBeingErased(Block *block)
Notifies that a block is about to be erased.
const TypeConverter * currentTypeConverter
The current type converter, or nullptr if no type converter is currently active.
Eliminates variable at the specified position using Fourier-Motzkin variable elimination.