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 for (
Value v : newVal)
160 mapping[std::forward<OldVal>(oldVal)] = std::forward<NewVal>(newVal);
164 template <
typename OldVal,
typename NewVal>
165 std::enable_if_t<!IsValueVector<OldVal>::value ||
166 !IsValueVector<NewVal>::value>
167 map(OldVal &&oldVal, NewVal &&newVal) {
168 if constexpr (IsValueVector<OldVal>{}) {
169 map(std::forward<OldVal>(oldVal),
ValueVector{newVal});
170 }
else if constexpr (IsValueVector<NewVal>{}) {
171 map(
ValueVector{oldVal}, std::forward<NewVal>(newVal));
178 void erase(
const ValueVector &value) { mapping.erase(value); }
190 ConversionValueMapping::lookupOrDefault(
Value from,
199 desiredValue = current;
203 for (
Value v : current) {
204 auto it = mapping.find({v});
205 if (it != mapping.end()) {
206 llvm::append_range(next, it->second);
211 if (next != current) {
213 current = std::move(next);
225 auto it = mapping.find(current);
226 if (it == mapping.end()) {
230 current = it->second;
236 return !desiredValue.empty() ? std::move(desiredValue) : std::move(current);
241 ValueVector result = lookupOrDefault(from, desiredTypes);
254 struct RewriterState {
255 RewriterState(
unsigned numRewrites,
unsigned numIgnoredOperations,
256 unsigned numReplacedOps)
257 : numRewrites(numRewrites), numIgnoredOperations(numIgnoredOperations),
258 numReplacedOps(numReplacedOps) {}
261 unsigned numRewrites;
264 unsigned numIgnoredOperations;
267 unsigned numReplacedOps;
299 UnresolvedMaterialization
302 virtual ~IRRewrite() =
default;
305 virtual void rollback() = 0;
324 Kind getKind()
const {
return kind; }
326 static bool classof(
const IRRewrite *
rewrite) {
return true; }
330 :
kind(
kind), rewriterImpl(rewriterImpl) {}
339 class BlockRewrite :
public IRRewrite {
342 Block *getBlock()
const {
return block; }
344 static bool classof(
const IRRewrite *
rewrite) {
345 return rewrite->getKind() >= Kind::CreateBlock &&
346 rewrite->getKind() <= Kind::ReplaceBlockArg;
352 : IRRewrite(
kind, rewriterImpl), block(block) {}
361 class CreateBlockRewrite :
public BlockRewrite {
364 : BlockRewrite(
Kind::CreateBlock, rewriterImpl, block) {}
366 static bool classof(
const IRRewrite *
rewrite) {
367 return rewrite->getKind() == Kind::CreateBlock;
373 listener->notifyBlockInserted(block, {}, {});
376 void rollback()
override {
379 auto &blockOps = block->getOperations();
380 while (!blockOps.empty())
381 blockOps.remove(blockOps.begin());
382 block->dropAllUses();
383 if (block->getParent())
394 class EraseBlockRewrite :
public BlockRewrite {
397 : BlockRewrite(
Kind::EraseBlock, rewriterImpl, block),
398 region(block->getParent()), insertBeforeBlock(block->getNextNode()) {}
400 static bool classof(
const IRRewrite *
rewrite) {
401 return rewrite->getKind() == Kind::EraseBlock;
404 ~EraseBlockRewrite()
override {
406 "rewrite was neither rolled back nor committed/cleaned up");
409 void rollback()
override {
412 assert(block &&
"expected block");
413 auto &blockList = region->getBlocks();
417 blockList.insert(before, block);
423 assert(block &&
"expected block");
424 assert(block->empty() &&
"expected empty block");
428 dyn_cast_or_null<RewriterBase::Listener>(rewriter.
getListener()))
429 listener->notifyBlockErased(block);
434 block->dropAllDefinedValueUses();
445 Block *insertBeforeBlock;
451 class InlineBlockRewrite :
public BlockRewrite {
455 : BlockRewrite(
Kind::InlineBlock, rewriterImpl, block),
456 sourceBlock(sourceBlock),
457 firstInlinedInst(sourceBlock->empty() ? nullptr
458 : &sourceBlock->front()),
459 lastInlinedInst(sourceBlock->empty() ? nullptr : &sourceBlock->back()) {
465 assert(!getConfig().listener &&
466 "InlineBlockRewrite not supported if listener is attached");
469 static bool classof(
const IRRewrite *
rewrite) {
470 return rewrite->getKind() == Kind::InlineBlock;
473 void rollback()
override {
476 if (firstInlinedInst) {
477 assert(lastInlinedInst &&
"expected operation");
497 class MoveBlockRewrite :
public BlockRewrite {
501 : BlockRewrite(
Kind::MoveBlock, rewriterImpl, block), region(region),
502 insertBeforeBlock(insertBeforeBlock) {}
504 static bool classof(
const IRRewrite *
rewrite) {
505 return rewrite->getKind() == Kind::MoveBlock;
513 listener->notifyBlockInserted(block, region,
518 void rollback()
override {
531 Block *insertBeforeBlock;
535 class BlockTypeConversionRewrite :
public BlockRewrite {
539 : BlockRewrite(
Kind::BlockTypeConversion, rewriterImpl, origBlock),
540 newBlock(newBlock) {}
542 static bool classof(
const IRRewrite *
rewrite) {
543 return rewrite->getKind() == Kind::BlockTypeConversion;
546 Block *getOrigBlock()
const {
return block; }
548 Block *getNewBlock()
const {
return newBlock; }
552 void rollback()
override;
562 class ReplaceBlockArgRewrite :
public BlockRewrite {
567 : BlockRewrite(
Kind::ReplaceBlockArg, rewriterImpl, block), arg(arg),
568 converter(converter) {}
570 static bool classof(
const IRRewrite *
rewrite) {
571 return rewrite->getKind() == Kind::ReplaceBlockArg;
576 void rollback()
override;
586 class OperationRewrite :
public IRRewrite {
589 Operation *getOperation()
const {
return op; }
591 static bool classof(
const IRRewrite *
rewrite) {
592 return rewrite->getKind() >= Kind::MoveOperation &&
593 rewrite->getKind() <= Kind::UnresolvedMaterialization;
599 : IRRewrite(
kind, rewriterImpl), op(op) {}
606 class MoveOperationRewrite :
public OperationRewrite {
610 : OperationRewrite(
Kind::MoveOperation, rewriterImpl, op), block(block),
611 insertBeforeOp(insertBeforeOp) {}
613 static bool classof(
const IRRewrite *
rewrite) {
614 return rewrite->getKind() == Kind::MoveOperation;
622 listener->notifyOperationInserted(
628 void rollback()
override {
632 block->
getOperations().splice(before, op->getBlock()->getOperations(), op);
646 class ModifyOperationRewrite :
public OperationRewrite {
650 : OperationRewrite(
Kind::ModifyOperation, rewriterImpl, op),
651 name(op->getName()), loc(op->getLoc()), attrs(op->getAttrDictionary()),
652 operands(op->operand_begin(), op->operand_end()),
653 successors(op->successor_begin(), op->successor_end()) {
658 name.initOpProperties(propCopy, prop);
662 static bool classof(
const IRRewrite *
rewrite) {
663 return rewrite->getKind() == Kind::ModifyOperation;
666 ~ModifyOperationRewrite()
override {
667 assert(!propertiesStorage &&
668 "rewrite was neither committed nor rolled back");
674 dyn_cast_or_null<RewriterBase::Listener>(rewriter.
getListener()))
675 listener->notifyOperationModified(op);
677 if (propertiesStorage) {
681 name.destroyOpProperties(propCopy);
682 operator delete(propertiesStorage);
683 propertiesStorage =
nullptr;
687 void rollback()
override {
693 if (propertiesStorage) {
696 name.destroyOpProperties(propCopy);
697 operator delete(propertiesStorage);
698 propertiesStorage =
nullptr;
705 DictionaryAttr attrs;
706 SmallVector<Value, 8> operands;
707 SmallVector<Block *, 2> successors;
708 void *propertiesStorage =
nullptr;
715 class ReplaceOperationRewrite :
public OperationRewrite {
719 : OperationRewrite(
Kind::ReplaceOperation, rewriterImpl, op),
720 converter(converter) {}
722 static bool classof(
const IRRewrite *
rewrite) {
723 return rewrite->getKind() == Kind::ReplaceOperation;
728 void rollback()
override;
738 class CreateOperationRewrite :
public OperationRewrite {
742 : OperationRewrite(
Kind::CreateOperation, rewriterImpl, op) {}
744 static bool classof(
const IRRewrite *
rewrite) {
745 return rewrite->getKind() == Kind::CreateOperation;
751 listener->notifyOperationInserted(op, {});
754 void rollback()
override;
758 enum MaterializationKind {
771 class UnresolvedMaterializationRewrite :
public OperationRewrite {
774 UnrealizedConversionCastOp op,
776 MaterializationKind
kind,
Type originalType,
779 static bool classof(
const IRRewrite *
rewrite) {
780 return rewrite->getKind() == Kind::UnresolvedMaterialization;
783 void rollback()
override;
785 UnrealizedConversionCastOp getOperation()
const {
786 return cast<UnrealizedConversionCastOp>(op);
791 return converterAndKind.getPointer();
795 MaterializationKind getMaterializationKind()
const {
796 return converterAndKind.getInt();
800 Type getOriginalType()
const {
return originalType; }
805 llvm::PointerIntPair<const TypeConverter *, 2, MaterializationKind>
818 #if MLIR_ENABLE_EXPENSIVE_PATTERN_API_CHECKS
821 template <
typename RewriteTy,
typename R>
822 static bool hasRewrite(R &&rewrites,
Operation *op) {
823 return any_of(std::forward<R>(rewrites), [&](
auto &
rewrite) {
824 auto *rewriteTy = dyn_cast<RewriteTy>(
rewrite.get());
825 return rewriteTy && rewriteTy->getOperation() == op;
831 template <
typename RewriteTy,
typename R>
832 static bool hasRewrite(R &&rewrites,
Block *block) {
833 return any_of(std::forward<R>(rewrites), [&](
auto &
rewrite) {
834 auto *rewriteTy = dyn_cast<RewriteTy>(
rewrite.get());
835 return rewriteTy && rewriteTy->getBlock() == block;
855 RewriterState getCurrentState();
859 void applyRewrites();
862 void resetState(RewriterState state);
866 template <
typename RewriteTy,
typename... Args>
869 std::make_unique<RewriteTy>(*
this, std::forward<Args>(args)...));
874 void undoRewrites(
unsigned numRewritesToKeep = 0);
880 LogicalResult remapValues(StringRef valueDiagTag,
881 std::optional<Location> inputLoc,
908 Block *applySignatureConversion(
931 UnrealizedConversionCastOp *castOp =
nullptr);
938 Value findOrBuildReplacementValue(
Value value,
946 void notifyOperationInserted(
Operation *op,
953 void notifyBlockIsBeingErased(
Block *block);
956 void notifyBlockInserted(
Block *block,
Region *previous,
960 void notifyBlockBeingInlined(
Block *block,
Block *srcBlock,
990 if (wasErased(block))
992 assert(block->
empty() &&
"expected empty block");
997 bool wasErased(
void *ptr)
const {
return erased.contains(ptr); }
1061 llvm::ScopedPrinter logger{llvm::dbgs()};
1068 return rewriterImpl.
config;
1071 void BlockTypeConversionRewrite::commit(
RewriterBase &rewriter) {
1075 if (
auto *listener =
1076 dyn_cast_or_null<RewriterBase::Listener>(rewriter.
getListener()))
1077 for (
Operation *op : getNewBlock()->getUsers())
1078 listener->notifyOperationModified(op);
1081 void BlockTypeConversionRewrite::rollback() {
1082 getNewBlock()->replaceAllUsesWith(getOrigBlock());
1085 void ReplaceBlockArgRewrite::commit(
RewriterBase &rewriter) {
1090 if (isa<BlockArgument>(repl)) {
1098 Operation *replOp = cast<OpResult>(repl).getOwner();
1106 void ReplaceBlockArgRewrite::rollback() { rewriterImpl.
mapping.erase({arg}); }
1108 void ReplaceOperationRewrite::commit(
RewriterBase &rewriter) {
1110 dyn_cast_or_null<RewriterBase::Listener>(rewriter.
getListener());
1113 SmallVector<Value> replacements =
1115 return rewriterImpl.findOrBuildReplacementValue(result, converter);
1120 listener->notifyOperationReplaced(op, replacements);
1123 for (
auto [result, newValue] :
1124 llvm::zip_equal(op->
getResults(), replacements))
1130 if (getConfig().unlegalizedOps)
1131 getConfig().unlegalizedOps->erase(op);
1137 [&](
Operation *op) { listener->notifyOperationErased(op); });
1145 void ReplaceOperationRewrite::rollback() {
1147 rewriterImpl.
mapping.erase({result});
1150 void ReplaceOperationRewrite::cleanup(
RewriterBase &rewriter) {
1154 void CreateOperationRewrite::rollback() {
1156 while (!region.getBlocks().empty())
1157 region.getBlocks().remove(region.getBlocks().begin());
1163 UnresolvedMaterializationRewrite::UnresolvedMaterializationRewrite(
1167 : OperationRewrite(
Kind::UnresolvedMaterialization, rewriterImpl, op),
1168 converterAndKind(converter,
kind), originalType(originalType),
1169 mappedValues(std::move(mappedValues)) {
1170 assert((!originalType || kind == MaterializationKind::Target) &&
1171 "original type is valid only for target materializations");
1175 void UnresolvedMaterializationRewrite::rollback() {
1176 if (!mappedValues.empty())
1177 rewriterImpl.
mapping.erase(mappedValues);
1187 for (
size_t i = 0; i <
rewrites.size(); ++i)
1207 while (
ignoredOps.size() != state.numIgnoredOperations)
1210 while (
replacedOps.size() != state.numReplacedOps)
1216 llvm::reverse(llvm::drop_begin(
rewrites, numRewritesToKeep)))
1218 rewrites.resize(numRewritesToKeep);
1222 StringRef valueDiagTag, std::optional<Location> inputLoc,
1225 remapped.reserve(llvm::size(values));
1228 Value operand = it.value();
1236 remapped.push_back(
mapping.lookupOrDefault(operand));
1244 diag <<
"unable to convert type for " << valueDiagTag <<
" #"
1245 << it.index() <<
", type was " << origType;
1250 if (legalTypes.empty()) {
1251 remapped.push_back({});
1260 remapped.push_back(std::move(repl));
1265 repl =
mapping.lookupOrDefault(operand);
1268 repl, repl, legalTypes,
1270 remapped.push_back(castValues);
1293 if (region->
empty())
1298 llvm::make_early_inc_range(llvm::drop_begin(*region, 1))) {
1300 std::optional<TypeConverter::SignatureConversion> conversion =
1310 if (entryConversion)
1313 std::optional<TypeConverter::SignatureConversion> conversion =
1325 #if MLIR_ENABLE_EXPENSIVE_PATTERN_API_CHECKS
1327 if (hasRewrite<BlockTypeConversionRewrite>(
rewrites, block))
1328 llvm::report_fatal_error(
"block was already converted");
1342 for (
unsigned i = 0; i < origArgCount; ++i) {
1344 if (!inputMap || inputMap->replacedWithValues())
1347 for (
unsigned j = 0;
j < inputMap->size; ++
j)
1348 newLocs[inputMap->inputNo +
j] = origLoc;
1355 convertedTypes, newLocs);
1365 appendRewrite<InlineBlockRewrite>(newBlock, block, newBlock->
end());
1368 while (!block->
empty())
1375 for (
unsigned i = 0; i != origArgCount; ++i) {
1379 std::optional<TypeConverter::SignatureConversion::InputMapping> inputMap =
1385 MaterializationKind::Source,
1388 origArgType,
Type(), converter);
1389 appendRewrite<ReplaceBlockArgRewrite>(block, origArg, converter);
1393 if (inputMap->replacedWithValues()) {
1395 assert(inputMap->size == 0 &&
1396 "invalid to provide a replacement value when the argument isn't "
1398 mapping.map(origArg, inputMap->replacementValues);
1399 appendRewrite<ReplaceBlockArgRewrite>(block, origArg, converter);
1405 newBlock->
getArguments().slice(inputMap->inputNo, inputMap->size);
1406 ValueVector replArgVals = llvm::to_vector_of<Value, 1>(replArgs);
1407 mapping.map(origArg, std::move(replArgVals));
1408 appendRewrite<ReplaceBlockArgRewrite>(block, origArg, converter);
1411 appendRewrite<BlockTypeConversionRewrite>(block, newBlock);
1430 UnrealizedConversionCastOp *castOp) {
1431 assert((!originalType ||
kind == MaterializationKind::Target) &&
1432 "original type is valid only for target materializations");
1433 assert(
TypeRange(inputs) != outputTypes &&
1434 "materialization is not necessary");
1438 OpBuilder builder(outputTypes.front().getContext());
1441 builder.
create<UnrealizedConversionCastOp>(loc, outputTypes, inputs);
1442 if (!valuesToMap.empty())
1443 mapping.map(valuesToMap, convertOp.getResults());
1445 *castOp = convertOp;
1446 appendRewrite<UnresolvedMaterializationRewrite>(
1447 convertOp, converter,
kind, originalType, std::move(valuesToMap));
1448 return convertOp.getResults();
1458 return repl.front();
1465 [&](
Operation *op) { return replacedOps.contains(op); }) &&
1472 repl =
mapping.lookupOrNull(value);
1505 logger.startLine() <<
"** Insert : '" << op->
getName() <<
"'(" << op
1509 "attempting to insert into a block within a replaced/erased op");
1511 if (!previous.
isSet()) {
1513 appendRewrite<CreateOperationRewrite>(op);
1519 appendRewrite<MoveOperationRewrite>(op, previous.
getBlock(), prevOp);
1525 assert(!
ignoredOps.contains(op) &&
"operation was already replaced");
1529 bool isUnresolvedMaterialization =
false;
1530 if (
auto castOp = dyn_cast<UnrealizedConversionCastOp>(op))
1532 isUnresolvedMaterialization =
true;
1535 for (
auto [repl, result] : llvm::zip_equal(newValues, op->
getResults())) {
1538 if (isUnresolvedMaterialization) {
1558 assert(!isUnresolvedMaterialization &&
1559 "attempting to replace an unresolved materialization");
1574 appendRewrite<EraseBlockRewrite>(block);
1580 "attempting to insert into a region within a replaced/erased op");
1585 logger.startLine() <<
"** Insert Block into : '" << parent->
getName()
1586 <<
"'(" << parent <<
")\n";
1589 <<
"** Insert Block into detached Region (nullptr parent op)'";
1595 appendRewrite<CreateBlockRewrite>(block);
1598 Block *prevBlock = previousIt == previous->
end() ? nullptr : &*previousIt;
1599 appendRewrite<MoveBlockRewrite>(block, previous, prevBlock);
1604 appendRewrite<InlineBlockRewrite>(block, srcBlock, before);
1611 reasonCallback(
diag);
1612 logger.startLine() <<
"** Failure : " <<
diag.str() <<
"\n";
1622 ConversionPatternRewriter::ConversionPatternRewriter(
1626 setListener(
impl.get());
1632 assert(op && newOp &&
"expected non-null op");
1638 "incorrect # of replacement values");
1640 impl->logger.startLine()
1641 <<
"** Replace : '" << op->
getName() <<
"'(" << op <<
")\n";
1644 for (
size_t i = 0; i < newValues.size(); ++i) {
1646 newVals.push_back(newValues.slice(i, 1));
1651 impl->notifyOpReplaced(op, newVals);
1657 "incorrect # of replacement values");
1659 impl->logger.startLine()
1660 <<
"** Replace : '" << op->
getName() <<
"'(" << op <<
")\n";
1662 impl->notifyOpReplaced(op, newValues);
1667 impl->logger.startLine()
1668 <<
"** Erase : '" << op->
getName() <<
"'(" << op <<
")\n";
1671 impl->notifyOpReplaced(op, nullRepls);
1676 "attempting to erase a block within a replaced/erased op");
1686 impl->notifyBlockIsBeingErased(block);
1694 "attempting to apply a signature conversion to a block within a "
1695 "replaced/erased op");
1696 return impl->applySignatureConversion(*
this, block, converter, conversion);
1703 "attempting to apply a signature conversion to a block within a "
1704 "replaced/erased op");
1705 return impl->convertRegionTypes(*
this, region, converter, entryConversion);
1711 impl->logger.startLine() <<
"** Replace Argument : '" << from <<
"'";
1713 impl->logger.getOStream() <<
" (in region of '" << parentOp->getName()
1714 <<
"' (" << parentOp <<
")\n";
1716 impl->logger.getOStream() <<
" (unlinked block)\n";
1719 impl->appendRewrite<ReplaceBlockArgRewrite>(from.
getOwner(), from,
1720 impl->currentTypeConverter);
1721 impl->mapping.map(
impl->mapping.lookupOrDefault(from), to);
1726 if (failed(
impl->remapValues(
"value", std::nullopt, *
this, key,
1729 assert(remappedValues.front().size() == 1 &&
"1:N conversion not supported");
1730 return remappedValues.front().front();
1739 if (failed(
impl->remapValues(
"value", std::nullopt, *
this, keys,
1742 for (
const auto &values : remapped) {
1743 assert(values.size() == 1 &&
"1:N conversion not supported");
1744 results.push_back(values.front());
1754 "incorrect # of argument replacement values");
1756 "attempting to inline a block from a replaced/erased op");
1758 "attempting to inline a block into a replaced/erased op");
1759 auto opIgnored = [&](
Operation *op) {
return impl->isOpIgnored(op); };
1762 assert(llvm::all_of(source->
getUsers(), opIgnored) &&
1763 "expected 'source' to have no predecessors");
1772 bool fastPath = !
impl->config.listener;
1775 impl->notifyBlockBeingInlined(dest, source, before);
1778 for (
auto it : llvm::zip(source->
getArguments(), argValues))
1779 replaceUsesOfBlockArgument(std::get<0>(it), std::get<1>(it));
1786 while (!source->
empty())
1787 moveOpBefore(&source->
front(), dest, before);
1795 assert(!
impl->wasOpReplaced(op) &&
1796 "attempting to modify a replaced/erased op");
1798 impl->pendingRootUpdates.insert(op);
1800 impl->appendRewrite<ModifyOperationRewrite>(op);
1804 assert(!
impl->wasOpReplaced(op) &&
1805 "attempting to modify a replaced/erased op");
1810 assert(
impl->pendingRootUpdates.erase(op) &&
1811 "operation did not have a pending in-place update");
1817 assert(
impl->pendingRootUpdates.erase(op) &&
1818 "operation did not have a pending in-place update");
1821 auto it = llvm::find_if(
1822 llvm::reverse(
impl->rewrites), [&](std::unique_ptr<IRRewrite> &
rewrite) {
1823 auto *modifyRewrite = dyn_cast<ModifyOperationRewrite>(rewrite.get());
1824 return modifyRewrite && modifyRewrite->getOperation() == op;
1826 assert(it !=
impl->rewrites.rend() &&
"no root update started on op");
1828 int updateIdx = std::prev(
impl->rewrites.rend()) - it;
1829 impl->rewrites.erase(
impl->rewrites.begin() + updateIdx);
1843 oneToOneOperands.reserve(operands.size());
1845 if (operand.size() != 1)
1846 llvm::report_fatal_error(
"pattern '" + getDebugName() +
1847 "' does not support 1:N conversion");
1848 oneToOneOperands.push_back(operand.front());
1850 return oneToOneOperands;
1857 auto &rewriterImpl = dialectRewriter.getImpl();
1861 getTypeConverter());
1870 llvm::to_vector_of<ValueRange>(remapped);
1871 return matchAndRewrite(op, remappedAsRange, dialectRewriter);
1883 class OperationLegalizer {
1903 LogicalResult legalizeWithFold(
Operation *op,
1908 LogicalResult legalizeWithPattern(
Operation *op,
1919 RewriterState &curState);
1923 legalizePatternBlockRewrites(
Operation *op,
1926 RewriterState &state, RewriterState &newState);
1927 LogicalResult legalizePatternCreatedOperations(
1929 RewriterState &state, RewriterState &newState);
1932 RewriterState &state,
1933 RewriterState &newState);
1943 void buildLegalizationGraph(
1944 LegalizationPatterns &anyOpLegalizerPatterns,
1955 void computeLegalizationGraphBenefit(
1956 LegalizationPatterns &anyOpLegalizerPatterns,
1961 unsigned computeOpLegalizationDepth(
1968 unsigned applyCostModelToPatterns(
1994 LegalizationPatterns anyOpLegalizerPatterns;
1996 buildLegalizationGraph(anyOpLegalizerPatterns, legalizerPatterns);
1997 computeLegalizationGraphBenefit(anyOpLegalizerPatterns, legalizerPatterns);
2000 bool OperationLegalizer::isIllegal(
Operation *op)
const {
2001 return target.isIllegal(op);
2005 OperationLegalizer::legalize(
Operation *op,
2008 const char *logLineComment =
2009 "//===-------------------------------------------===//\n";
2014 logger.getOStream() <<
"\n";
2015 logger.startLine() << logLineComment;
2016 logger.startLine() <<
"Legalizing operation : '" << op->
getName() <<
"'("
2022 op->print(logger.startLine(), OpPrintingFlags().printGenericOpForm());
2023 logger.getOStream() <<
"\n\n";
2028 if (
auto legalityInfo = target.isLegal(op)) {
2031 logger,
"operation marked legal by the target{0}",
2032 legalityInfo->isRecursivelyLegal
2033 ?
"; NOTE: operation is recursively legal; skipping internals"
2035 logger.startLine() << logLineComment;
2040 if (legalityInfo->isRecursivelyLegal) {
2053 logSuccess(logger,
"operation marked 'ignored' during conversion");
2054 logger.startLine() << logLineComment;
2062 if (succeeded(legalizeWithFold(op, rewriter))) {
2065 logger.startLine() << logLineComment;
2071 if (succeeded(legalizeWithPattern(op, rewriter))) {
2074 logger.startLine() << logLineComment;
2080 logFailure(logger,
"no matched legalization pattern");
2081 logger.startLine() << logLineComment;
2087 OperationLegalizer::legalizeWithFold(
Operation *op,
2089 auto &rewriterImpl = rewriter.
getImpl();
2093 rewriterImpl.
logger.startLine() <<
"* Fold {\n";
2094 rewriterImpl.
logger.indent();
2098 SmallVector<Value, 2> replacementValues;
2100 if (failed(rewriter.
tryFold(op, replacementValues))) {
2106 if (replacementValues.empty())
2107 return legalize(op, rewriter);
2110 rewriter.
replaceOp(op, replacementValues);
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()));
2133 OperationLegalizer::legalizeWithPattern(
Operation *op,
2135 auto &rewriterImpl = rewriter.
getImpl();
2138 auto canApply = [&](
const Pattern &pattern) {
2139 bool canApply = canApplyPattern(op, pattern, rewriter);
2140 if (canApply &&
config.listener)
2141 config.listener->notifyPatternBegin(pattern, op);
2147 auto onFailure = [&](
const Pattern &pattern) {
2153 diag <<
"Failed to apply pattern \"" << pattern.getDebugName()
2160 config.listener->notifyPatternEnd(pattern, failure());
2162 appliedPatterns.erase(&pattern);
2167 auto onSuccess = [&](
const Pattern &pattern) {
2169 auto result = legalizePatternResult(op, pattern, rewriter, curState);
2170 appliedPatterns.erase(&pattern);
2174 config.listener->notifyPatternEnd(pattern, result);
2179 return applicator.matchAndRewrite(op, rewriter, canApply, onFailure,
2183 bool OperationLegalizer::canApplyPattern(
Operation *op,
const Pattern &pattern,
2186 auto &os = rewriter.
getImpl().logger;
2187 os.getOStream() <<
"\n";
2188 os.startLine() <<
"* Pattern : '" << op->
getName() <<
" -> (";
2190 os.getOStream() <<
")' {\n";
2197 !appliedPatterns.insert(&pattern).second) {
2206 OperationLegalizer::legalizePatternResult(
Operation *op,
const Pattern &pattern,
2208 RewriterState &curState) {
2210 assert(
impl.pendingRootUpdates.empty() &&
"dangling root updates");
2212 #if MLIR_ENABLE_EXPENSIVE_PATTERN_API_CHECKS
2214 auto newRewrites = llvm::drop_begin(
impl.rewrites, curState.numRewrites);
2215 auto replacedRoot = [&] {
2216 return hasRewrite<ReplaceOperationRewrite>(newRewrites, op);
2218 auto updatedRootInPlace = [&] {
2219 return hasRewrite<ModifyOperationRewrite>(newRewrites, op);
2221 if (!replacedRoot() && !updatedRootInPlace())
2222 llvm::report_fatal_error(
"expected pattern to replace the root operation");
2226 RewriterState newState =
impl.getCurrentState();
2227 if (failed(legalizePatternBlockRewrites(op, rewriter,
impl, curState,
2229 failed(legalizePatternRootUpdates(rewriter,
impl, curState, newState)) ||
2230 failed(legalizePatternCreatedOperations(rewriter,
impl, curState,
2235 LLVM_DEBUG(
logSuccess(
impl.logger,
"pattern applied successfully"));
2239 LogicalResult OperationLegalizer::legalizePatternBlockRewrites(
2242 RewriterState &newState) {
2247 for (
int i = state.numRewrites, e = newState.numRewrites; i != e; ++i) {
2248 BlockRewrite *
rewrite = dyn_cast<BlockRewrite>(
impl.rewrites[i].get());
2252 if (isa<BlockTypeConversionRewrite, EraseBlockRewrite,
2253 ReplaceBlockArgRewrite>(
rewrite))
2262 if (
auto *converter =
impl.regionToConverter.lookup(block->
getParent())) {
2263 std::optional<TypeConverter::SignatureConversion> conversion =
2266 LLVM_DEBUG(
logFailure(
impl.logger,
"failed to convert types of moved "
2270 impl.applySignatureConversion(rewriter, block, converter, *conversion);
2278 if (operationsToIgnore.empty()) {
2279 for (
unsigned i = state.numRewrites, e =
impl.rewrites.size(); i != e;
2282 dyn_cast<CreateOperationRewrite>(
impl.rewrites[i].get());
2285 operationsToIgnore.insert(createOp->getOperation());
2290 if (operationsToIgnore.insert(parentOp).second &&
2291 failed(legalize(parentOp, rewriter))) {
2293 "operation '{0}'({1}) became illegal after rewrite",
2294 parentOp->
getName(), parentOp));
2301 LogicalResult OperationLegalizer::legalizePatternCreatedOperations(
2303 RewriterState &state, RewriterState &newState) {
2304 for (
int i = state.numRewrites, e = newState.numRewrites; i != e; ++i) {
2305 auto *createOp = dyn_cast<CreateOperationRewrite>(
impl.rewrites[i].get());
2308 Operation *op = createOp->getOperation();
2309 if (failed(legalize(op, rewriter))) {
2311 "failed to legalize generated operation '{0}'({1})",
2319 LogicalResult OperationLegalizer::legalizePatternRootUpdates(
2321 RewriterState &state, RewriterState &newState) {
2322 for (
int i = state.numRewrites, e = newState.numRewrites; i != e; ++i) {
2323 auto *
rewrite = dyn_cast<ModifyOperationRewrite>(
impl.rewrites[i].get());
2327 if (failed(legalize(op, rewriter))) {
2329 impl.logger,
"failed to legalize operation updated in-place '{0}'",
2340 void OperationLegalizer::buildLegalizationGraph(
2341 LegalizationPatterns &anyOpLegalizerPatterns,
2352 applicator.walkAllPatterns([&](
const Pattern &pattern) {
2353 std::optional<OperationName> root = pattern.
getRootKind();
2359 anyOpLegalizerPatterns.push_back(&pattern);
2364 if (target.getOpAction(*root) == LegalizationAction::Legal)
2369 invalidPatterns[*root].insert(&pattern);
2371 parentOps[op].insert(*root);
2374 patternWorklist.insert(&pattern);
2382 if (!anyOpLegalizerPatterns.empty()) {
2383 for (
const Pattern *pattern : patternWorklist)
2384 legalizerPatterns[*pattern->
getRootKind()].push_back(pattern);
2388 while (!patternWorklist.empty()) {
2389 auto *pattern = patternWorklist.pop_back_val();
2393 std::optional<LegalizationAction> action = target.getOpAction(op);
2394 return !legalizerPatterns.count(op) &&
2395 (!action || action == LegalizationAction::Illegal);
2401 legalizerPatterns[*pattern->
getRootKind()].push_back(pattern);
2402 invalidPatterns[*pattern->
getRootKind()].erase(pattern);
2406 for (
auto op : parentOps[*pattern->
getRootKind()])
2407 patternWorklist.set_union(invalidPatterns[op]);
2411 void OperationLegalizer::computeLegalizationGraphBenefit(
2412 LegalizationPatterns &anyOpLegalizerPatterns,
2418 for (
auto &opIt : legalizerPatterns)
2419 if (!minOpPatternDepth.count(opIt.first))
2420 computeOpLegalizationDepth(opIt.first, minOpPatternDepth,
2426 if (!anyOpLegalizerPatterns.empty())
2427 applyCostModelToPatterns(anyOpLegalizerPatterns, minOpPatternDepth,
2433 applicator.applyCostModel([&](
const Pattern &pattern) {
2435 if (std::optional<OperationName> rootName = pattern.
getRootKind())
2436 orderedPatternList = legalizerPatterns[*rootName];
2438 orderedPatternList = anyOpLegalizerPatterns;
2441 auto *it = llvm::find(orderedPatternList, &pattern);
2442 if (it == orderedPatternList.end())
2446 return PatternBenefit(std::distance(it, orderedPatternList.end()));
2450 unsigned OperationLegalizer::computeOpLegalizationDepth(
2454 auto depthIt = minOpPatternDepth.find(op);
2455 if (depthIt != minOpPatternDepth.end())
2456 return depthIt->second;
2460 auto opPatternsIt = legalizerPatterns.find(op);
2461 if (opPatternsIt == legalizerPatterns.end() || opPatternsIt->second.empty())
2470 unsigned minDepth = applyCostModelToPatterns(
2471 opPatternsIt->second, minOpPatternDepth, legalizerPatterns);
2472 minOpPatternDepth[op] = minDepth;
2476 unsigned OperationLegalizer::applyCostModelToPatterns(
2483 SmallVector<std::pair<const Pattern *, unsigned>, 4> patternsByDepth;
2484 patternsByDepth.reserve(
patterns.size());
2488 unsigned generatedOpDepth = computeOpLegalizationDepth(
2489 generatedOp, minOpPatternDepth, legalizerPatterns);
2490 depth =
std::max(depth, generatedOpDepth + 1);
2492 patternsByDepth.emplace_back(pattern, depth);
2495 minDepth =
std::min(minDepth, depth);
2500 if (patternsByDepth.size() == 1)
2504 std::stable_sort(patternsByDepth.begin(), patternsByDepth.end(),
2505 [](
const std::pair<const Pattern *, unsigned> &lhs,
2506 const std::pair<const Pattern *, unsigned> &rhs) {
2509 if (lhs.second != rhs.second)
2510 return lhs.second < rhs.second;
2513 auto lhsBenefit = lhs.first->getBenefit();
2514 auto rhsBenefit = rhs.first->getBenefit();
2515 return lhsBenefit > rhsBenefit;
2520 for (
auto &patternIt : patternsByDepth)
2521 patterns.push_back(patternIt.first);
2529 enum OpConversionMode {
2552 OpConversionMode mode)
2567 OperationLegalizer opLegalizer;
2570 OpConversionMode mode;
2577 if (failed(opLegalizer.legalize(op, rewriter))) {
2580 if (mode == OpConversionMode::Full)
2582 <<
"failed to legalize operation '" << op->
getName() <<
"'";
2586 if (mode == OpConversionMode::Partial) {
2587 if (opLegalizer.isIllegal(op))
2589 <<
"failed to legalize operation '" << op->
getName()
2590 <<
"' that was explicitly marked illegal";
2594 }
else if (mode == OpConversionMode::Analysis) {
2604 static LogicalResult
2606 UnresolvedMaterializationRewrite *
rewrite) {
2607 UnrealizedConversionCastOp op =
rewrite->getOperation();
2608 assert(!op.use_empty() &&
2609 "expected that dead materializations have already been DCE'd");
2615 SmallVector<Value> newMaterialization;
2616 switch (
rewrite->getMaterializationKind()) {
2617 case MaterializationKind::Target:
2619 rewriter, op->getLoc(), op.getResultTypes(), inputOperands,
2622 case MaterializationKind::Source:
2623 assert(op->getNumResults() == 1 &&
"expected single result");
2625 rewriter, op->getLoc(), op.getResultTypes().front(), inputOperands);
2627 newMaterialization.push_back(sourceMat);
2630 if (!newMaterialization.empty()) {
2632 ValueRange newMaterializationRange(newMaterialization);
2633 assert(
TypeRange(newMaterializationRange) == op.getResultTypes() &&
2634 "materialization callback produced value of incorrect type");
2636 rewriter.
replaceOp(op, newMaterialization);
2642 <<
"failed to legalize unresolved materialization "
2644 << inputOperands.
getTypes() <<
") to ("
2645 << op.getResultTypes()
2646 <<
") that remained live after conversion";
2647 diag.attachNote(op->getUsers().begin()->getLoc())
2648 <<
"see existing live user here: " << *op->getUsers().begin();
2659 for (
auto *op : ops) {
2662 toConvert.push_back(op);
2665 auto legalityInfo = target.
isLegal(op);
2666 if (legalityInfo && legalityInfo->isRecursivelyLegal)
2676 for (
auto *op : toConvert)
2677 if (failed(convert(rewriter, op)))
2687 for (
auto it : materializations) {
2690 allCastOps.push_back(it.first);
2702 for (UnrealizedConversionCastOp castOp : remainingCastOps) {
2703 auto it = materializations.find(castOp);
2704 assert(it != materializations.end() &&
"inconsistent state");
2727 auto enqueueOperands = [&](UnrealizedConversionCastOp castOp) {
2728 for (
Value v : castOp.getInputs())
2729 if (
auto inputCastOp = v.getDefiningOp<UnrealizedConversionCastOp>())
2730 worklist.insert(inputCastOp);
2737 [](UnrealizedConversionCastOp castOp) -> UnrealizedConversionCastOp {
2738 if (castOp.getInputs().empty())
2741 castOp.getInputs().front().getDefiningOp<UnrealizedConversionCastOp>();
2744 if (inputCastOp.getOutputs() != castOp.getInputs())
2750 while (!worklist.empty()) {
2751 UnrealizedConversionCastOp castOp = worklist.pop_back_val();
2752 if (castOp->use_empty()) {
2755 enqueueOperands(castOp);
2756 if (remainingCastOps)
2757 erasedOps.insert(castOp.getOperation());
2764 UnrealizedConversionCastOp nextCast = castOp;
2766 if (nextCast.getInputs().getTypes() == castOp.getResultTypes()) {
2770 enqueueOperands(castOp);
2771 castOp.replaceAllUsesWith(nextCast.getInputs());
2772 if (remainingCastOps)
2773 erasedOps.insert(castOp.getOperation());
2777 nextCast = getInputCast(nextCast);
2781 if (remainingCastOps)
2782 for (UnrealizedConversionCastOp op : castOps)
2783 if (!erasedOps.contains(op.getOperation()))
2784 remainingCastOps->push_back(op);
2793 assert(!types.empty() &&
"expected valid types");
2794 remapInput(origInputNo, argTypes.size(), types.size());
2799 assert(!types.empty() &&
2800 "1->0 type remappings don't need to be added explicitly");
2801 argTypes.append(types.begin(), types.end());
2805 unsigned newInputNo,
2806 unsigned newInputCount) {
2807 assert(!remappedInputs[origInputNo] &&
"input has already been remapped");
2808 assert(newInputCount != 0 &&
"expected valid input count");
2809 remappedInputs[origInputNo] =
2810 InputMapping{newInputNo, newInputCount, {}};
2815 assert(!remappedInputs[origInputNo] &&
"input has already been remapped");
2823 assert(t &&
"expected non-null type");
2826 std::shared_lock<decltype(cacheMutex)> cacheReadLock(cacheMutex,
2829 cacheReadLock.lock();
2830 auto existingIt = cachedDirectConversions.find(t);
2831 if (existingIt != cachedDirectConversions.end()) {
2832 if (existingIt->second)
2833 results.push_back(existingIt->second);
2834 return success(existingIt->second !=
nullptr);
2836 auto multiIt = cachedMultiConversions.find(t);
2837 if (multiIt != cachedMultiConversions.end()) {
2838 results.append(multiIt->second.begin(), multiIt->second.end());
2844 size_t currentCount = results.size();
2846 std::unique_lock<decltype(cacheMutex)> cacheWriteLock(cacheMutex,
2849 for (
const ConversionCallbackFn &converter : llvm::reverse(conversions)) {
2850 if (std::optional<LogicalResult> result = converter(t, results)) {
2852 cacheWriteLock.lock();
2853 if (!succeeded(*result)) {
2854 cachedDirectConversions.try_emplace(t,
nullptr);
2857 auto newTypes =
ArrayRef<Type>(results).drop_front(currentCount);
2858 if (newTypes.size() == 1)
2859 cachedDirectConversions.try_emplace(t, newTypes.front());
2861 cachedMultiConversions.try_emplace(t, llvm::to_vector<2>(newTypes));
2875 return results.size() == 1 ? results.front() :
nullptr;
2881 for (
Type type : types)
2895 return llvm::all_of(*region, [
this](
Block &block) {
2901 return isLegal(llvm::concat<const Type>(ty.getInputs(), ty.getResults()));
2913 if (convertedTypes.empty())
2917 result.
addInputs(inputNo, convertedTypes);
2923 unsigned origInputOffset)
const {
2924 for (
unsigned i = 0, e = types.size(); i != e; ++i)
2934 for (
const MaterializationCallbackFn &fn :
2935 llvm::reverse(argumentMaterializations))
2936 if (
Value result = fn(builder, resultType, inputs, loc))
2944 for (
const MaterializationCallbackFn &fn :
2945 llvm::reverse(sourceMaterializations))
2946 if (
Value result = fn(builder, resultType, inputs, loc))
2954 Type originalType)
const {
2956 builder, loc,
TypeRange(resultType), inputs, originalType);
2959 assert(result.size() == 1 &&
"expected single result");
2960 return result.front();
2965 Type originalType)
const {
2966 for (
const TargetMaterializationCallbackFn &fn :
2967 llvm::reverse(targetMaterializations)) {
2969 fn(builder, resultTypes, inputs, loc, originalType);
2973 "callback produced incorrect number of values or values with "
2980 std::optional<TypeConverter::SignatureConversion>
2984 return std::nullopt;
3007 return impl.getInt() == resultTag;
3011 return impl.getInt() == naTag;
3015 return impl.getInt() == abortTag;
3019 assert(hasResult() &&
"Cannot get result from N/A or abort");
3020 return impl.getPointer();
3023 std::optional<Attribute>
3025 for (
const TypeAttributeConversionCallbackFn &fn :
3026 llvm::reverse(typeAttributeConversions)) {
3031 return std::nullopt;
3033 return std::nullopt;
3043 FunctionType type = dyn_cast<FunctionType>(funcOp.getFunctionType());
3049 SmallVector<Type, 1> newResults;
3051 failed(typeConverter.
convertTypes(type.getResults(), newResults)) ||
3053 typeConverter, &result)))
3070 FunctionOpInterfaceSignatureConversion(StringRef functionLikeOpName,
3078 FunctionOpInterface funcOp = cast<FunctionOpInterface>(op);
3083 struct AnyFunctionOpInterfaceSignatureConversion
3095 FailureOr<Operation *>
3099 assert(op &&
"Invalid op");
3113 return rewriter.
create(newOp);
3119 patterns.add<FunctionOpInterfaceSignatureConversion>(
3120 functionLikeOpName,
patterns.getContext(), converter);
3125 patterns.add<AnyFunctionOpInterfaceSignatureConversion>(
3135 legalOperations[op].action = action;
3140 for (StringRef dialect : dialectNames)
3141 legalDialects[dialect] = action;
3145 -> std::optional<LegalizationAction> {
3146 std::optional<LegalizationInfo> info = getOpInfo(op);
3147 return info ? info->action : std::optional<LegalizationAction>();
3151 -> std::optional<LegalOpDetails> {
3152 std::optional<LegalizationInfo> info = getOpInfo(op->
getName());
3154 return std::nullopt;
3157 auto isOpLegal = [&] {
3159 if (info->action == LegalizationAction::Dynamic) {
3160 std::optional<bool> result = info->legalityFn(op);
3166 return info->action == LegalizationAction::Legal;
3169 return std::nullopt;
3173 if (info->isRecursivelyLegal) {
3174 auto legalityFnIt = opRecursiveLegalityFns.find(op->
getName());
3175 if (legalityFnIt != opRecursiveLegalityFns.end()) {
3177 legalityFnIt->second(op).value_or(
true);
3182 return legalityDetails;
3186 std::optional<LegalizationInfo> info = getOpInfo(op->
getName());
3190 if (info->action == LegalizationAction::Dynamic) {
3191 std::optional<bool> result = info->legalityFn(op);
3198 return info->action == LegalizationAction::Illegal;
3207 auto chain = [oldCl = std::move(oldCallback), newCl = std::move(newCallback)](
3209 if (std::optional<bool> result = newCl(op))
3217 void ConversionTarget::setLegalityCallback(
3218 OperationName name,
const DynamicLegalityCallbackFn &callback) {
3219 assert(callback &&
"expected valid legality callback");
3220 auto *infoIt = legalOperations.find(name);
3221 assert(infoIt != legalOperations.end() &&
3222 infoIt->second.action == LegalizationAction::Dynamic &&
3223 "expected operation to already be marked as dynamically legal");
3224 infoIt->second.legalityFn =
3230 auto *infoIt = legalOperations.find(name);
3231 assert(infoIt != legalOperations.end() &&
3232 infoIt->second.action != LegalizationAction::Illegal &&
3233 "expected operation to already be marked as legal");
3234 infoIt->second.isRecursivelyLegal =
true;
3237 std::move(opRecursiveLegalityFns[name]), callback);
3239 opRecursiveLegalityFns.erase(name);
3242 void ConversionTarget::setLegalityCallback(
3244 assert(callback &&
"expected valid legality callback");
3245 for (StringRef dialect : dialects)
3247 std::move(dialectLegalityFns[dialect]), callback);
3250 void ConversionTarget::setLegalityCallback(
3251 const DynamicLegalityCallbackFn &callback) {
3252 assert(callback &&
"expected valid legality callback");
3257 -> std::optional<LegalizationInfo> {
3259 const auto *it = legalOperations.find(op);
3260 if (it != legalOperations.end())
3263 auto dialectIt = legalDialects.find(op.getDialectNamespace());
3264 if (dialectIt != legalDialects.end()) {
3265 DynamicLegalityCallbackFn callback;
3266 auto dialectFn = dialectLegalityFns.find(op.getDialectNamespace());
3267 if (dialectFn != dialectLegalityFns.end())
3268 callback = dialectFn->second;
3269 return LegalizationInfo{dialectIt->second,
false,
3273 if (unknownLegalityFn)
3274 return LegalizationInfo{LegalizationAction::Dynamic,
3275 false, unknownLegalityFn};
3276 return std::nullopt;
3279 #if MLIR_ENABLE_PDL_IN_PATTERNMATCH
3285 auto &rewriterImpl =
3291 auto &rewriterImpl =
3298 static FailureOr<SmallVector<Value>>
3300 SmallVector<Value> mappedValues;
3303 return std::move(mappedValues);
3307 patterns.getPDLPatterns().registerRewriteFunction(
3312 if (failed(results))
3314 return results->front();
3316 patterns.getPDLPatterns().registerRewriteFunction(
3321 patterns.getPDLPatterns().registerRewriteFunction(
3324 auto &rewriterImpl =
3334 patterns.getPDLPatterns().registerRewriteFunction(
3337 TypeRange types) -> FailureOr<SmallVector<Type>> {
3338 auto &rewriterImpl =
3345 if (failed(converter->
convertTypes(types, remappedTypes)))
3347 return std::move(remappedTypes);
3363 OpConversionMode::Partial);
3381 OpConversionMode::Full);
3404 "expected top-level op to be isolated from above");
3407 "expected ops to have a common ancestor");
3416 for (
Operation *op : ops.drop_front()) {
3420 assert(commonAncestor &&
3421 "expected to find a common isolated from above ancestor");
3425 return commonAncestor;
3432 if (
config.legalizableOps)
3433 assert(
config.legalizableOps->empty() &&
"expected empty set");
3443 inverseOperationMap[it.second] = it.first;
3449 OpConversionMode::Analysis);
3450 LogicalResult status = opConverter.convertOperations(opsToConvert);
3454 if (
config.legalizableOps) {
3457 originalLegalizableOps.insert(inverseOperationMap[op]);
3458 *
config.legalizableOps = std::move(originalLegalizableOps);
3462 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::@1181::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.
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.
void replaceOpWithMultiple(Operation *op, ArrayRef< ValueRange > newValues)
Replace the given operation with the new value ranges.
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).
void notifyOpReplaced(Operation *op, ArrayRef< ValueRange > newValues)
Notifies that an op is about to be replaced with the given values.
const ConversionConfig & config
Dialect conversion configuration.
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.