10 #include "mlir/Config/mlir-config.h"
18 #include "llvm/ADT/ScopeExit.h"
19 #include "llvm/ADT/SetVector.h"
20 #include "llvm/ADT/SmallPtrSet.h"
21 #include "llvm/Support/Debug.h"
22 #include "llvm/Support/FormatVariadic.h"
23 #include "llvm/Support/SaveAndRestore.h"
24 #include "llvm/Support/ScopedPrinter.h"
30 #define DEBUG_TYPE "dialect-conversion"
33 template <
typename... Args>
34 static void logSuccess(llvm::ScopedPrinter &os, StringRef fmt, Args &&...args) {
37 os.startLine() <<
"} -> SUCCESS";
39 os.getOStream() <<
" : "
40 << llvm::formatv(fmt.data(), std::forward<Args>(args)...);
41 os.getOStream() <<
"\n";
46 template <
typename... Args>
47 static void logFailure(llvm::ScopedPrinter &os, StringRef fmt, Args &&...args) {
50 os.startLine() <<
"} -> FAILURE : "
51 << llvm::formatv(fmt.data(), std::forward<Args>(args)...)
61 if (
OpResult inputRes = dyn_cast<OpResult>(value))
62 insertPt = ++inputRes.getOwner()->getIterator();
73 struct ConversionValueMapping {
76 bool isMappedTo(
Value value)
const {
return mappedTo.contains(value); }
88 Value lookupOrDefault(
Value from,
Type desiredType =
nullptr)
const;
98 for (
Value it = newVal; it; it = mapping.lookupOrNull(it))
99 assert(it != oldVal &&
"inserting cyclic mapping");
101 mapping.map(oldVal, newVal);
102 mappedTo.insert(newVal);
106 void erase(
Value value) { mapping.erase(value); }
117 Value ConversionValueMapping::lookupOrDefault(
Value from,
118 Type desiredType)
const {
123 if (!desiredType || from.
getType() == desiredType)
126 Value mappedValue = mapping.lookupOrNull(from);
133 return desiredValue ? desiredValue : from;
136 Value ConversionValueMapping::lookupOrNull(
Value from,
Type desiredType)
const {
137 Value result = lookupOrDefault(from, desiredType);
138 if (result == from || (desiredType && result.
getType() != desiredType))
149 struct RewriterState {
150 RewriterState(
unsigned numRewrites,
unsigned numIgnoredOperations,
151 unsigned numReplacedOps)
152 : numRewrites(numRewrites), numIgnoredOperations(numIgnoredOperations),
153 numReplacedOps(numReplacedOps) {}
156 unsigned numRewrites;
159 unsigned numIgnoredOperations;
162 unsigned numReplacedOps;
194 UnresolvedMaterialization
197 virtual ~IRRewrite() =
default;
200 virtual void rollback() = 0;
219 Kind getKind()
const {
return kind; }
221 static bool classof(
const IRRewrite *
rewrite) {
return true; }
225 : kind(kind), rewriterImpl(rewriterImpl) {}
234 class BlockRewrite :
public IRRewrite {
237 Block *getBlock()
const {
return block; }
239 static bool classof(
const IRRewrite *
rewrite) {
240 return rewrite->getKind() >= Kind::CreateBlock &&
241 rewrite->getKind() <= Kind::ReplaceBlockArg;
247 : IRRewrite(kind, rewriterImpl), block(block) {}
256 class CreateBlockRewrite :
public BlockRewrite {
259 : BlockRewrite(
Kind::CreateBlock, rewriterImpl, block) {}
261 static bool classof(
const IRRewrite *
rewrite) {
262 return rewrite->getKind() == Kind::CreateBlock;
268 listener->notifyBlockInserted(block, {}, {});
271 void rollback()
override {
274 auto &blockOps = block->getOperations();
275 while (!blockOps.empty())
276 blockOps.remove(blockOps.begin());
277 block->dropAllUses();
278 if (block->getParent())
289 class EraseBlockRewrite :
public BlockRewrite {
292 : BlockRewrite(
Kind::EraseBlock, rewriterImpl, block),
293 region(block->getParent()), insertBeforeBlock(block->getNextNode()) {}
295 static bool classof(
const IRRewrite *
rewrite) {
296 return rewrite->getKind() == Kind::EraseBlock;
299 ~EraseBlockRewrite()
override {
301 "rewrite was neither rolled back nor committed/cleaned up");
304 void rollback()
override {
307 assert(block &&
"expected block");
308 auto &blockList = region->getBlocks();
312 blockList.insert(before, block);
318 assert(block &&
"expected block");
319 assert(block->empty() &&
"expected empty block");
323 dyn_cast_or_null<RewriterBase::Listener>(rewriter.
getListener()))
324 listener->notifyBlockErased(block);
329 block->dropAllDefinedValueUses();
340 Block *insertBeforeBlock;
346 class InlineBlockRewrite :
public BlockRewrite {
350 : BlockRewrite(
Kind::InlineBlock, rewriterImpl, block),
351 sourceBlock(sourceBlock),
352 firstInlinedInst(sourceBlock->empty() ? nullptr
353 : &sourceBlock->front()),
354 lastInlinedInst(sourceBlock->empty() ? nullptr : &sourceBlock->back()) {
360 assert(!getConfig().listener &&
361 "InlineBlockRewrite not supported if listener is attached");
364 static bool classof(
const IRRewrite *
rewrite) {
365 return rewrite->getKind() == Kind::InlineBlock;
368 void rollback()
override {
371 if (firstInlinedInst) {
372 assert(lastInlinedInst &&
"expected operation");
392 class MoveBlockRewrite :
public BlockRewrite {
396 : BlockRewrite(
Kind::MoveBlock, rewriterImpl, block), region(region),
397 insertBeforeBlock(insertBeforeBlock) {}
399 static bool classof(
const IRRewrite *
rewrite) {
400 return rewrite->getKind() == Kind::MoveBlock;
408 listener->notifyBlockInserted(block, region,
413 void rollback()
override {
426 Block *insertBeforeBlock;
430 class BlockTypeConversionRewrite :
public BlockRewrite {
434 : BlockRewrite(
Kind::BlockTypeConversion, rewriterImpl, origBlock),
435 newBlock(newBlock) {}
437 static bool classof(
const IRRewrite *
rewrite) {
438 return rewrite->getKind() == Kind::BlockTypeConversion;
441 Block *getOrigBlock()
const {
return block; }
443 Block *getNewBlock()
const {
return newBlock; }
447 void rollback()
override;
457 class ReplaceBlockArgRewrite :
public BlockRewrite {
462 : BlockRewrite(
Kind::ReplaceBlockArg, rewriterImpl, block), arg(arg),
463 converter(converter) {}
465 static bool classof(
const IRRewrite *
rewrite) {
466 return rewrite->getKind() == Kind::ReplaceBlockArg;
471 void rollback()
override;
481 class OperationRewrite :
public IRRewrite {
484 Operation *getOperation()
const {
return op; }
486 static bool classof(
const IRRewrite *
rewrite) {
487 return rewrite->getKind() >= Kind::MoveOperation &&
488 rewrite->getKind() <= Kind::UnresolvedMaterialization;
494 : IRRewrite(kind, rewriterImpl), op(op) {}
501 class MoveOperationRewrite :
public OperationRewrite {
505 : OperationRewrite(
Kind::MoveOperation, rewriterImpl, op), block(block),
506 insertBeforeOp(insertBeforeOp) {}
508 static bool classof(
const IRRewrite *
rewrite) {
509 return rewrite->getKind() == Kind::MoveOperation;
517 listener->notifyOperationInserted(
523 void rollback()
override {
527 block->
getOperations().splice(before, op->getBlock()->getOperations(), op);
541 class ModifyOperationRewrite :
public OperationRewrite {
545 : OperationRewrite(
Kind::ModifyOperation, rewriterImpl, op),
546 name(op->getName()), loc(op->getLoc()), attrs(op->getAttrDictionary()),
547 operands(op->operand_begin(), op->operand_end()),
548 successors(op->successor_begin(), op->successor_end()) {
553 name.initOpProperties(propCopy, prop);
557 static bool classof(
const IRRewrite *
rewrite) {
558 return rewrite->getKind() == Kind::ModifyOperation;
561 ~ModifyOperationRewrite()
override {
562 assert(!propertiesStorage &&
563 "rewrite was neither committed nor rolled back");
569 dyn_cast_or_null<RewriterBase::Listener>(rewriter.
getListener()))
570 listener->notifyOperationModified(op);
572 if (propertiesStorage) {
576 name.destroyOpProperties(propCopy);
577 operator delete(propertiesStorage);
578 propertiesStorage =
nullptr;
582 void rollback()
override {
588 if (propertiesStorage) {
591 name.destroyOpProperties(propCopy);
592 operator delete(propertiesStorage);
593 propertiesStorage =
nullptr;
600 DictionaryAttr attrs;
603 void *propertiesStorage =
nullptr;
610 class ReplaceOperationRewrite :
public OperationRewrite {
614 : OperationRewrite(
Kind::ReplaceOperation, rewriterImpl, op),
615 converter(converter) {}
617 static bool classof(
const IRRewrite *
rewrite) {
618 return rewrite->getKind() == Kind::ReplaceOperation;
623 void rollback()
override;
633 class CreateOperationRewrite :
public OperationRewrite {
637 : OperationRewrite(
Kind::CreateOperation, rewriterImpl, op) {}
639 static bool classof(
const IRRewrite *
rewrite) {
640 return rewrite->getKind() == Kind::CreateOperation;
646 listener->notifyOperationInserted(op, {});
649 void rollback()
override;
653 enum MaterializationKind {
670 class UnresolvedMaterializationRewrite :
public OperationRewrite {
673 UnrealizedConversionCastOp op,
675 MaterializationKind kind,
Type originalType,
678 static bool classof(
const IRRewrite *
rewrite) {
679 return rewrite->getKind() == Kind::UnresolvedMaterialization;
682 void rollback()
override;
684 UnrealizedConversionCastOp getOperation()
const {
685 return cast<UnrealizedConversionCastOp>(op);
690 return converterAndKind.getPointer();
694 MaterializationKind getMaterializationKind()
const {
695 return converterAndKind.getInt();
699 Type getOriginalType()
const {
return originalType; }
704 llvm::PointerIntPair<const TypeConverter *, 2, MaterializationKind>
717 #if MLIR_ENABLE_EXPENSIVE_PATTERN_API_CHECKS
720 template <
typename RewriteTy,
typename R>
721 static bool hasRewrite(R &&rewrites,
Operation *op) {
722 return any_of(std::forward<R>(rewrites), [&](
auto &
rewrite) {
723 auto *rewriteTy = dyn_cast<RewriteTy>(
rewrite.get());
724 return rewriteTy && rewriteTy->getOperation() == op;
730 template <
typename RewriteTy,
typename R>
731 static bool hasRewrite(R &&rewrites,
Block *block) {
732 return any_of(std::forward<R>(rewrites), [&](
auto &
rewrite) {
733 auto *rewriteTy = dyn_cast<RewriteTy>(
rewrite.get());
734 return rewriteTy && rewriteTy->getBlock() == block;
754 RewriterState getCurrentState();
758 void applyRewrites();
761 void resetState(RewriterState state);
765 template <
typename RewriteTy,
typename... Args>
768 std::make_unique<RewriteTy>(*
this, std::forward<Args>(args)...));
773 void undoRewrites(
unsigned numRewritesToKeep = 0);
779 LogicalResult remapValues(StringRef valueDiagTag,
780 std::optional<Location> inputLoc,
807 Block *applySignatureConversion(
830 UnrealizedConversionCastOp *castOp =
nullptr);
835 UnrealizedConversionCastOp *castOp =
nullptr) {
836 return buildUnresolvedMaterialization(kind, ip, loc, valueToMap, inputs,
862 Value findOrBuildReplacementValue(
Value value,
880 void notifyOperationInserted(
Operation *op,
887 void notifyBlockIsBeingErased(
Block *block);
890 void notifyBlockInserted(
Block *block,
Region *previous,
894 void notifyBlockBeingInlined(
Block *block,
Block *srcBlock,
924 if (wasErased(block))
926 assert(block->
empty() &&
"expected empty block");
931 bool wasErased(
void *ptr)
const {
return erased.contains(ptr); }
999 llvm::ScopedPrinter logger{llvm::dbgs()};
1006 return rewriterImpl.
config;
1009 void BlockTypeConversionRewrite::commit(
RewriterBase &rewriter) {
1013 if (
auto *listener =
1014 dyn_cast_or_null<RewriterBase::Listener>(rewriter.
getListener()))
1015 for (
Operation *op : getNewBlock()->getUsers())
1016 listener->notifyOperationModified(op);
1019 void BlockTypeConversionRewrite::rollback() {
1020 getNewBlock()->replaceAllUsesWith(getOrigBlock());
1023 void ReplaceBlockArgRewrite::commit(
RewriterBase &rewriter) {
1028 if (isa<BlockArgument>(repl)) {
1036 Operation *replOp = cast<OpResult>(repl).getOwner();
1044 void ReplaceBlockArgRewrite::rollback() { rewriterImpl.
mapping.erase(arg); }
1046 void ReplaceOperationRewrite::commit(
RewriterBase &rewriter) {
1048 dyn_cast_or_null<RewriterBase::Listener>(rewriter.
getListener());
1053 return rewriterImpl.findOrBuildReplacementValue(result, converter);
1058 listener->notifyOperationReplaced(op, replacements);
1061 for (
auto [result, newValue] :
1062 llvm::zip_equal(op->
getResults(), replacements))
1068 if (getConfig().unlegalizedOps)
1069 getConfig().unlegalizedOps->erase(op);
1075 [&](
Operation *op) { listener->notifyOperationErased(op); });
1083 void ReplaceOperationRewrite::rollback() {
1085 rewriterImpl.
mapping.erase(result);
1088 void ReplaceOperationRewrite::cleanup(
RewriterBase &rewriter) {
1092 void CreateOperationRewrite::rollback() {
1094 while (!region.getBlocks().empty())
1095 region.getBlocks().remove(region.getBlocks().begin());
1101 UnresolvedMaterializationRewrite::UnresolvedMaterializationRewrite(
1105 : OperationRewrite(
Kind::UnresolvedMaterialization, rewriterImpl, op),
1106 converterAndKind(converter, kind), originalType(originalType),
1107 mappedValue(mappedValue) {
1108 assert((!originalType || kind == MaterializationKind::Target) &&
1109 "original type is valid only for target materializations");
1113 void UnresolvedMaterializationRewrite::rollback() {
1115 rewriterImpl.
mapping.erase(mappedValue);
1126 for (
size_t i = 0; i <
rewrites.size(); ++i)
1146 while (
ignoredOps.size() != state.numIgnoredOperations)
1149 while (
replacedOps.size() != state.numReplacedOps)
1155 llvm::reverse(llvm::drop_begin(
rewrites, numRewritesToKeep)))
1157 rewrites.resize(numRewritesToKeep);
1161 StringRef valueDiagTag, std::optional<Location> inputLoc,
1164 remapped.reserve(llvm::size(values));
1167 Value operand = it.value();
1182 remapped.push_back(std::move(unpacked));
1190 diag <<
"unable to convert type for " << valueDiagTag <<
" #"
1191 << it.index() <<
", type was " << origType;
1197 if (legalTypes.empty()) {
1198 remapped.push_back({});
1202 if (legalTypes.size() != 1) {
1207 if (
TypeRange(unpackedRange) == legalTypes) {
1208 remapped.push_back(std::move(unpacked));
1217 legalTypes, origType,
1219 remapped.push_back(targetMat);
1224 Type desiredType = legalTypes.front();
1227 Value newOperand =
mapping.lookupOrDefault(operand, desiredType);
1228 if (newOperand.
getType() != desiredType) {
1234 operandLoc, newOperand, unpacked,
1235 desiredType, origType,
1237 newOperand = castValue;
1239 remapped.push_back({newOperand});
1262 if (region->
empty())
1267 llvm::make_early_inc_range(llvm::drop_begin(*region, 1))) {
1269 std::optional<TypeConverter::SignatureConversion> conversion =
1279 if (entryConversion)
1282 std::optional<TypeConverter::SignatureConversion> conversion =
1294 #if MLIR_ENABLE_EXPENSIVE_PATTERN_API_CHECKS
1296 if (hasRewrite<BlockTypeConversionRewrite>(
rewrites, block))
1297 llvm::report_fatal_error(
"block was already converted");
1311 for (
unsigned i = 0; i < origArgCount; ++i) {
1313 if (!inputMap || inputMap->replacementValue)
1316 for (
unsigned j = 0;
j < inputMap->size; ++
j)
1317 newLocs[inputMap->inputNo +
j] = origLoc;
1324 convertedTypes, newLocs);
1334 appendRewrite<InlineBlockRewrite>(newBlock, block, newBlock->
end());
1337 while (!block->
empty())
1344 for (
unsigned i = 0; i != origArgCount; ++i) {
1348 std::optional<TypeConverter::SignatureConversion::InputMapping> inputMap =
1354 MaterializationKind::Source,
1357 origArgType,
Type(), converter);
1358 appendRewrite<ReplaceBlockArgRewrite>(block, origArg, converter);
1362 if (
Value repl = inputMap->replacementValue) {
1364 assert(inputMap->size == 0 &&
1365 "invalid to provide a replacement value when the argument isn't "
1368 appendRewrite<ReplaceBlockArgRewrite>(block, origArg, converter);
1377 newBlock->
getArguments().slice(inputMap->inputNo, inputMap->size);
1378 if (replArgs.size() == 1) {
1379 mapping.map(origArg, replArgs.front());
1383 replArgs, origArg, converter);
1385 appendRewrite<ReplaceBlockArgRewrite>(block, origArg, converter);
1388 appendRewrite<BlockTypeConversionRewrite>(block, newBlock);
1407 UnrealizedConversionCastOp *castOp) {
1408 assert((!originalType || kind == MaterializationKind::Target) &&
1409 "original type is valid only for target materializations");
1414 assert(inputs.size() == 1 &&
"1:N mapping is not supported");
1415 mapping.map(valueToMap, inputs.front());
1422 OpBuilder builder(outputTypes.front().getContext());
1425 builder.
create<UnrealizedConversionCastOp>(loc, outputTypes, inputs);
1427 assert(outputTypes.size() == 1 &&
"1:N mapping is not supported");
1428 mapping.map(valueToMap, convertOp.getResult(0));
1431 *castOp = convertOp;
1432 appendRewrite<UnresolvedMaterializationRewrite>(convertOp, converter, kind,
1433 originalType, valueToMap);
1434 return convertOp.getResults();
1442 UnrealizedConversionCastOp argCastOp;
1445 replacements, originalType,
1446 Type(), converter, &argCastOp);
1463 [&](
Operation *op) { return replacedOps.contains(op); }) &&
1470 repl =
mapping.lookupOrNull(value);
1483 mapping.map(value, castValue);
1491 auto castOp = value.
getDefiningOp<UnrealizedConversionCastOp>();
1496 assert(castOp->getNumResults() == 1 &&
"expected single result");
1499 for (
Value v : castOp.getOperands()) {
1515 logger.startLine() <<
"** Insert : '" << op->
getName() <<
"'(" << op
1519 "attempting to insert into a block within a replaced/erased op");
1521 if (!previous.
isSet()) {
1523 appendRewrite<CreateOperationRewrite>(op);
1529 appendRewrite<MoveOperationRewrite>(op, previous.
getBlock(), prevOp);
1535 assert(!
ignoredOps.contains(op) &&
"operation was already replaced");
1539 bool isUnresolvedMaterialization =
false;
1540 if (
auto castOp = dyn_cast<UnrealizedConversionCastOp>(op))
1542 isUnresolvedMaterialization =
true;
1545 for (
auto [repl, result] : llvm::zip_equal(newValues, op->
getResults())) {
1548 if (isUnresolvedMaterialization) {
1568 assert(!isUnresolvedMaterialization &&
1569 "attempting to replace an unresolved materialization");
1576 if (repl.size() == 1) {
1578 mapping.map(result, repl.front());
1593 appendRewrite<EraseBlockRewrite>(block);
1599 "attempting to insert into a region within a replaced/erased op");
1604 logger.startLine() <<
"** Insert Block into : '" << parent->
getName()
1605 <<
"'(" << parent <<
")\n";
1608 <<
"** Insert Block into detached Region (nullptr parent op)'";
1614 appendRewrite<CreateBlockRewrite>(block);
1617 Block *prevBlock = previousIt == previous->
end() ? nullptr : &*previousIt;
1618 appendRewrite<MoveBlockRewrite>(block, previous, prevBlock);
1623 appendRewrite<InlineBlockRewrite>(block, srcBlock, before);
1630 reasonCallback(
diag);
1631 logger.startLine() <<
"** Failure : " <<
diag.str() <<
"\n";
1641 ConversionPatternRewriter::ConversionPatternRewriter(
1645 setListener(
impl.get());
1651 assert(op && newOp &&
"expected non-null op");
1657 "incorrect # of replacement values");
1659 impl->logger.startLine()
1660 <<
"** Replace : '" << op->
getName() <<
"'(" << op <<
")\n";
1663 for (
size_t i = 0; i < newValues.size(); ++i)
1664 newVals.push_back(newValues.slice(i, 1));
1665 impl->notifyOpReplaced(op, newVals);
1671 "incorrect # of replacement values");
1673 impl->logger.startLine()
1674 <<
"** Replace : '" << op->
getName() <<
"'(" << op <<
")\n";
1676 impl->notifyOpReplaced(op, newValues);
1681 impl->logger.startLine()
1682 <<
"** Erase : '" << op->
getName() <<
"'(" << op <<
")\n";
1685 impl->notifyOpReplaced(op, nullRepls);
1690 "attempting to erase a block within a replaced/erased op");
1700 impl->notifyBlockIsBeingErased(block);
1708 "attempting to apply a signature conversion to a block within a "
1709 "replaced/erased op");
1710 return impl->applySignatureConversion(*
this, block, converter, conversion);
1717 "attempting to apply a signature conversion to a block within a "
1718 "replaced/erased op");
1719 return impl->convertRegionTypes(*
this, region, converter, entryConversion);
1726 impl->logger.startLine() <<
"** Replace Argument : '" << from
1727 <<
"'(in region of '" << parentOp->
getName()
1730 impl->appendRewrite<ReplaceBlockArgRewrite>(from.
getOwner(), from,
1731 impl->currentTypeConverter);
1732 impl->mapping.map(
impl->mapping.lookupOrDefault(from), to);
1737 if (failed(
impl->remapValues(
"value", std::nullopt, *
this, key,
1740 assert(remappedValues.front().size() == 1 &&
"1:N conversion not supported");
1741 return remappedValues.front().front();
1750 if (failed(
impl->remapValues(
"value", std::nullopt, *
this, keys,
1753 for (
const auto &values : remapped) {
1754 assert(values.size() == 1 &&
"1:N conversion not supported");
1755 results.push_back(values.front());
1765 "incorrect # of argument replacement values");
1767 "attempting to inline a block from a replaced/erased op");
1769 "attempting to inline a block into a replaced/erased op");
1770 auto opIgnored = [&](
Operation *op) {
return impl->isOpIgnored(op); };
1773 assert(llvm::all_of(source->
getUsers(), opIgnored) &&
1774 "expected 'source' to have no predecessors");
1783 bool fastPath = !
impl->config.listener;
1786 impl->notifyBlockBeingInlined(dest, source, before);
1789 for (
auto it : llvm::zip(source->
getArguments(), argValues))
1790 replaceUsesOfBlockArgument(std::get<0>(it), std::get<1>(it));
1797 while (!source->
empty())
1798 moveOpBefore(&source->
front(), dest, before);
1806 assert(!
impl->wasOpReplaced(op) &&
1807 "attempting to modify a replaced/erased op");
1809 impl->pendingRootUpdates.insert(op);
1811 impl->appendRewrite<ModifyOperationRewrite>(op);
1815 assert(!
impl->wasOpReplaced(op) &&
1816 "attempting to modify a replaced/erased op");
1821 assert(
impl->pendingRootUpdates.erase(op) &&
1822 "operation did not have a pending in-place update");
1828 assert(
impl->pendingRootUpdates.erase(op) &&
1829 "operation did not have a pending in-place update");
1832 auto it = llvm::find_if(
1833 llvm::reverse(
impl->rewrites), [&](std::unique_ptr<IRRewrite> &
rewrite) {
1834 auto *modifyRewrite = dyn_cast<ModifyOperationRewrite>(rewrite.get());
1835 return modifyRewrite && modifyRewrite->getOperation() == op;
1837 assert(it !=
impl->rewrites.rend() &&
"no root update started on op");
1839 int updateIdx = std::prev(
impl->rewrites.rend()) - it;
1840 impl->rewrites.erase(
impl->rewrites.begin() + updateIdx);
1854 oneToOneOperands.reserve(operands.size());
1856 if (operand.size() != 1)
1857 llvm::report_fatal_error(
"pattern '" + getDebugName() +
1858 "' does not support 1:N conversion");
1859 oneToOneOperands.push_back(operand.front());
1861 return oneToOneOperands;
1868 auto &rewriterImpl = dialectRewriter.getImpl();
1872 getTypeConverter());
1881 llvm::to_vector_of<ValueRange>(remapped);
1882 return matchAndRewrite(op, remappedAsRange, dialectRewriter);
1894 class OperationLegalizer {
1914 LogicalResult legalizeWithFold(
Operation *op,
1919 LogicalResult legalizeWithPattern(
Operation *op,
1930 RewriterState &curState);
1934 legalizePatternBlockRewrites(
Operation *op,
1937 RewriterState &state, RewriterState &newState);
1938 LogicalResult legalizePatternCreatedOperations(
1940 RewriterState &state, RewriterState &newState);
1943 RewriterState &state,
1944 RewriterState &newState);
1954 void buildLegalizationGraph(
1955 LegalizationPatterns &anyOpLegalizerPatterns,
1966 void computeLegalizationGraphBenefit(
1967 LegalizationPatterns &anyOpLegalizerPatterns,
1972 unsigned computeOpLegalizationDepth(
1979 unsigned applyCostModelToPatterns(
2005 LegalizationPatterns anyOpLegalizerPatterns;
2007 buildLegalizationGraph(anyOpLegalizerPatterns, legalizerPatterns);
2008 computeLegalizationGraphBenefit(anyOpLegalizerPatterns, legalizerPatterns);
2011 bool OperationLegalizer::isIllegal(
Operation *op)
const {
2012 return target.isIllegal(op);
2016 OperationLegalizer::legalize(
Operation *op,
2019 const char *logLineComment =
2020 "//===-------------------------------------------===//\n";
2025 logger.getOStream() <<
"\n";
2026 logger.startLine() << logLineComment;
2027 logger.startLine() <<
"Legalizing operation : '" << op->
getName() <<
"'("
2033 op->print(logger.startLine(), OpPrintingFlags().printGenericOpForm());
2034 logger.getOStream() <<
"\n\n";
2039 if (
auto legalityInfo = target.isLegal(op)) {
2042 logger,
"operation marked legal by the target{0}",
2043 legalityInfo->isRecursivelyLegal
2044 ?
"; NOTE: operation is recursively legal; skipping internals"
2046 logger.startLine() << logLineComment;
2051 if (legalityInfo->isRecursivelyLegal) {
2064 logSuccess(logger,
"operation marked 'ignored' during conversion");
2065 logger.startLine() << logLineComment;
2073 if (succeeded(legalizeWithFold(op, rewriter))) {
2076 logger.startLine() << logLineComment;
2082 if (succeeded(legalizeWithPattern(op, rewriter))) {
2085 logger.startLine() << logLineComment;
2091 logFailure(logger,
"no matched legalization pattern");
2092 logger.startLine() << logLineComment;
2098 OperationLegalizer::legalizeWithFold(
Operation *op,
2100 auto &rewriterImpl = rewriter.
getImpl();
2104 rewriterImpl.
logger.startLine() <<
"* Fold {\n";
2105 rewriterImpl.
logger.indent();
2111 if (failed(rewriter.
tryFold(op, replacementValues))) {
2117 if (replacementValues.empty())
2118 return legalize(op, rewriter);
2121 rewriter.
replaceOp(op, replacementValues);
2124 for (
unsigned i = curState.numRewrites, e = rewriterImpl.
rewrites.size();
2127 dyn_cast<CreateOperationRewrite>(rewriterImpl.
rewrites[i].get());
2130 if (failed(legalize(createOp->getOperation(), rewriter))) {
2132 "failed to legalize generated constant '{0}'",
2133 createOp->getOperation()->getName()));
2144 OperationLegalizer::legalizeWithPattern(
Operation *op,
2146 auto &rewriterImpl = rewriter.
getImpl();
2149 auto canApply = [&](
const Pattern &pattern) {
2150 bool canApply = canApplyPattern(op, pattern, rewriter);
2151 if (canApply &&
config.listener)
2152 config.listener->notifyPatternBegin(pattern, op);
2158 auto onFailure = [&](
const Pattern &pattern) {
2164 diag <<
"Failed to apply pattern \"" << pattern.getDebugName()
2171 config.listener->notifyPatternEnd(pattern, failure());
2173 appliedPatterns.erase(&pattern);
2178 auto onSuccess = [&](
const Pattern &pattern) {
2180 auto result = legalizePatternResult(op, pattern, rewriter, curState);
2181 appliedPatterns.erase(&pattern);
2185 config.listener->notifyPatternEnd(pattern, result);
2190 return applicator.matchAndRewrite(op, rewriter, canApply, onFailure,
2194 bool OperationLegalizer::canApplyPattern(
Operation *op,
const Pattern &pattern,
2197 auto &os = rewriter.
getImpl().logger;
2198 os.getOStream() <<
"\n";
2199 os.startLine() <<
"* Pattern : '" << op->
getName() <<
" -> (";
2201 os.getOStream() <<
")' {\n";
2208 !appliedPatterns.insert(&pattern).second) {
2217 OperationLegalizer::legalizePatternResult(
Operation *op,
const Pattern &pattern,
2219 RewriterState &curState) {
2221 assert(
impl.pendingRootUpdates.empty() &&
"dangling root updates");
2223 #if MLIR_ENABLE_EXPENSIVE_PATTERN_API_CHECKS
2225 auto newRewrites = llvm::drop_begin(
impl.rewrites, curState.numRewrites);
2226 auto replacedRoot = [&] {
2227 return hasRewrite<ReplaceOperationRewrite>(newRewrites, op);
2229 auto updatedRootInPlace = [&] {
2230 return hasRewrite<ModifyOperationRewrite>(newRewrites, op);
2232 if (!replacedRoot() && !updatedRootInPlace())
2233 llvm::report_fatal_error(
"expected pattern to replace the root operation");
2237 RewriterState newState =
impl.getCurrentState();
2238 if (failed(legalizePatternBlockRewrites(op, rewriter,
impl, curState,
2240 failed(legalizePatternRootUpdates(rewriter,
impl, curState, newState)) ||
2241 failed(legalizePatternCreatedOperations(rewriter,
impl, curState,
2246 LLVM_DEBUG(
logSuccess(
impl.logger,
"pattern applied successfully"));
2250 LogicalResult OperationLegalizer::legalizePatternBlockRewrites(
2253 RewriterState &newState) {
2258 for (
int i = state.numRewrites, e = newState.numRewrites; i != e; ++i) {
2259 BlockRewrite *
rewrite = dyn_cast<BlockRewrite>(
impl.rewrites[i].get());
2263 if (isa<BlockTypeConversionRewrite, EraseBlockRewrite,
2264 ReplaceBlockArgRewrite>(
rewrite))
2273 if (
auto *converter =
impl.regionToConverter.lookup(block->
getParent())) {
2274 std::optional<TypeConverter::SignatureConversion> conversion =
2277 LLVM_DEBUG(
logFailure(
impl.logger,
"failed to convert types of moved "
2281 impl.applySignatureConversion(rewriter, block, converter, *conversion);
2289 if (operationsToIgnore.empty()) {
2290 for (
unsigned i = state.numRewrites, e =
impl.rewrites.size(); i != e;
2293 dyn_cast<CreateOperationRewrite>(
impl.rewrites[i].get());
2296 operationsToIgnore.insert(createOp->getOperation());
2301 if (operationsToIgnore.insert(parentOp).second &&
2302 failed(legalize(parentOp, rewriter))) {
2304 "operation '{0}'({1}) became illegal after rewrite",
2305 parentOp->
getName(), parentOp));
2312 LogicalResult OperationLegalizer::legalizePatternCreatedOperations(
2314 RewriterState &state, RewriterState &newState) {
2315 for (
int i = state.numRewrites, e = newState.numRewrites; i != e; ++i) {
2316 auto *createOp = dyn_cast<CreateOperationRewrite>(
impl.rewrites[i].get());
2319 Operation *op = createOp->getOperation();
2320 if (failed(legalize(op, rewriter))) {
2322 "failed to legalize generated operation '{0}'({1})",
2330 LogicalResult OperationLegalizer::legalizePatternRootUpdates(
2332 RewriterState &state, RewriterState &newState) {
2333 for (
int i = state.numRewrites, e = newState.numRewrites; i != e; ++i) {
2334 auto *
rewrite = dyn_cast<ModifyOperationRewrite>(
impl.rewrites[i].get());
2338 if (failed(legalize(op, rewriter))) {
2340 impl.logger,
"failed to legalize operation updated in-place '{0}'",
2351 void OperationLegalizer::buildLegalizationGraph(
2352 LegalizationPatterns &anyOpLegalizerPatterns,
2363 applicator.walkAllPatterns([&](
const Pattern &pattern) {
2364 std::optional<OperationName> root = pattern.
getRootKind();
2370 anyOpLegalizerPatterns.push_back(&pattern);
2375 if (target.getOpAction(*root) == LegalizationAction::Legal)
2380 invalidPatterns[*root].insert(&pattern);
2382 parentOps[op].insert(*root);
2385 patternWorklist.insert(&pattern);
2393 if (!anyOpLegalizerPatterns.empty()) {
2394 for (
const Pattern *pattern : patternWorklist)
2395 legalizerPatterns[*pattern->
getRootKind()].push_back(pattern);
2399 while (!patternWorklist.empty()) {
2400 auto *pattern = patternWorklist.pop_back_val();
2404 std::optional<LegalizationAction> action = target.getOpAction(op);
2405 return !legalizerPatterns.count(op) &&
2406 (!action || action == LegalizationAction::Illegal);
2412 legalizerPatterns[*pattern->
getRootKind()].push_back(pattern);
2413 invalidPatterns[*pattern->
getRootKind()].erase(pattern);
2417 for (
auto op : parentOps[*pattern->
getRootKind()])
2418 patternWorklist.set_union(invalidPatterns[op]);
2422 void OperationLegalizer::computeLegalizationGraphBenefit(
2423 LegalizationPatterns &anyOpLegalizerPatterns,
2429 for (
auto &opIt : legalizerPatterns)
2430 if (!minOpPatternDepth.count(opIt.first))
2431 computeOpLegalizationDepth(opIt.first, minOpPatternDepth,
2437 if (!anyOpLegalizerPatterns.empty())
2438 applyCostModelToPatterns(anyOpLegalizerPatterns, minOpPatternDepth,
2444 applicator.applyCostModel([&](
const Pattern &pattern) {
2446 if (std::optional<OperationName> rootName = pattern.
getRootKind())
2447 orderedPatternList = legalizerPatterns[*rootName];
2449 orderedPatternList = anyOpLegalizerPatterns;
2452 auto *it = llvm::find(orderedPatternList, &pattern);
2453 if (it == orderedPatternList.end())
2457 return PatternBenefit(std::distance(it, orderedPatternList.end()));
2461 unsigned OperationLegalizer::computeOpLegalizationDepth(
2465 auto depthIt = minOpPatternDepth.find(op);
2466 if (depthIt != minOpPatternDepth.end())
2467 return depthIt->second;
2471 auto opPatternsIt = legalizerPatterns.find(op);
2472 if (opPatternsIt == legalizerPatterns.end() || opPatternsIt->second.empty())
2481 unsigned minDepth = applyCostModelToPatterns(
2482 opPatternsIt->second, minOpPatternDepth, legalizerPatterns);
2483 minOpPatternDepth[op] = minDepth;
2487 unsigned OperationLegalizer::applyCostModelToPatterns(
2495 patternsByDepth.reserve(
patterns.size());
2499 unsigned generatedOpDepth = computeOpLegalizationDepth(
2500 generatedOp, minOpPatternDepth, legalizerPatterns);
2501 depth =
std::max(depth, generatedOpDepth + 1);
2503 patternsByDepth.emplace_back(pattern, depth);
2506 minDepth =
std::min(minDepth, depth);
2511 if (patternsByDepth.size() == 1)
2515 std::stable_sort(patternsByDepth.begin(), patternsByDepth.end(),
2516 [](
const std::pair<const Pattern *, unsigned> &lhs,
2517 const std::pair<const Pattern *, unsigned> &rhs) {
2520 if (lhs.second != rhs.second)
2521 return lhs.second < rhs.second;
2524 auto lhsBenefit = lhs.first->getBenefit();
2525 auto rhsBenefit = rhs.first->getBenefit();
2526 return lhsBenefit > rhsBenefit;
2531 for (
auto &patternIt : patternsByDepth)
2532 patterns.push_back(patternIt.first);
2540 enum OpConversionMode {
2563 OpConversionMode mode)
2578 OperationLegalizer opLegalizer;
2581 OpConversionMode mode;
2588 if (failed(opLegalizer.legalize(op, rewriter))) {
2591 if (mode == OpConversionMode::Full)
2593 <<
"failed to legalize operation '" << op->
getName() <<
"'";
2597 if (mode == OpConversionMode::Partial) {
2598 if (opLegalizer.isIllegal(op))
2600 <<
"failed to legalize operation '" << op->
getName()
2601 <<
"' that was explicitly marked illegal";
2605 }
else if (mode == OpConversionMode::Analysis) {
2615 static LogicalResult
2617 UnresolvedMaterializationRewrite *
rewrite) {
2618 UnrealizedConversionCastOp op =
rewrite->getOperation();
2619 assert(!op.use_empty() &&
2620 "expected that dead materializations have already been DCE'd");
2627 switch (
rewrite->getMaterializationKind()) {
2630 assert(op->getNumResults() == 1 &&
"expected single result");
2632 rewriter, op->getLoc(), op.getResultTypes().front(), inputOperands);
2634 newMaterialization.push_back(argMat);
2641 case MaterializationKind::Target:
2643 rewriter, op->getLoc(), op.getResultTypes(), inputOperands,
2646 case MaterializationKind::Source:
2647 assert(op->getNumResults() == 1 &&
"expected single result");
2649 rewriter, op->getLoc(), op.getResultTypes().front(), inputOperands);
2651 newMaterialization.push_back(sourceMat);
2654 if (!newMaterialization.empty()) {
2656 ValueRange newMaterializationRange(newMaterialization);
2657 assert(
TypeRange(newMaterializationRange) == op.getResultTypes() &&
2658 "materialization callback produced value of incorrect type");
2660 rewriter.
replaceOp(op, newMaterialization);
2666 <<
"failed to legalize unresolved materialization "
2668 << inputOperands.
getTypes() <<
") to ("
2669 << op.getResultTypes()
2670 <<
") that remained live after conversion";
2671 diag.attachNote(op->getUsers().begin()->getLoc())
2672 <<
"see existing live user here: " << *op->getUsers().begin();
2683 for (
auto *op : ops) {
2686 toConvert.push_back(op);
2689 auto legalityInfo = target.
isLegal(op);
2690 if (legalityInfo && legalityInfo->isRecursivelyLegal)
2700 for (
auto *op : toConvert)
2701 if (failed(convert(rewriter, op)))
2711 for (
auto it : materializations) {
2714 allCastOps.push_back(it.first);
2726 for (UnrealizedConversionCastOp castOp : remainingCastOps) {
2727 auto it = materializations.find(castOp);
2728 assert(it != materializations.end() &&
"inconsistent state");
2751 auto enqueueOperands = [&](UnrealizedConversionCastOp castOp) {
2752 for (
Value v : castOp.getInputs())
2753 if (
auto inputCastOp = v.getDefiningOp<UnrealizedConversionCastOp>())
2754 worklist.insert(inputCastOp);
2761 [](UnrealizedConversionCastOp castOp) -> UnrealizedConversionCastOp {
2762 if (castOp.getInputs().empty())
2765 castOp.getInputs().front().getDefiningOp<UnrealizedConversionCastOp>();
2768 if (inputCastOp.getOutputs() != castOp.getInputs())
2774 while (!worklist.empty()) {
2775 UnrealizedConversionCastOp castOp = worklist.pop_back_val();
2776 if (castOp->use_empty()) {
2779 enqueueOperands(castOp);
2780 if (remainingCastOps)
2781 erasedOps.insert(castOp.getOperation());
2788 UnrealizedConversionCastOp nextCast = castOp;
2790 if (nextCast.getInputs().getTypes() == castOp.getResultTypes()) {
2794 enqueueOperands(castOp);
2795 castOp.replaceAllUsesWith(nextCast.getInputs());
2796 if (remainingCastOps)
2797 erasedOps.insert(castOp.getOperation());
2801 nextCast = getInputCast(nextCast);
2805 if (remainingCastOps)
2806 for (UnrealizedConversionCastOp op : castOps)
2807 if (!erasedOps.contains(op.getOperation()))
2808 remainingCastOps->push_back(op);
2817 assert(!types.empty() &&
"expected valid types");
2818 remapInput(origInputNo, argTypes.size(), types.size());
2823 assert(!types.empty() &&
2824 "1->0 type remappings don't need to be added explicitly");
2825 argTypes.append(types.begin(), types.end());
2829 unsigned newInputNo,
2830 unsigned newInputCount) {
2831 assert(!remappedInputs[origInputNo] &&
"input has already been remapped");
2832 assert(newInputCount != 0 &&
"expected valid input count");
2833 remappedInputs[origInputNo] =
2834 InputMapping{newInputNo, newInputCount,
nullptr};
2838 Value replacementValue) {
2839 assert(!remappedInputs[origInputNo] &&
"input has already been remapped");
2840 remappedInputs[origInputNo] =
2846 assert(t &&
"expected non-null type");
2849 std::shared_lock<decltype(cacheMutex)> cacheReadLock(cacheMutex,
2852 cacheReadLock.lock();
2853 auto existingIt = cachedDirectConversions.find(t);
2854 if (existingIt != cachedDirectConversions.end()) {
2855 if (existingIt->second)
2856 results.push_back(existingIt->second);
2857 return success(existingIt->second !=
nullptr);
2859 auto multiIt = cachedMultiConversions.find(t);
2860 if (multiIt != cachedMultiConversions.end()) {
2861 results.append(multiIt->second.begin(), multiIt->second.end());
2867 size_t currentCount = results.size();
2869 std::unique_lock<decltype(cacheMutex)> cacheWriteLock(cacheMutex,
2872 for (
const ConversionCallbackFn &converter : llvm::reverse(conversions)) {
2873 if (std::optional<LogicalResult> result = converter(t, results)) {
2875 cacheWriteLock.lock();
2876 if (!succeeded(*result)) {
2877 cachedDirectConversions.try_emplace(t,
nullptr);
2880 auto newTypes =
ArrayRef<Type>(results).drop_front(currentCount);
2881 if (newTypes.size() == 1)
2882 cachedDirectConversions.try_emplace(t, newTypes.front());
2884 cachedMultiConversions.try_emplace(t, llvm::to_vector<2>(newTypes));
2898 return results.size() == 1 ? results.front() :
nullptr;
2904 for (
Type type : types)
2918 return llvm::all_of(*region, [
this](
Block &block) {
2924 return isLegal(llvm::concat<const Type>(ty.getInputs(), ty.getResults()));
2936 if (convertedTypes.empty())
2940 result.
addInputs(inputNo, convertedTypes);
2946 unsigned origInputOffset)
const {
2947 for (
unsigned i = 0, e = types.size(); i != e; ++i)
2957 for (
const MaterializationCallbackFn &fn :
2958 llvm::reverse(argumentMaterializations))
2959 if (
Value result = fn(builder, resultType, inputs, loc))
2967 for (
const MaterializationCallbackFn &fn :
2968 llvm::reverse(sourceMaterializations))
2969 if (
Value result = fn(builder, resultType, inputs, loc))
2977 Type originalType)
const {
2979 builder, loc,
TypeRange(resultType), inputs, originalType);
2982 assert(result.size() == 1 &&
"expected single result");
2983 return result.front();
2988 Type originalType)
const {
2989 for (
const TargetMaterializationCallbackFn &fn :
2990 llvm::reverse(targetMaterializations)) {
2992 fn(builder, resultTypes, inputs, loc, originalType);
2996 "callback produced incorrect number of values or values with "
3003 std::optional<TypeConverter::SignatureConversion>
3007 return std::nullopt;
3030 return impl.getInt() == resultTag;
3034 return impl.getInt() == naTag;
3038 return impl.getInt() == abortTag;
3042 assert(hasResult() &&
"Cannot get result from N/A or abort");
3043 return impl.getPointer();
3046 std::optional<Attribute>
3048 for (
const TypeAttributeConversionCallbackFn &fn :
3049 llvm::reverse(typeAttributeConversions)) {
3054 return std::nullopt;
3056 return std::nullopt;
3066 FunctionType type = dyn_cast<FunctionType>(funcOp.getFunctionType());
3074 failed(typeConverter.
convertTypes(type.getResults(), newResults)) ||
3076 typeConverter, &result)))
3093 FunctionOpInterfaceSignatureConversion(StringRef functionLikeOpName,
3101 FunctionOpInterface funcOp = cast<FunctionOpInterface>(op);
3106 struct AnyFunctionOpInterfaceSignatureConversion
3118 FailureOr<Operation *>
3122 assert(op &&
"Invalid op");
3136 return rewriter.
create(newOp);
3142 patterns.add<FunctionOpInterfaceSignatureConversion>(
3143 functionLikeOpName,
patterns.getContext(), converter);
3148 patterns.add<AnyFunctionOpInterfaceSignatureConversion>(
3158 legalOperations[op].action = action;
3163 for (StringRef dialect : dialectNames)
3164 legalDialects[dialect] = action;
3168 -> std::optional<LegalizationAction> {
3169 std::optional<LegalizationInfo> info = getOpInfo(op);
3170 return info ? info->action : std::optional<LegalizationAction>();
3174 -> std::optional<LegalOpDetails> {
3175 std::optional<LegalizationInfo> info = getOpInfo(op->
getName());
3177 return std::nullopt;
3180 auto isOpLegal = [&] {
3182 if (info->action == LegalizationAction::Dynamic) {
3183 std::optional<bool> result = info->legalityFn(op);
3189 return info->action == LegalizationAction::Legal;
3192 return std::nullopt;
3196 if (info->isRecursivelyLegal) {
3197 auto legalityFnIt = opRecursiveLegalityFns.find(op->
getName());
3198 if (legalityFnIt != opRecursiveLegalityFns.end()) {
3200 legalityFnIt->second(op).value_or(
true);
3205 return legalityDetails;
3209 std::optional<LegalizationInfo> info = getOpInfo(op->
getName());
3213 if (info->action == LegalizationAction::Dynamic) {
3214 std::optional<bool> result = info->legalityFn(op);
3221 return info->action == LegalizationAction::Illegal;
3230 auto chain = [oldCl = std::move(oldCallback), newCl = std::move(newCallback)](
3232 if (std::optional<bool> result = newCl(op))
3240 void ConversionTarget::setLegalityCallback(
3241 OperationName name,
const DynamicLegalityCallbackFn &callback) {
3242 assert(callback &&
"expected valid legality callback");
3243 auto *infoIt = legalOperations.find(name);
3244 assert(infoIt != legalOperations.end() &&
3245 infoIt->second.action == LegalizationAction::Dynamic &&
3246 "expected operation to already be marked as dynamically legal");
3247 infoIt->second.legalityFn =
3253 auto *infoIt = legalOperations.find(name);
3254 assert(infoIt != legalOperations.end() &&
3255 infoIt->second.action != LegalizationAction::Illegal &&
3256 "expected operation to already be marked as legal");
3257 infoIt->second.isRecursivelyLegal =
true;
3260 std::move(opRecursiveLegalityFns[name]), callback);
3262 opRecursiveLegalityFns.erase(name);
3265 void ConversionTarget::setLegalityCallback(
3267 assert(callback &&
"expected valid legality callback");
3268 for (StringRef dialect : dialects)
3270 std::move(dialectLegalityFns[dialect]), callback);
3273 void ConversionTarget::setLegalityCallback(
3274 const DynamicLegalityCallbackFn &callback) {
3275 assert(callback &&
"expected valid legality callback");
3280 -> std::optional<LegalizationInfo> {
3282 const auto *it = legalOperations.find(op);
3283 if (it != legalOperations.end())
3286 auto dialectIt = legalDialects.find(op.getDialectNamespace());
3287 if (dialectIt != legalDialects.end()) {
3288 DynamicLegalityCallbackFn callback;
3289 auto dialectFn = dialectLegalityFns.find(op.getDialectNamespace());
3290 if (dialectFn != dialectLegalityFns.end())
3291 callback = dialectFn->second;
3292 return LegalizationInfo{dialectIt->second,
false,
3296 if (unknownLegalityFn)
3297 return LegalizationInfo{LegalizationAction::Dynamic,
3298 false, unknownLegalityFn};
3299 return std::nullopt;
3302 #if MLIR_ENABLE_PDL_IN_PATTERNMATCH
3308 auto &rewriterImpl =
3314 auto &rewriterImpl =
3321 static FailureOr<SmallVector<Value>>
3326 return std::move(mappedValues);
3330 patterns.getPDLPatterns().registerRewriteFunction(
3335 if (failed(results))
3337 return results->front();
3339 patterns.getPDLPatterns().registerRewriteFunction(
3344 patterns.getPDLPatterns().registerRewriteFunction(
3347 auto &rewriterImpl =
3357 patterns.getPDLPatterns().registerRewriteFunction(
3360 TypeRange types) -> FailureOr<SmallVector<Type>> {
3361 auto &rewriterImpl =
3368 if (failed(converter->
convertTypes(types, remappedTypes)))
3370 return std::move(remappedTypes);
3386 OpConversionMode::Partial);
3404 OpConversionMode::Full);
3427 "expected top-level op to be isolated from above");
3430 "expected ops to have a common ancestor");
3439 for (
Operation *op : ops.drop_front()) {
3443 assert(commonAncestor &&
3444 "expected to find a common isolated from above ancestor");
3448 return commonAncestor;
3455 if (
config.legalizableOps)
3456 assert(
config.legalizableOps->empty() &&
"expected empty set");
3466 inverseOperationMap[it.second] = it.first;
3472 OpConversionMode::Analysis);
3473 LogicalResult status = opConverter.convertOperations(opsToConvert);
3477 if (
config.legalizableOps) {
3480 originalLegalizableOps.insert(inverseOperationMap[op]);
3481 *
config.legalizableOps = std::move(originalLegalizableOps);
3485 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.
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...
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.
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, Value replacement)
Remap an input of the original signature to another replacement value.
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.
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.
Operation * getDefiningOp() const
If this value is the result of an operation, return the operation that defines it.
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.
llvm::PointerUnion< NamedAttribute *, NamedProperty *, NamedTypeConstraint * > Argument
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/ argument materializations t...
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.
SmallVector< Value > unpackNTo1Materialization(Value value)
Unpack an N:1 materialization and return the inputs of the materialization.
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...
Value buildUnresolvedMaterialization(MaterializationKind kind, OpBuilder::InsertPoint ip, Location loc, Value valueToMap, ValueRange inputs, Type outputType, Type originalType, const TypeConverter *converter, UnrealizedConversionCastOp *castOp=nullptr)
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 insertNTo1Materialization(OpBuilder::InsertPoint ip, Location loc, ValueRange replacements, Value originalValue, const TypeConverter *converter)
Build an N:1 materialization for the given original value that was replaced with the given replacemen...
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.
DenseSet< UnrealizedConversionCastOp > nTo1TempMaterializations
A set of all N:1 materializations that were added to work around incomplete 1:N support in the dialec...
ValueRange buildUnresolvedMaterialization(MaterializationKind kind, OpBuilder::InsertPoint ip, Location loc, Value valueToMap, 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.
LogicalResult remapValues(StringRef valueDiagTag, std::optional< Location > inputLoc, PatternRewriter &rewriter, ValueRange values, SmallVector< SmallVector< Value >> &remapped)
Remap the given values to those with potentially different types.
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.