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)...)
63 struct ConversionValueMapping {
68 Value lookupOrDefault(
Value from,
Type desiredType =
nullptr)
const;
78 for (
Value it = newVal; it; it = mapping.lookupOrNull(it))
79 assert(it != oldVal &&
"inserting cyclic mapping");
81 mapping.map(oldVal, newVal);
90 void erase(
Value value) { mapping.erase(value); }
95 for (
auto &it : mapping.getValueMap())
96 inverse[it.second].push_back(it.first);
106 Value ConversionValueMapping::lookupOrDefault(
Value from,
107 Type desiredType)
const {
112 while (
auto mappedValue = mapping.lookupOrNull(from))
120 if (from.
getType() == desiredType)
123 Value mappedValue = mapping.lookupOrNull(from);
130 return desiredValue ? desiredValue : from;
133 Value ConversionValueMapping::lookupOrNull(
Value from,
Type desiredType)
const {
134 Value result = lookupOrDefault(from, desiredType);
135 if (result == from || (desiredType && result.
getType() != desiredType))
140 bool ConversionValueMapping::tryMap(
Value oldVal,
Value newVal) {
141 for (
Value it = newVal; it; it = mapping.lookupOrNull(it))
154 struct RewriterState {
155 RewriterState(
unsigned numRewrites,
unsigned numIgnoredOperations,
156 unsigned numReplacedOps)
157 : numRewrites(numRewrites), numIgnoredOperations(numIgnoredOperations),
158 numReplacedOps(numReplacedOps) {}
161 unsigned numRewrites;
164 unsigned numIgnoredOperations;
167 unsigned numReplacedOps;
199 UnresolvedMaterialization
202 virtual ~IRRewrite() =
default;
205 virtual void rollback() = 0;
224 Kind getKind()
const {
return kind; }
226 static bool classof(
const IRRewrite *
rewrite) {
return true; }
230 : kind(kind), rewriterImpl(rewriterImpl) {}
239 class BlockRewrite :
public IRRewrite {
242 Block *getBlock()
const {
return block; }
244 static bool classof(
const IRRewrite *
rewrite) {
245 return rewrite->getKind() >= Kind::CreateBlock &&
246 rewrite->getKind() <= Kind::ReplaceBlockArg;
252 : IRRewrite(kind, rewriterImpl), block(block) {}
261 class CreateBlockRewrite :
public BlockRewrite {
264 : BlockRewrite(
Kind::CreateBlock, rewriterImpl, block) {}
266 static bool classof(
const IRRewrite *
rewrite) {
267 return rewrite->getKind() == Kind::CreateBlock;
273 listener->notifyBlockInserted(block, {}, {});
276 void rollback()
override {
279 auto &blockOps = block->getOperations();
280 while (!blockOps.empty())
281 blockOps.remove(blockOps.begin());
282 block->dropAllUses();
283 if (block->getParent())
294 class EraseBlockRewrite :
public BlockRewrite {
297 : BlockRewrite(
Kind::EraseBlock, rewriterImpl, block),
298 region(block->getParent()), insertBeforeBlock(block->getNextNode()) {}
300 static bool classof(
const IRRewrite *
rewrite) {
301 return rewrite->getKind() == Kind::EraseBlock;
304 ~EraseBlockRewrite()
override {
306 "rewrite was neither rolled back nor committed/cleaned up");
309 void rollback()
override {
312 assert(block &&
"expected block");
313 auto &blockList = region->getBlocks();
317 blockList.insert(before, block);
323 assert(block &&
"expected block");
324 assert(block->empty() &&
"expected empty block");
328 dyn_cast_or_null<RewriterBase::Listener>(rewriter.
getListener()))
329 listener->notifyBlockErased(block);
334 block->dropAllDefinedValueUses();
345 Block *insertBeforeBlock;
351 class InlineBlockRewrite :
public BlockRewrite {
355 : BlockRewrite(
Kind::InlineBlock, rewriterImpl, block),
356 sourceBlock(sourceBlock),
357 firstInlinedInst(sourceBlock->empty() ? nullptr
358 : &sourceBlock->front()),
359 lastInlinedInst(sourceBlock->empty() ? nullptr : &sourceBlock->back()) {
365 assert(!getConfig().listener &&
366 "InlineBlockRewrite not supported if listener is attached");
369 static bool classof(
const IRRewrite *
rewrite) {
370 return rewrite->getKind() == Kind::InlineBlock;
373 void rollback()
override {
376 if (firstInlinedInst) {
377 assert(lastInlinedInst &&
"expected operation");
397 class MoveBlockRewrite :
public BlockRewrite {
401 : BlockRewrite(
Kind::MoveBlock, rewriterImpl, block), region(region),
402 insertBeforeBlock(insertBeforeBlock) {}
404 static bool classof(
const IRRewrite *
rewrite) {
405 return rewrite->getKind() == Kind::MoveBlock;
413 listener->notifyBlockInserted(block, region,
418 void rollback()
override {
431 Block *insertBeforeBlock;
435 class BlockTypeConversionRewrite :
public BlockRewrite {
440 : BlockRewrite(
Kind::BlockTypeConversion, rewriterImpl, block),
441 origBlock(origBlock), converter(converter) {}
443 static bool classof(
const IRRewrite *
rewrite) {
444 return rewrite->getKind() == Kind::BlockTypeConversion;
455 void rollback()
override;
468 class ReplaceBlockArgRewrite :
public BlockRewrite {
472 : BlockRewrite(
Kind::ReplaceBlockArg, rewriterImpl, block), arg(arg) {}
474 static bool classof(
const IRRewrite *
rewrite) {
475 return rewrite->getKind() == Kind::ReplaceBlockArg;
480 void rollback()
override;
487 class OperationRewrite :
public IRRewrite {
490 Operation *getOperation()
const {
return op; }
492 static bool classof(
const IRRewrite *
rewrite) {
493 return rewrite->getKind() >= Kind::MoveOperation &&
494 rewrite->getKind() <= Kind::UnresolvedMaterialization;
500 : IRRewrite(kind, rewriterImpl), op(op) {}
507 class MoveOperationRewrite :
public OperationRewrite {
511 : OperationRewrite(
Kind::MoveOperation, rewriterImpl, op), block(block),
512 insertBeforeOp(insertBeforeOp) {}
514 static bool classof(
const IRRewrite *
rewrite) {
515 return rewrite->getKind() == Kind::MoveOperation;
523 listener->notifyOperationInserted(
529 void rollback()
override {
547 class ModifyOperationRewrite :
public OperationRewrite {
551 : OperationRewrite(
Kind::ModifyOperation, rewriterImpl, op),
552 name(op->getName()), loc(op->getLoc()), attrs(op->getAttrDictionary()),
553 operands(op->operand_begin(), op->operand_end()),
554 successors(op->successor_begin(), op->successor_end()) {
559 name.initOpProperties(propCopy, prop);
563 static bool classof(
const IRRewrite *
rewrite) {
564 return rewrite->getKind() == Kind::ModifyOperation;
567 ~ModifyOperationRewrite()
override {
568 assert(!propertiesStorage &&
569 "rewrite was neither committed nor rolled back");
575 dyn_cast_or_null<RewriterBase::Listener>(rewriter.
getListener()))
576 listener->notifyOperationModified(op);
578 if (propertiesStorage) {
582 name.destroyOpProperties(propCopy);
583 operator delete(propertiesStorage);
584 propertiesStorage =
nullptr;
588 void rollback()
override {
594 if (propertiesStorage) {
597 name.destroyOpProperties(propCopy);
598 operator delete(propertiesStorage);
599 propertiesStorage =
nullptr;
606 DictionaryAttr attrs;
609 void *propertiesStorage =
nullptr;
616 class ReplaceOperationRewrite :
public OperationRewrite {
621 : OperationRewrite(
Kind::ReplaceOperation, rewriterImpl, op),
622 converter(converter), changedResults(changedResults) {}
624 static bool classof(
const IRRewrite *
rewrite) {
625 return rewrite->getKind() == Kind::ReplaceOperation;
630 void rollback()
override;
634 const TypeConverter *getConverter()
const {
return converter; }
636 bool hasChangedResults()
const {
return changedResults; }
647 class CreateOperationRewrite :
public OperationRewrite {
651 : OperationRewrite(
Kind::CreateOperation, rewriterImpl, op) {}
653 static bool classof(
const IRRewrite *
rewrite) {
654 return rewrite->getKind() == Kind::CreateOperation;
660 listener->notifyOperationInserted(op, {});
663 void rollback()
override;
667 enum MaterializationKind {
684 class UnresolvedMaterializationRewrite :
public OperationRewrite {
686 UnresolvedMaterializationRewrite(
688 UnrealizedConversionCastOp op,
const TypeConverter *converter =
nullptr,
689 MaterializationKind kind = MaterializationKind::Target)
690 : OperationRewrite(
Kind::UnresolvedMaterialization, rewriterImpl, op),
691 converterAndKind(converter, kind) {}
693 static bool classof(
const IRRewrite *
rewrite) {
694 return rewrite->getKind() == Kind::UnresolvedMaterialization;
697 UnrealizedConversionCastOp getOperation()
const {
698 return cast<UnrealizedConversionCastOp>(op);
701 void rollback()
override;
707 return converterAndKind.getPointer();
711 MaterializationKind getMaterializationKind()
const {
712 return converterAndKind.getInt();
718 llvm::PointerIntPair<const TypeConverter *, 2, MaterializationKind>
725 template <
typename RewriteTy,
typename R>
727 return any_of(std::forward<R>(rewrites), [&](
auto &
rewrite) {
728 auto *rewriteTy = dyn_cast<RewriteTy>(
rewrite.get());
729 return rewriteTy && rewriteTy->getOperation() == op;
736 template <
typename RewriteTy,
typename R>
738 RewriteTy *result =
nullptr;
739 for (
auto &
rewrite : rewrites) {
740 auto *rewriteTy = dyn_cast<RewriteTy>(
rewrite.get());
741 if (rewriteTy && rewriteTy->getBlock() == block) {
743 assert(!result &&
"expected single matching rewrite");
761 : context(ctx), config(config) {}
768 RewriterState getCurrentState();
772 void applyRewrites();
775 void resetState(RewriterState state);
779 template <
typename RewriteTy,
typename... Args>
782 std::make_unique<RewriteTy>(*
this, std::forward<Args>(args)...));
787 void undoRewrites(
unsigned numRewritesToKeep = 0);
793 LogicalResult remapValues(StringRef valueDiagTag,
794 std::optional<Location> inputLoc,
821 Block *applySignatureConversion(
831 Value buildUnresolvedMaterialization(MaterializationKind kind,
846 void notifyOperationInserted(
Operation *op,
853 void notifyBlockIsBeingErased(
Block *block);
856 void notifyBlockInserted(
Block *block,
Region *previous,
860 void notifyBlockBeingInlined(
Block *block,
Block *srcBlock,
882 if (erased.contains(op))
890 if (erased.contains(block))
892 assert(block->
empty() &&
"expected empty block");
948 llvm::ScopedPrinter logger{llvm::dbgs()};
955 return rewriterImpl.
config;
958 void BlockTypeConversionRewrite::commit(
RewriterBase &rewriter) {
963 dyn_cast_or_null<RewriterBase::Listener>(rewriter.
getListener()))
965 listener->notifyOperationModified(op);
968 void BlockTypeConversionRewrite::rollback() {
972 LogicalResult BlockTypeConversionRewrite::materializeLiveConversions(
978 OpBuilder builder(it.value().getContext(), &rewriterImpl);
979 builder.setInsertionPointToStart(block);
983 if (rewriterImpl.
mapping.lookupOrNull(origArg, origArg.
getType()))
985 Operation *liveUser = findLiveUser(origArg);
989 Value replacementValue = rewriterImpl.
mapping.lookupOrDefault(origArg);
990 assert(replacementValue &&
"replacement value not found");
993 builder.setInsertionPointAfterValue(replacementValue);
994 newArg = converter->materializeSourceConversion(
995 builder, origArg.
getLoc(), origArg.
getType(), replacementValue);
997 "materialization hook did not provide a value of the expected "
1003 <<
"failed to materialize conversion for block argument #"
1004 << it.index() <<
" that remained live after conversion, type was "
1007 <<
"see existing live user here: " << *liveUser;
1010 rewriterImpl.
mapping.map(origArg, newArg);
1015 void ReplaceBlockArgRewrite::commit(
RewriterBase &rewriter) {
1016 Value repl = rewriterImpl.
mapping.lookupOrNull(arg, arg.getType());
1020 if (isa<BlockArgument>(repl)) {
1028 Operation *replOp = cast<OpResult>(repl).getOwner();
1036 void ReplaceBlockArgRewrite::rollback() { rewriterImpl.
mapping.erase(arg); }
1038 void ReplaceOperationRewrite::commit(
RewriterBase &rewriter) {
1040 dyn_cast_or_null<RewriterBase::Listener>(rewriter.
getListener());
1045 return rewriterImpl.mapping.lookupOrNull(result, result.getType());
1050 listener->notifyOperationReplaced(op, replacements);
1053 for (
auto [result, newValue] :
1054 llvm::zip_equal(op->
getResults(), replacements))
1060 if (getConfig().unlegalizedOps)
1061 getConfig().unlegalizedOps->erase(op);
1067 [&](
Operation *op) { listener->notifyOperationErased(op); });
1075 void ReplaceOperationRewrite::rollback() {
1077 rewriterImpl.
mapping.erase(result);
1080 void ReplaceOperationRewrite::cleanup(
RewriterBase &rewriter) {
1084 void CreateOperationRewrite::rollback() {
1086 while (!region.getBlocks().empty())
1087 region.getBlocks().remove(region.getBlocks().begin());
1093 void UnresolvedMaterializationRewrite::rollback() {
1094 if (getMaterializationKind() == MaterializationKind::Target) {
1096 rewriterImpl.
mapping.erase(input);
1101 void UnresolvedMaterializationRewrite::cleanup(
RewriterBase &rewriter) {
1114 rewrite->cleanup(eraseRewriter);
1129 while (
ignoredOps.size() != state.numIgnoredOperations)
1132 while (
replacedOps.size() != state.numReplacedOps)
1138 llvm::reverse(llvm::drop_begin(
rewrites, numRewritesToKeep)))
1140 rewrites.resize(numRewritesToKeep);
1144 StringRef valueDiagTag, std::optional<Location> inputLoc,
1147 remapped.reserve(llvm::size(values));
1151 Value operand = it.value();
1163 diag <<
"unable to convert type for " << valueDiagTag <<
" #"
1164 << it.index() <<
", type was " << origType;
1170 if (legalTypes.size() == 1)
1171 desiredType = legalTypes.front();
1180 Value newOperand =
mapping.lookupOrDefault(operand, desiredType);
1190 newOperand = castValue;
1192 remapped.push_back(newOperand);
1215 if (region->
empty())
1220 llvm::make_early_inc_range(llvm::drop_begin(*region, 1))) {
1222 std::optional<TypeConverter::SignatureConversion> conversion =
1232 if (entryConversion)
1235 std::optional<TypeConverter::SignatureConversion> conversion =
1258 for (
unsigned i = 0; i < origArgCount; ++i) {
1260 if (!inputMap || inputMap->replacementValue)
1263 for (
unsigned j = 0;
j < inputMap->size; ++
j)
1264 newLocs[inputMap->inputNo +
j] = origLoc;
1271 convertedTypes, newLocs);
1281 appendRewrite<InlineBlockRewrite>(newBlock, block, newBlock->
end());
1284 while (!block->
empty())
1291 for (
unsigned i = 0; i != origArgCount; ++i) {
1295 std::optional<TypeConverter::SignatureConversion::InputMapping> inputMap =
1301 MaterializationKind::Source, newBlock, newBlock->
begin(),
1303 origArgType, converter);
1305 appendRewrite<ReplaceBlockArgRewrite>(block, origArg);
1309 if (
Value repl = inputMap->replacementValue) {
1311 assert(inputMap->size == 0 &&
1312 "invalid to provide a replacement value when the argument isn't "
1315 appendRewrite<ReplaceBlockArgRewrite>(block, origArg);
1324 newBlock->
getArguments().slice(inputMap->inputNo, inputMap->size);
1327 origArg.
getLoc(), replArgs, origArgType, converter);
1329 appendRewrite<ReplaceBlockArgRewrite>(block, origArg);
1337 Type legalOutputType;
1339 legalOutputType = converter->
convertType(origArgType);
1340 if (legalOutputType && legalOutputType != origArgType) {
1342 origArg.
getLoc(), argMat, legalOutputType, converter);
1343 mapping.map(argMat, targetMat);
1345 appendRewrite<ReplaceBlockArgRewrite>(block, origArg);
1348 appendRewrite<BlockTypeConversionRewrite>(newBlock, block, converter);
1368 if (inputs.size() == 1 && inputs.front().
getType() == outputType)
1369 return inputs.front();
1376 builder.
create<UnrealizedConversionCastOp>(loc, outputType, inputs);
1377 appendRewrite<UnresolvedMaterializationRewrite>(convertOp, converter, kind);
1378 return convertOp.getResult(0);
1385 if (
OpResult inputRes = dyn_cast<OpResult>(input))
1386 insertPt = ++inputRes.getOwner()->getIterator();
1389 insertBlock, insertPt, loc, input,
1390 outputType, converter);
1399 logger.startLine() <<
"** Insert : '" << op->
getName() <<
"'(" << op
1403 "attempting to insert into a block within a replaced/erased op");
1405 if (!previous.
isSet()) {
1407 appendRewrite<CreateOperationRewrite>(op);
1413 appendRewrite<MoveOperationRewrite>(op, previous.
getBlock(), prevOp);
1419 assert(!
ignoredOps.contains(op) &&
"operation was already replaced");
1422 bool resultChanged =
false;
1425 for (
auto [newValue, result] : llvm::zip(newValues, op->
getResults())) {
1427 resultChanged =
true;
1431 mapping.map(result, newValue);
1432 resultChanged |= (newValue.getType() != result.
getType());
1443 appendRewrite<EraseBlockRewrite>(block);
1449 "attempting to insert into a region within a replaced/erased op");
1454 logger.startLine() <<
"** Insert Block into : '" << parent->
getName()
1455 <<
"'(" << parent <<
")\n";
1458 <<
"** Insert Block into detached Region (nullptr parent op)'";
1464 appendRewrite<CreateBlockRewrite>(block);
1467 Block *prevBlock = previousIt == previous->
end() ? nullptr : &*previousIt;
1468 appendRewrite<MoveBlockRewrite>(block, previous, prevBlock);
1473 appendRewrite<InlineBlockRewrite>(block, srcBlock, before);
1480 reasonCallback(
diag);
1481 logger.startLine() <<
"** Failure : " <<
diag.str() <<
"\n";
1491 ConversionPatternRewriter::ConversionPatternRewriter(
1495 setListener(
impl.get());
1501 assert(op && newOp &&
"expected non-null op");
1507 "incorrect # of replacement values");
1509 impl->logger.startLine()
1510 <<
"** Replace : '" << op->
getName() <<
"'(" << op <<
")\n";
1512 impl->notifyOpReplaced(op, newValues);
1517 impl->logger.startLine()
1518 <<
"** Erase : '" << op->
getName() <<
"'(" << op <<
")\n";
1521 impl->notifyOpReplaced(op, nullRepls);
1526 "attempting to erase a block within a replaced/erased op");
1536 impl->notifyBlockIsBeingErased(block);
1544 "attempting to apply a signature conversion to a block within a "
1545 "replaced/erased op");
1546 return impl->applySignatureConversion(*
this, block, converter, conversion);
1553 "attempting to apply a signature conversion to a block within a "
1554 "replaced/erased op");
1555 return impl->convertRegionTypes(*
this, region, converter, entryConversion);
1562 impl->logger.startLine() <<
"** Replace Argument : '" << from
1563 <<
"'(in region of '" << parentOp->
getName()
1566 impl->appendRewrite<ReplaceBlockArgRewrite>(from.
getOwner(), from);
1567 impl->mapping.map(
impl->mapping.lookupOrDefault(from), to);
1572 if (failed(
impl->remapValues(
"value", std::nullopt, *
this, key,
1575 return remappedValues.front();
1583 return impl->remapValues(
"value", std::nullopt, *
this, keys,
1592 "incorrect # of argument replacement values");
1594 "attempting to inline a block from a replaced/erased op");
1596 "attempting to inline a block into a replaced/erased op");
1597 auto opIgnored = [&](
Operation *op) {
return impl->isOpIgnored(op); };
1600 assert(llvm::all_of(source->
getUsers(), opIgnored) &&
1601 "expected 'source' to have no predecessors");
1610 bool fastPath = !
impl->config.listener;
1613 impl->notifyBlockBeingInlined(dest, source, before);
1616 for (
auto it : llvm::zip(source->
getArguments(), argValues))
1617 replaceUsesOfBlockArgument(std::get<0>(it), std::get<1>(it));
1624 while (!source->
empty())
1625 moveOpBefore(&source->
front(), dest, before);
1633 assert(!
impl->wasOpReplaced(op) &&
1634 "attempting to modify a replaced/erased op");
1636 impl->pendingRootUpdates.insert(op);
1638 impl->appendRewrite<ModifyOperationRewrite>(op);
1642 assert(!
impl->wasOpReplaced(op) &&
1643 "attempting to modify a replaced/erased op");
1648 assert(
impl->pendingRootUpdates.erase(op) &&
1649 "operation did not have a pending in-place update");
1655 assert(
impl->pendingRootUpdates.erase(op) &&
1656 "operation did not have a pending in-place update");
1659 auto it = llvm::find_if(
1660 llvm::reverse(
impl->rewrites), [&](std::unique_ptr<IRRewrite> &
rewrite) {
1661 auto *modifyRewrite = dyn_cast<ModifyOperationRewrite>(rewrite.get());
1662 return modifyRewrite && modifyRewrite->getOperation() == op;
1664 assert(it !=
impl->rewrites.rend() &&
"no root update started on op");
1666 int updateIdx = std::prev(
impl->rewrites.rend()) - it;
1667 impl->rewrites.erase(
impl->rewrites.begin() + updateIdx);
1682 auto &rewriterImpl = dialectRewriter.getImpl();
1686 getTypeConverter());
1694 return matchAndRewrite(op, operands, dialectRewriter);
1706 class OperationLegalizer {
1726 LogicalResult legalizeWithFold(
Operation *op,
1731 LogicalResult legalizeWithPattern(
Operation *op,
1742 RewriterState &curState);
1746 legalizePatternBlockRewrites(
Operation *op,
1749 RewriterState &state, RewriterState &newState);
1750 LogicalResult legalizePatternCreatedOperations(
1752 RewriterState &state, RewriterState &newState);
1755 RewriterState &state,
1756 RewriterState &newState);
1766 void buildLegalizationGraph(
1767 LegalizationPatterns &anyOpLegalizerPatterns,
1778 void computeLegalizationGraphBenefit(
1779 LegalizationPatterns &anyOpLegalizerPatterns,
1784 unsigned computeOpLegalizationDepth(
1791 unsigned applyCostModelToPatterns(
1792 LegalizationPatterns &patterns,
1813 : target(targetInfo), applicator(patterns), config(config) {
1817 LegalizationPatterns anyOpLegalizerPatterns;
1819 buildLegalizationGraph(anyOpLegalizerPatterns, legalizerPatterns);
1820 computeLegalizationGraphBenefit(anyOpLegalizerPatterns, legalizerPatterns);
1823 bool OperationLegalizer::isIllegal(
Operation *op)
const {
1824 return target.isIllegal(op);
1828 OperationLegalizer::legalize(
Operation *op,
1831 const char *logLineComment =
1832 "//===-------------------------------------------===//\n";
1837 logger.getOStream() <<
"\n";
1838 logger.startLine() << logLineComment;
1839 logger.startLine() <<
"Legalizing operation : '" << op->
getName() <<
"'("
1845 op->print(logger.startLine(), OpPrintingFlags().printGenericOpForm());
1846 logger.getOStream() <<
"\n\n";
1851 if (
auto legalityInfo = target.isLegal(op)) {
1854 logger,
"operation marked legal by the target{0}",
1855 legalityInfo->isRecursivelyLegal
1856 ?
"; NOTE: operation is recursively legal; skipping internals"
1858 logger.startLine() << logLineComment;
1863 if (legalityInfo->isRecursivelyLegal) {
1876 logSuccess(logger,
"operation marked 'ignored' during conversion");
1877 logger.startLine() << logLineComment;
1885 if (succeeded(legalizeWithFold(op, rewriter))) {
1888 logger.startLine() << logLineComment;
1894 if (succeeded(legalizeWithPattern(op, rewriter))) {
1897 logger.startLine() << logLineComment;
1903 logFailure(logger,
"no matched legalization pattern");
1904 logger.startLine() << logLineComment;
1910 OperationLegalizer::legalizeWithFold(
Operation *op,
1912 auto &rewriterImpl = rewriter.
getImpl();
1916 rewriterImpl.
logger.startLine() <<
"* Fold {\n";
1917 rewriterImpl.
logger.indent();
1923 if (failed(rewriter.
tryFold(op, replacementValues))) {
1929 if (replacementValues.empty())
1930 return legalize(op, rewriter);
1933 rewriter.
replaceOp(op, replacementValues);
1936 for (
unsigned i = curState.numRewrites, e = rewriterImpl.
rewrites.size();
1939 dyn_cast<CreateOperationRewrite>(rewriterImpl.
rewrites[i].get());
1942 if (failed(legalize(createOp->getOperation(), rewriter))) {
1944 "failed to legalize generated constant '{0}'",
1945 createOp->getOperation()->getName()));
1956 OperationLegalizer::legalizeWithPattern(
Operation *op,
1958 auto &rewriterImpl = rewriter.
getImpl();
1961 auto canApply = [&](
const Pattern &pattern) {
1962 bool canApply = canApplyPattern(op, pattern, rewriter);
1963 if (canApply && config.listener)
1964 config.listener->notifyPatternBegin(pattern, op);
1970 auto onFailure = [&](
const Pattern &pattern) {
1976 diag <<
"Failed to apply pattern \"" << pattern.getDebugName()
1982 if (config.listener)
1983 config.listener->notifyPatternEnd(pattern, failure());
1985 appliedPatterns.erase(&pattern);
1990 auto onSuccess = [&](
const Pattern &pattern) {
1992 auto result = legalizePatternResult(op, pattern, rewriter, curState);
1993 appliedPatterns.erase(&pattern);
1996 if (config.listener)
1997 config.listener->notifyPatternEnd(pattern, result);
2002 return applicator.matchAndRewrite(op, rewriter, canApply, onFailure,
2006 bool OperationLegalizer::canApplyPattern(
Operation *op,
const Pattern &pattern,
2009 auto &os = rewriter.
getImpl().logger;
2010 os.getOStream() <<
"\n";
2011 os.startLine() <<
"* Pattern : '" << op->
getName() <<
" -> (";
2013 os.getOStream() <<
")' {\n";
2020 !appliedPatterns.insert(&pattern).second) {
2029 OperationLegalizer::legalizePatternResult(
Operation *op,
const Pattern &pattern,
2031 RewriterState &curState) {
2035 assert(
impl.pendingRootUpdates.empty() &&
"dangling root updates");
2037 auto newRewrites = llvm::drop_begin(
impl.rewrites, curState.numRewrites);
2038 auto replacedRoot = [&] {
2039 return hasRewrite<ReplaceOperationRewrite>(newRewrites, op);
2041 auto updatedRootInPlace = [&] {
2042 return hasRewrite<ModifyOperationRewrite>(newRewrites, op);
2044 assert((replacedRoot() || updatedRootInPlace()) &&
2045 "expected pattern to replace the root operation");
2049 RewriterState newState =
impl.getCurrentState();
2050 if (failed(legalizePatternBlockRewrites(op, rewriter,
impl, curState,
2052 failed(legalizePatternRootUpdates(rewriter,
impl, curState, newState)) ||
2053 failed(legalizePatternCreatedOperations(rewriter,
impl, curState,
2058 LLVM_DEBUG(
logSuccess(
impl.logger,
"pattern applied successfully"));
2062 LogicalResult OperationLegalizer::legalizePatternBlockRewrites(
2065 RewriterState &newState) {
2070 for (
int i = state.numRewrites, e = newState.numRewrites; i != e; ++i) {
2071 BlockRewrite *
rewrite = dyn_cast<BlockRewrite>(
impl.rewrites[i].get());
2075 if (isa<BlockTypeConversionRewrite, EraseBlockRewrite,
2076 ReplaceBlockArgRewrite>(
rewrite))
2085 if (
auto *converter =
impl.regionToConverter.lookup(block->
getParent())) {
2086 std::optional<TypeConverter::SignatureConversion> conversion =
2087 converter->convertBlockSignature(block);
2089 LLVM_DEBUG(
logFailure(
impl.logger,
"failed to convert types of moved "
2093 impl.applySignatureConversion(rewriter, block, converter, *conversion);
2101 if (operationsToIgnore.empty()) {
2102 for (
unsigned i = state.numRewrites, e =
impl.rewrites.size(); i != e;
2105 dyn_cast<CreateOperationRewrite>(
impl.rewrites[i].get());
2108 operationsToIgnore.insert(createOp->getOperation());
2113 if (operationsToIgnore.insert(parentOp).second &&
2114 failed(legalize(parentOp, rewriter))) {
2116 "operation '{0}'({1}) became illegal after rewrite",
2117 parentOp->
getName(), parentOp));
2124 LogicalResult OperationLegalizer::legalizePatternCreatedOperations(
2126 RewriterState &state, RewriterState &newState) {
2127 for (
int i = state.numRewrites, e = newState.numRewrites; i != e; ++i) {
2128 auto *createOp = dyn_cast<CreateOperationRewrite>(
impl.rewrites[i].get());
2131 Operation *op = createOp->getOperation();
2132 if (failed(legalize(op, rewriter))) {
2134 "failed to legalize generated operation '{0}'({1})",
2142 LogicalResult OperationLegalizer::legalizePatternRootUpdates(
2144 RewriterState &state, RewriterState &newState) {
2145 for (
int i = state.numRewrites, e = newState.numRewrites; i != e; ++i) {
2146 auto *
rewrite = dyn_cast<ModifyOperationRewrite>(
impl.rewrites[i].get());
2150 if (failed(legalize(op, rewriter))) {
2152 impl.logger,
"failed to legalize operation updated in-place '{0}'",
2163 void OperationLegalizer::buildLegalizationGraph(
2164 LegalizationPatterns &anyOpLegalizerPatterns,
2175 applicator.walkAllPatterns([&](
const Pattern &pattern) {
2176 std::optional<OperationName> root = pattern.
getRootKind();
2182 anyOpLegalizerPatterns.push_back(&pattern);
2187 if (target.getOpAction(*root) == LegalizationAction::Legal)
2192 invalidPatterns[*root].insert(&pattern);
2194 parentOps[op].insert(*root);
2197 patternWorklist.insert(&pattern);
2205 if (!anyOpLegalizerPatterns.empty()) {
2206 for (
const Pattern *pattern : patternWorklist)
2207 legalizerPatterns[*pattern->
getRootKind()].push_back(pattern);
2211 while (!patternWorklist.empty()) {
2212 auto *pattern = patternWorklist.pop_back_val();
2216 std::optional<LegalizationAction> action = target.getOpAction(op);
2217 return !legalizerPatterns.count(op) &&
2218 (!action || action == LegalizationAction::Illegal);
2224 legalizerPatterns[*pattern->
getRootKind()].push_back(pattern);
2225 invalidPatterns[*pattern->
getRootKind()].erase(pattern);
2229 for (
auto op : parentOps[*pattern->
getRootKind()])
2230 patternWorklist.set_union(invalidPatterns[op]);
2234 void OperationLegalizer::computeLegalizationGraphBenefit(
2235 LegalizationPatterns &anyOpLegalizerPatterns,
2241 for (
auto &opIt : legalizerPatterns)
2242 if (!minOpPatternDepth.count(opIt.first))
2243 computeOpLegalizationDepth(opIt.first, minOpPatternDepth,
2249 if (!anyOpLegalizerPatterns.empty())
2250 applyCostModelToPatterns(anyOpLegalizerPatterns, minOpPatternDepth,
2256 applicator.applyCostModel([&](
const Pattern &pattern) {
2258 if (std::optional<OperationName> rootName = pattern.
getRootKind())
2259 orderedPatternList = legalizerPatterns[*rootName];
2261 orderedPatternList = anyOpLegalizerPatterns;
2264 auto *it = llvm::find(orderedPatternList, &pattern);
2265 if (it == orderedPatternList.end())
2269 return PatternBenefit(std::distance(it, orderedPatternList.end()));
2273 unsigned OperationLegalizer::computeOpLegalizationDepth(
2277 auto depthIt = minOpPatternDepth.find(op);
2278 if (depthIt != minOpPatternDepth.end())
2279 return depthIt->second;
2283 auto opPatternsIt = legalizerPatterns.find(op);
2284 if (opPatternsIt == legalizerPatterns.end() || opPatternsIt->second.empty())
2293 unsigned minDepth = applyCostModelToPatterns(
2294 opPatternsIt->second, minOpPatternDepth, legalizerPatterns);
2295 minOpPatternDepth[op] = minDepth;
2299 unsigned OperationLegalizer::applyCostModelToPatterns(
2300 LegalizationPatterns &patterns,
2307 patternsByDepth.reserve(patterns.size());
2308 for (
const Pattern *pattern : patterns) {
2311 unsigned generatedOpDepth = computeOpLegalizationDepth(
2312 generatedOp, minOpPatternDepth, legalizerPatterns);
2313 depth =
std::max(depth, generatedOpDepth + 1);
2315 patternsByDepth.emplace_back(pattern, depth);
2318 minDepth =
std::min(minDepth, depth);
2323 if (patternsByDepth.size() == 1)
2327 std::stable_sort(patternsByDepth.begin(), patternsByDepth.end(),
2328 [](
const std::pair<const Pattern *, unsigned> &lhs,
2329 const std::pair<const Pattern *, unsigned> &rhs) {
2332 if (lhs.second != rhs.second)
2333 return lhs.second < rhs.second;
2336 auto lhsBenefit = lhs.first->getBenefit();
2337 auto rhsBenefit = rhs.first->getBenefit();
2338 return lhsBenefit > rhsBenefit;
2343 for (
auto &patternIt : patternsByDepth)
2344 patterns.push_back(patternIt.first);
2352 enum OpConversionMode {
2375 OpConversionMode mode)
2376 : config(config), opLegalizer(target, patterns, this->config),
2396 LogicalResult legalizeUnresolvedMaterializations(
2408 LogicalResult legalizeChangedResultType(
2418 OperationLegalizer opLegalizer;
2421 OpConversionMode mode;
2428 if (failed(opLegalizer.legalize(op, rewriter))) {
2431 if (mode == OpConversionMode::Full)
2433 <<
"failed to legalize operation '" << op->
getName() <<
"'";
2437 if (mode == OpConversionMode::Partial) {
2438 if (opLegalizer.isIllegal(op))
2440 <<
"failed to legalize operation '" << op->
getName()
2441 <<
"' that was explicitly marked illegal";
2445 }
else if (mode == OpConversionMode::Analysis) {
2462 for (
auto *op : ops) {
2465 toConvert.push_back(op);
2468 auto legalityInfo = target.
isLegal(op);
2469 if (legalityInfo && legalityInfo->isRecursivelyLegal)
2479 for (
auto *op : toConvert)
2480 if (failed(convert(rewriter, op)))
2486 if (failed(finalize(rewriter)))
2491 if (mode == OpConversionMode::Analysis) {
2501 std::optional<DenseMap<Value, SmallVector<Value>>> inverseMapping;
2503 if (failed(legalizeUnresolvedMaterializations(rewriter, rewriterImpl,
2505 failed(legalizeConvertedArgumentTypes(rewriter, rewriterImpl)))
2509 for (
unsigned i = 0; i < rewriterImpl.
rewrites.size(); ++i) {
2510 auto *opReplacement =
2511 dyn_cast<ReplaceOperationRewrite>(rewriterImpl.
rewrites[i].get());
2512 if (!opReplacement || !opReplacement->hasChangedResults())
2514 Operation *op = opReplacement->getOperation();
2516 Value newValue = rewriterImpl.
mapping.lookupOrNull(result);
2521 if (failed(legalizeErasedResult(op, result, rewriterImpl)))
2531 if (!inverseMapping)
2532 inverseMapping = rewriterImpl.
mapping.getInverse();
2536 if (failed(legalizeChangedResultType(
2537 op, result, newValue, opReplacement->getConverter(), rewriter,
2538 rewriterImpl, *inverseMapping)))
2545 LogicalResult OperationConverter::legalizeConvertedArgumentTypes(
2550 auto findLiveUser = [&](
Value val) {
2551 auto liveUserIt = llvm::find_if_not(val.getUsers(), [&](
Operation *user) {
2552 return rewriterImpl.isOpIgnored(user);
2554 return liveUserIt == val.user_end() ? nullptr : *liveUserIt;
2557 for (int64_t i = 0; i < static_cast<int64_t>(rewriterImpl.
rewrites.size());
2560 if (
auto *blockTypeConversionRewrite =
2561 dyn_cast<BlockTypeConversionRewrite>(
rewrite.get()))
2562 if (failed(blockTypeConversionRewrite->materializeLiveConversions(
2578 for (
auto [matResult, newValue] : llvm::zip(matResults, values)) {
2579 auto inverseMapIt = inverseMapping.find(matResult);
2580 if (inverseMapIt == inverseMapping.end())
2589 for (
Value inverseMapVal : inverseMapIt->second)
2590 if (!rewriterImpl.
mapping.tryMap(inverseMapVal, newValue))
2591 rewriterImpl.
mapping.erase(inverseMapVal);
2599 &materializationOps,
2607 auto isLive = [&](
Value value) {
2609 auto matIt = materializationOps.find(user);
2610 if (matIt != materializationOps.end())
2611 return !necessaryMaterializations.count(matIt->second);
2617 worklist.push_back(value);
2618 while (!worklist.empty()) {
2619 Value next = worklist.pop_back_val();
2623 llvm::append_range(worklist, inverseMapping.lookup(next));
2632 Value remappedValue = rewriterImpl.
mapping.lookupOrDefault(value, type);
2633 if (remappedValue.
getType() == type && remappedValue != invalidRoot)
2634 return remappedValue;
2639 auto inputCastOp = value.
getDefiningOp<UnrealizedConversionCastOp>();
2640 if (inputCastOp && inputCastOp->getNumOperands() == 1)
2641 return lookupRemappedValue(invalidRoot, inputCastOp->getOperand(0),
2649 auto *mat = dyn_cast<UnresolvedMaterializationRewrite>(
rewrite.get());
2652 materializationOps.try_emplace(mat->getOperation(), mat);
2653 worklist.insert(mat);
2655 while (!worklist.empty()) {
2656 UnresolvedMaterializationRewrite *mat = worklist.pop_back_val();
2657 UnrealizedConversionCastOp op = mat->getOperation();
2660 assert(op->
getNumResults() == 1 &&
"unexpected materialization type");
2668 auto castOp = dyn_cast<UnrealizedConversionCastOp>(user);
2671 if (castOp->getResultTypes() == inputOperands.
getTypes()) {
2674 necessaryMaterializations.remove(materializationOps.lookup(user));
2680 if (inputOperands.size() == 1) {
2683 Value remappedValue =
2684 lookupRemappedValue(opResult, inputOperands[0], outputType);
2685 if (remappedValue && remappedValue != opResult) {
2688 necessaryMaterializations.remove(mat);
2697 bool isMaterializationLive = isLive(opResult);
2698 if (!isMaterializationLive)
2700 if (!necessaryMaterializations.insert(mat))
2704 for (
Value input : inputOperands) {
2705 if (
auto parentOp = input.getDefiningOp<UnrealizedConversionCastOp>()) {
2706 if (
auto *mat = materializationOps.lookup(parentOp))
2707 worklist.insert(mat);
2716 UnresolvedMaterializationRewrite &mat,
2718 &materializationOps,
2722 auto findLiveUser = [&](
auto &&users) {
2723 auto liveUserIt = llvm::find_if_not(
2725 return liveUserIt == users.end() ? nullptr : *liveUserIt;
2732 Value remappedValue = rewriterImpl.
mapping.lookupOrDefault(value, type);
2733 if (remappedValue.
getType() == type)
2734 return remappedValue;
2738 UnrealizedConversionCastOp op = mat.getOperation();
2750 auto valueCast = value.
getDefiningOp<UnrealizedConversionCastOp>();
2754 auto matIt = materializationOps.find(valueCast);
2755 if (matIt != materializationOps.end())
2757 *matIt->second, materializationOps, rewriter, rewriterImpl,
2765 if (inputOperands.size() == 1) {
2768 Value remappedValue = lookupRemappedValue(inputOperands[0], outputType);
2769 if (remappedValue && remappedValue != opResult) {
2781 Value newMaterialization;
2782 switch (mat.getMaterializationKind()) {
2785 newMaterialization = converter->materializeArgumentConversion(
2786 rewriter, op->
getLoc(), outputType, inputOperands);
2787 if (newMaterialization)
2792 case MaterializationKind::Target:
2793 newMaterialization = converter->materializeTargetConversion(
2794 rewriter, op->
getLoc(), outputType, inputOperands);
2796 case MaterializationKind::Source:
2797 newMaterialization = converter->materializeSourceConversion(
2798 rewriter, op->
getLoc(), outputType, inputOperands);
2801 if (newMaterialization) {
2802 assert(newMaterialization.
getType() == outputType &&
2803 "materialization callback produced value of incorrect type");
2811 <<
"failed to legalize unresolved materialization "
2813 << inputOperands.
getTypes() <<
") to " << outputType
2814 <<
" that remained live after conversion";
2817 <<
"see existing live user here: " << *liveUser;
2822 LogicalResult OperationConverter::legalizeUnresolvedMaterializations(
2826 inverseMapping = rewriterImpl.
mapping.getInverse();
2833 *inverseMapping, necessaryMaterializations);
2836 for (
auto *mat : necessaryMaterializations) {
2838 *mat, materializationOps, rewriter, rewriterImpl, *inverseMapping)))
2844 LogicalResult OperationConverter::legalizeErasedResult(
2850 return rewriterImpl.isOpIgnored(user);
2852 if (liveUserIt != result.
user_end()) {
2854 << op->
getName() <<
"' marked as erased";
2855 diag.attachNote(liveUserIt->getLoc())
2869 while (!worklist.empty()) {
2870 Value value = worklist.pop_back_val();
2875 return rewriterImpl.isOpIgnored(user);
2877 if (liveUserIt != value.
user_end())
2879 auto mapIt = inverseMapping.find(value);
2880 if (mapIt != inverseMapping.end())
2881 worklist.append(mapIt->second);
2886 LogicalResult OperationConverter::legalizeChangedResultType(
2897 auto emitConversionError = [&] {
2899 <<
"failed to materialize conversion for result #"
2902 <<
"' that remained live after conversion";
2904 <<
"see existing live user here: " << *liveUser;
2911 return emitConversionError();
2916 rewriter, op->
getLoc(), resultType, newValue);
2917 if (!convertedValue)
2918 return emitConversionError();
2920 rewriterImpl.
mapping.map(result, convertedValue);
2930 assert(!types.empty() &&
"expected valid types");
2931 remapInput(origInputNo, argTypes.size(), types.size());
2936 assert(!types.empty() &&
2937 "1->0 type remappings don't need to be added explicitly");
2938 argTypes.append(types.begin(), types.end());
2942 unsigned newInputNo,
2943 unsigned newInputCount) {
2944 assert(!remappedInputs[origInputNo] &&
"input has already been remapped");
2945 assert(newInputCount != 0 &&
"expected valid input count");
2946 remappedInputs[origInputNo] =
2947 InputMapping{newInputNo, newInputCount,
nullptr};
2951 Value replacementValue) {
2952 assert(!remappedInputs[origInputNo] &&
"input has already been remapped");
2953 remappedInputs[origInputNo] =
2960 std::shared_lock<decltype(cacheMutex)> cacheReadLock(cacheMutex,
2963 cacheReadLock.lock();
2964 auto existingIt = cachedDirectConversions.find(t);
2965 if (existingIt != cachedDirectConversions.end()) {
2966 if (existingIt->second)
2967 results.push_back(existingIt->second);
2968 return success(existingIt->second !=
nullptr);
2970 auto multiIt = cachedMultiConversions.find(t);
2971 if (multiIt != cachedMultiConversions.end()) {
2972 results.append(multiIt->second.begin(), multiIt->second.end());
2978 size_t currentCount = results.size();
2980 std::unique_lock<decltype(cacheMutex)> cacheWriteLock(cacheMutex,
2983 for (
const ConversionCallbackFn &converter : llvm::reverse(conversions)) {
2984 if (std::optional<LogicalResult> result = converter(t, results)) {
2986 cacheWriteLock.lock();
2987 if (!succeeded(*result)) {
2988 cachedDirectConversions.try_emplace(t,
nullptr);
2991 auto newTypes =
ArrayRef<Type>(results).drop_front(currentCount);
2992 if (newTypes.size() == 1)
2993 cachedDirectConversions.try_emplace(t, newTypes.front());
2995 cachedMultiConversions.try_emplace(t, llvm::to_vector<2>(newTypes));
3009 return results.size() == 1 ? results.front() :
nullptr;
3015 for (
Type type : types)
3029 return llvm::all_of(*region, [
this](
Block &block) {
3035 return isLegal(llvm::concat<const Type>(ty.getInputs(), ty.getResults()));
3047 if (convertedTypes.empty())
3051 result.
addInputs(inputNo, convertedTypes);
3057 unsigned origInputOffset)
const {
3058 for (
unsigned i = 0, e = types.size(); i != e; ++i)
3064 Value TypeConverter::materializeConversion(
3067 for (
const MaterializationCallbackFn &fn : llvm::reverse(materializations))
3068 if (std::optional<Value> result = fn(builder, resultType, inputs, loc))
3073 std::optional<TypeConverter::SignatureConversion>
3077 return std::nullopt;
3100 return impl.getInt() == resultTag;
3104 return impl.getInt() == naTag;
3108 return impl.getInt() == abortTag;
3112 assert(hasResult() &&
"Cannot get result from N/A or abort");
3113 return impl.getPointer();
3116 std::optional<Attribute>
3118 for (
const TypeAttributeConversionCallbackFn &fn :
3119 llvm::reverse(typeAttributeConversions)) {
3124 return std::nullopt;
3126 return std::nullopt;
3136 FunctionType type = dyn_cast<FunctionType>(funcOp.getFunctionType());
3144 failed(typeConverter.
convertTypes(type.getResults(), newResults)) ||
3146 typeConverter, &result)))
3163 FunctionOpInterfaceSignatureConversion(StringRef functionLikeOpName,
3171 FunctionOpInterface funcOp = cast<FunctionOpInterface>(op);
3176 struct AnyFunctionOpInterfaceSignatureConversion
3188 FailureOr<Operation *>
3192 assert(op &&
"Invalid op");
3206 return rewriter.
create(newOp);
3212 patterns.
add<FunctionOpInterfaceSignatureConversion>(
3213 functionLikeOpName, patterns.
getContext(), converter);
3218 patterns.
add<AnyFunctionOpInterfaceSignatureConversion>(
3228 legalOperations[op].action = action;
3233 for (StringRef dialect : dialectNames)
3234 legalDialects[dialect] = action;
3238 -> std::optional<LegalizationAction> {
3239 std::optional<LegalizationInfo> info = getOpInfo(op);
3240 return info ? info->action : std::optional<LegalizationAction>();
3244 -> std::optional<LegalOpDetails> {
3245 std::optional<LegalizationInfo> info = getOpInfo(op->
getName());
3247 return std::nullopt;
3250 auto isOpLegal = [&] {
3252 if (info->action == LegalizationAction::Dynamic) {
3253 std::optional<bool> result = info->legalityFn(op);
3259 return info->action == LegalizationAction::Legal;
3262 return std::nullopt;
3266 if (info->isRecursivelyLegal) {
3267 auto legalityFnIt = opRecursiveLegalityFns.find(op->
getName());
3268 if (legalityFnIt != opRecursiveLegalityFns.end()) {
3270 legalityFnIt->second(op).value_or(
true);
3275 return legalityDetails;
3279 std::optional<LegalizationInfo> info = getOpInfo(op->
getName());
3283 if (info->action == LegalizationAction::Dynamic) {
3284 std::optional<bool> result = info->legalityFn(op);
3291 return info->action == LegalizationAction::Illegal;
3300 auto chain = [oldCl = std::move(oldCallback), newCl = std::move(newCallback)](
3302 if (std::optional<bool> result = newCl(op))
3310 void ConversionTarget::setLegalityCallback(
3311 OperationName name,
const DynamicLegalityCallbackFn &callback) {
3312 assert(callback &&
"expected valid legality callback");
3313 auto *infoIt = legalOperations.find(name);
3314 assert(infoIt != legalOperations.end() &&
3315 infoIt->second.action == LegalizationAction::Dynamic &&
3316 "expected operation to already be marked as dynamically legal");
3317 infoIt->second.legalityFn =
3323 auto *infoIt = legalOperations.find(name);
3324 assert(infoIt != legalOperations.end() &&
3325 infoIt->second.action != LegalizationAction::Illegal &&
3326 "expected operation to already be marked as legal");
3327 infoIt->second.isRecursivelyLegal =
true;
3330 std::move(opRecursiveLegalityFns[name]), callback);
3332 opRecursiveLegalityFns.erase(name);
3335 void ConversionTarget::setLegalityCallback(
3337 assert(callback &&
"expected valid legality callback");
3338 for (StringRef dialect : dialects)
3340 std::move(dialectLegalityFns[dialect]), callback);
3343 void ConversionTarget::setLegalityCallback(
3344 const DynamicLegalityCallbackFn &callback) {
3345 assert(callback &&
"expected valid legality callback");
3350 -> std::optional<LegalizationInfo> {
3352 const auto *it = legalOperations.find(op);
3353 if (it != legalOperations.end())
3356 auto dialectIt = legalDialects.find(op.getDialectNamespace());
3357 if (dialectIt != legalDialects.end()) {
3358 DynamicLegalityCallbackFn callback;
3359 auto dialectFn = dialectLegalityFns.find(op.getDialectNamespace());
3360 if (dialectFn != dialectLegalityFns.end())
3361 callback = dialectFn->second;
3362 return LegalizationInfo{dialectIt->second,
false,
3366 if (unknownLegalityFn)
3367 return LegalizationInfo{LegalizationAction::Dynamic,
3368 false, unknownLegalityFn};
3369 return std::nullopt;
3372 #if MLIR_ENABLE_PDL_IN_PATTERNMATCH
3378 auto &rewriterImpl =
3384 auto &rewriterImpl =
3391 static FailureOr<SmallVector<Value>>
3396 return std::move(mappedValues);
3405 if (failed(results))
3407 return results->front();
3417 auto &rewriterImpl =
3421 if (
Type newType = converter->convertType(type))
3430 TypeRange types) -> FailureOr<SmallVector<Type>> {
3431 auto &rewriterImpl =
3438 if (failed(converter->
convertTypes(types, remappedTypes)))
3440 return std::move(remappedTypes);
3456 OpConversionMode::Partial);
3474 OpConversionMode::Full);
3491 OpConversionMode::Analysis);
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 legalizeUnresolvedMaterialization(UnresolvedMaterializationRewrite &mat, DenseMap< Operation *, UnresolvedMaterializationRewrite * > &materializationOps, ConversionPatternRewriter &rewriter, ConversionPatternRewriterImpl &rewriterImpl, DenseMap< Value, SmallVector< Value >> &inverseMapping)
Legalize the given unresolved materialization.
static RewriteTy * findSingleRewrite(R &&rewrites, Block *block)
Find the single rewrite object of the specified type and block among the given rewrites.
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 Operation * findLiveUserOfReplaced(Value initialValue, ConversionPatternRewriterImpl &rewriterImpl, const DenseMap< Value, SmallVector< Value >> &inverseMapping)
Finds a user of the given value, or of any other value that the given value replaced,...
static void logSuccess(llvm::ScopedPrinter &os, StringRef fmt, Args &&...args)
A utility function to log a successful result for the given reason.
static void replaceMaterialization(ConversionPatternRewriterImpl &rewriterImpl, ResultRange matResults, ValueRange values, DenseMap< Value, SmallVector< Value >> &inverseMapping)
Replace the results of a materialization operation with the given values.
static void computeNecessaryMaterializations(DenseMap< Operation *, UnresolvedMaterializationRewrite * > &materializationOps, ConversionPatternRewriter &rewriter, ConversionPatternRewriterImpl &rewriterImpl, DenseMap< Value, SmallVector< Value >> &inverseMapping, SetVector< UnresolvedMaterializationRewrite * > &necessaryMaterializations)
Compute all of the unresolved materializations that will persist beyond the conversion process,...
static bool hasRewrite(R &&rewrites, Operation *op)
Return "true" if there is an operation rewrite that matches the specified rewrite type and operation ...
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
PatternRewriter hook for replacing an operation.
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.
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.
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.
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.
unsigned getResultNumber() const
Returns the number of this result.
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.
OpResult getOpResult(unsigned idx)
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...
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.
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)
void setOperands(ValueRange operands)
Replace the current operands of this operation with the ones provided in 'operands'.
user_range getUsers()
Returns a range of all users.
result_range getResults()
int getPropertiesStorageSize() const
Returns the properties storage size.
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 implements the result iterators for the Operation class.
std::enable_if_t<!std::is_convertible< ValuesT, Operation * >::value > replaceAllUsesWith(ValuesT &&values)
Replace all uses of results of this range with the provided 'values'.
MLIRContext * getContext() const
PDLPatternModule & getPDLPatterns()
Return the PDL patterns held in this list.
RewritePatternSet & add(ConstructorArg &&arg, ConstructorArgs &&...args)
Add an instance of each of the pattern types 'Ts' to the pattern list with the given arguments.
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.
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...
bool isLegal(Type type) const
Return true if the given type is legal for this type converter, i.e.
Value materializeSourceConversion(OpBuilder &builder, Location loc, Type resultType, ValueRange inputs) const
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.
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.
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.
user_iterator user_end() const
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)
InFlightDiagnostic emitError(Location loc)
Utility method to emit an error message using this location.
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.
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.
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.
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.
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.
DenseSet< void * > erased
Pointers to all erased operations and blocks.
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).
Value buildUnresolvedMaterialization(MaterializationKind kind, Block *insertBlock, Block::iterator insertPt, Location loc, ValueRange inputs, Type outputType, const TypeConverter *converter)
Build an unresolved materialization operation given an output type and set of input operands.
void notifyOperationInserted(Operation *op, OpBuilder::InsertPoint previous) override
Notify the listener that the specified operation was inserted.
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.
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.
void notifyOpReplaced(Operation *op, ValueRange newValues)
Notifies that an op is about to be replaced with the given values.
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.
LogicalResult remapValues(StringRef valueDiagTag, std::optional< Location > inputLoc, PatternRewriter &rewriter, ValueRange values, SmallVectorImpl< Value > &remapped)
Remap the given values to those with potentially different types.
void notifyMatchFailure(Location loc, function_ref< void(Diagnostic &)> reasonCallback) override
Notifies that a pattern match failed for the given reason.
MLIRContext * context
MLIR context.
bool isOpIgnored(Operation *op) const
Return "true" if the given operation is ignored, and does not need to be converted.
SetVector< Operation * > ignoredOps
A set of operations that should no longer be considered for legalization.
SmallVector< std::unique_ptr< IRRewrite > > rewrites
Ordered list of block operations (creations, splits, motions).
const ConversionConfig & config
Dialect conversion configuration.
SetVector< Operation * > replacedOps
A set of operations that were replaced/erased.
void notifyBlockIsBeingErased(Block *block)
Notifies that a block is about to be erased.
Value buildUnresolvedTargetMaterialization(Location loc, Value input, Type outputType, const TypeConverter *converter)
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.