22 genericOp.getDpsInitOperand(
result.getResultNumber());
23 if (!genericOp.payloadUsesValueFromOperand(outputOpOperand))
34 genericOp.getRegionOutputArgs()[
result.getResultNumber()];
46 auto yieldOp = dyn_cast<linalg::YieldOp>(argUserOp);
47 if (!yieldOp || yieldOp != genericOp.getBody()->getTerminator())
51 if (yieldOp.getOperand(
result.getResultNumber()) != outputArg)
69 llvm::SmallDenseMap<unsigned, unsigned> origToNewPos;
70 llvm::SmallDenseMap<std::pair<Value, AffineMap>,
unsigned> dedupedInputs;
71 for (
const auto &en : llvm::enumerate(genericOp.getDpsInputOperands())) {
75 if (!genericOp.payloadUsesValueFromOperand(inputOpOperand)) {
78 droppedOpOperands.push_back(inputOpOperand);
79 if (genericOp.canOpOperandsBeDropped(droppedOpOperands))
81 droppedOpOperands.pop_back();
85 AffineMap indexingMap = genericOp.getMatchingIndexingMap(inputOpOperand);
87 dedupedInputs.find(std::make_pair(inputOpOperand->
get(), indexingMap));
88 if (it != dedupedInputs.end()) {
89 origToNewPos[en.index()] = it->second;
90 droppedOpOperands.push_back(inputOpOperand);
95 origToNewPos[en.index()] = newInputOperands.size();
96 dedupedInputs[{inputOpOperand->
get(), indexingMap}] =
97 newInputOperands.size();
98 newInputOperands.push_back(inputOpOperand->
get());
99 newIndexingMaps.push_back(indexingMap);
112 llvm::SmallDenseMap<unsigned, unsigned> origToNewPos;
113 llvm::SmallDenseMap<std::tuple<Value, AffineMap, Value>,
unsigned>
117 if (!genericOp.hasPureTensorSemantics() || !removeOutputs) {
118 for (
const auto &en : llvm::enumerate(genericOp.getDpsInitsMutable())) {
119 origToNewPos[en.index()] = newOutputOperands.size();
120 newOutputOperands.push_back(en.value().get());
121 newIndexingMaps.push_back(genericOp.getMatchingIndexingMap(&en.value()));
130 auto yieldOp = cast<YieldOp>(genericOp.getBody()->getTerminator());
131 for (
const auto &outputOpOperand :
132 llvm::enumerate(genericOp.getDpsInitsMutable())) {
133 OpResult result = genericOp.getTiedOpResult(&outputOpOperand.value());
135 genericOp.getMatchingIndexingMap(&outputOpOperand.value());
136 auto key = std::make_tuple(outputOpOperand.value().get(), indexingMap,
137 yieldOp->getOperand(outputOpOperand.index()));
143 droppedOpOperands.push_back(&outputOpOperand.value());
144 if (genericOp.canOpOperandsBeDropped(droppedOpOperands)) {
147 droppedOpOperands.pop_back();
150 if (!genericOp.payloadUsesValueFromOperand(&outputOpOperand.value())) {
156 auto it = dedupedOutpts.find(key);
157 if (it != dedupedOutpts.end()) {
158 origToNewPos[outputOpOperand.index()] = it->second;
159 droppedOpOperands.push_back(&outputOpOperand.value());
164 origToNewPos[outputOpOperand.index()] = newOutputOperands.size();
165 dedupedOutpts[key] = newOutputOperands.size();
166 newOutputOperands.push_back(outputOpOperand.value().get());
167 newIndexingMaps.push_back(
168 genericOp.getMatchingIndexingMap(&outputOpOperand.value()));
175 GenericOp genericOp, GenericOp newOp,
176 const llvm::SmallDenseMap<unsigned, unsigned> &origInsToNewInsPos,
177 const llvm::SmallDenseMap<unsigned, unsigned> &origOutsToNewOutsPos,
180 Block *newOpBlock = &newOp.getRegion().front();
181 assert(newOpBlock->
empty() &&
"expected new op to have an empty payload");
182 Block *origOpBlock = &genericOp.getRegion().front();
187 auto updateReplacements =
190 const llvm::SmallDenseMap<unsigned, unsigned> &map) {
191 for (
const auto &origOperand : llvm::enumerate(origOperands)) {
192 auto it = map.find(origOperand.index());
195 OpOperand *newOperand = newOperands[it->second];
196 replacements[origOperand.value()->getOperandNumber()] =
203 updateReplacements(origInputOperands, newInputOperands, origInsToNewInsPos);
206 llvm::to_vector(llvm::make_pointer_range(genericOp.getDpsInitsMutable()));
208 llvm::to_vector(llvm::make_pointer_range(newOp.getDpsInitsMutable()));
209 updateReplacements(origOutputOperands, newOutputOperands,
210 origOutsToNewOutsPos);
213 if (newOp.getNumDpsInits() != genericOp.getNumDpsInits()) {
215 YieldOp origYieldOp = cast<YieldOp>(origOpBlock->
getTerminator());
219 for (
const auto &yieldOpOperands :
220 llvm::enumerate(origYieldOp.getValues())) {
221 auto it = origOutsToNewOutsPos.find(yieldOpOperands.index());
222 if (it == origOutsToNewOutsPos.end())
224 newYieldVals[it->second] = yieldOpOperands.value();
229 rewriter.
mergeBlocks(origOpBlock, newOpBlock, replacements);
232FailureOr<linalg::GenericOp>
234 RewriterBase &rewriter, linalg::GenericOp genericOp,
bool removeOutputs) {
244 llvm::SmallDenseMap<unsigned, unsigned> origInsToNewInsPos =
249 llvm::SmallDenseMap<unsigned, unsigned> origOutsToNewOutsPos =
251 newIndexingMaps, removeOutputs);
254 if (newInputOperands.size() + newOutputOperands.size() ==
255 genericOp->getNumOperands())
261 for (
Value v : newOutputOperands)
262 if (isa<TensorType>(v.getType()))
263 newResultTypes.push_back(v.getType());
264 auto newOp = GenericOp::create(
265 rewriter, loc, newResultTypes, newInputOperands, newOutputOperands,
267 genericOp.getIteratorTypes(), genericOp.getDocAttr(),
268 genericOp.getLibraryCallAttr(),
274 for (
NamedAttribute kv : genericOp->getDiscardableAttrDictionary())
275 if (!llvm::is_contained(odsAttrs, kv.getName().getValue()))
276 newOp->setDiscardableAttr(kv.getName(), kv.getValue());
284 for (
const auto &
result : llvm::enumerate(genericOp.getResults())) {
285 auto it = origOutsToNewOutsPos.find(
result.index());
286 if (it == origOutsToNewOutsPos.end())
288 replacementsVals[
result.index()] = newOp.getResult(it->second);
290 rewriter.
replaceOp(genericOp, replacementsVals);
296struct DeduplicateAndRemoveDeadOperandsAndResults
298 DeduplicateAndRemoveDeadOperandsAndResults(
MLIRContext *ctx,
302 LogicalResult matchAndRewrite(GenericOp genericOp,
305 rewriter, genericOp, removeOutputs);
306 if (failed(newOp) || newOp.value() == genericOp) {
308 genericOp,
"failed to dedup operands/remove dead results");
326 using OpRewritePattern<GenericOp>::OpRewritePattern;
328 LogicalResult matchAndRewrite(GenericOp genericOp,
329 PatternRewriter &rewriter)
const override {
332 if (!genericOp.hasPureTensorSemantics())
335 bool hasRemovedCycles =
false;
337 for (
const auto &outputOpOperand :
338 llvm::enumerate(genericOp.getDpsInits())) {
341 Value
result = genericOp.getResult(outputOpOperand.index());
346 BlockArgument outputArg =
347 genericOp.getRegionOutputArgs()[outputOpOperand.index()];
357 Operation *cycleUserOp = *cycleOp->
user_begin();
358 if (!isa<linalg::YieldOp>(cycleUserOp))
362 if (cycleUserOp->
getOperand(outputOpOperand.index()) !=
370 hasRemovedCycles =
true;
373 if (hasRemovedCycles) {
393 using OpRewritePattern<GenericOp>::OpRewritePattern;
395 LogicalResult matchAndRewrite(GenericOp genericOp,
396 PatternRewriter &rewriter)
const override {
399 for (
int i = 0; i < genericOp.getNumDpsInputs(); ++i) {
401 if (genericOp.getBody()->getArgument(i).getUses().empty())
404 for (
int j = genericOp->getNumOperands() - 1; j > i; --j) {
405 if (genericOp->getOperand(i) == genericOp->getOperand(j) &&
406 genericOp.getIndexingMapsArray()[i] ==
407 genericOp.getIndexingMapsArray()[j]) {
415 if (replacements.empty())
420 for (
auto [before, after] : replacements) {
421 BlockArgument bbArg = genericOp.getBody()->getArgument(before);
422 BlockArgument
replacement = genericOp.getBody()->getArgument(after);
435 patterns.
insert<DeduplicateAndRemoveDeadOperandsAndResults>(
442 patterns.
insert<DeduplicateAndRemoveDeadOperandsAndResults>(
static llvm::SmallDenseMap< unsigned, unsigned > deduplicateOutputOperands(GenericOp genericOp, SmallVector< OpOperand * > &droppedOpOperands, SmallVector< Value > &newOutputOperands, SmallVector< AffineMap > &newIndexingMaps, bool removeOutputs)
static llvm::SmallDenseMap< unsigned, unsigned > deduplicateInputOperands(GenericOp genericOp, SmallVector< OpOperand * > &droppedOpOperands, SmallVector< Value > &newInputOperands, SmallVector< AffineMap > &newIndexingMaps)
static void populateOpPayload(GenericOp genericOp, GenericOp newOp, const llvm::SmallDenseMap< unsigned, unsigned > &origInsToNewInsPos, const llvm::SmallDenseMap< unsigned, unsigned > &origOutsToNewOutsPos, RewriterBase &rewriter)
static bool isResultValueDead(linalg::GenericOp genericOp, OpResult result)
Return true if the result of an operation genericOp is dead.
*if copies could not be generated due to yet unimplemented cases *copyInPlacementStart and copyOutPlacementStart in copyPlacementBlock *specify the insertion points where the incoming copies and outgoing should be the output argument nBegin is set to its * replacement(set to `begin` if no invalidation happens). Since outgoing *copies could have been inserted at `end`
A multi-dimensional affine map Affine map's are immutable like Type's, and they are uniqued.
This class represents an argument of a Block.
Block represents an ordered list of Operations.
BlockArgument getArgument(unsigned i)
unsigned getNumArguments()
Operation * getTerminator()
Get the terminator operation of this block.
ArrayAttr getAffineMapArrayAttr(ArrayRef< AffineMap > values)
IRValueT get() const
Return the current value being used by this operand.
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.
NamedAttribute represents a combination of a name and an Attribute value.
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.
This class represents an operand of an operation.
unsigned getOperandNumber() const
Return which operand this is in the OpOperand list of the Operation.
This is a value defined by a result of an operation.
Operation is the basic unit of execution within MLIR.
bool use_empty()
Returns true if this operation has no uses.
Value getOperand(unsigned idx)
bool hasOneUse()
Returns true if this operation has exactly one use.
OpResult getResult(unsigned idx)
Get the 'idx'th result of this operation.
user_iterator user_begin()
A special type of RewriterBase that coordinates the application of a rewrite pattern on the current I...
RewritePatternSet & insert(ConstructorArg &&arg, ConstructorArgs &&...args)
Add an instance of each of the pattern types 'Ts' to the pattern list with the given arguments.
MLIRContext * getContext() const
This class coordinates the application of a rewrite on a set of IR, providing a way for clients to tr...
virtual void replaceOp(Operation *op, ValueRange newValues)
Replace the results of the given (original) operation with the specified list of values (replacements...
void mergeBlocks(Block *source, Block *dest, ValueRange argValues={})
Inline the operations of block 'source' into the end of block 'dest'.
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,...
void modifyOpInPlace(Operation *root, CallableT &&callable)
This method is a utility wrapper around an in-place modification of an operation.
virtual void replaceAllUsesWith(Value from, Value to)
Find uses of from and replace them with to.
OpTy replaceOpWithNewOp(Operation *op, Args &&...args)
Replace the results of the given (original) op with a new op that is created without verification (re...
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...
user_iterator user_begin() const
bool hasOneUse() const
Returns true if this value has exactly one use.
FailureOr< linalg::GenericOp > deduplicateOperandsAndRemoveDeadResults(RewriterBase &rewriter, linalg::GenericOp genericOp, bool removeOutputs)
Method to deduplicate operands and remove dead results of linalg.generic operations.
void populateEraseUnusedOperandsAndResultsPatterns(RewritePatternSet &patterns)
Pattern to remove dead operands and results of linalg.generic operations.
void populateEraseUnnecessaryInputsPatterns(RewritePatternSet &patterns)
Patterns to promote inputs to outputs and remove unused inputs of linalg.generic ops.
Include the generated interface declarations.
llvm::DenseMap< KeyT, ValueT, KeyInfoT, BucketT > DenseMap
OpRewritePattern is a wrapper around RewritePattern that allows for matching and rewriting against an...