34template <
typename SubClass,
typename SourceOp>
36 using OpRewritePattern<SourceOp>::OpRewritePattern;
37 using OpAdaptor =
typename SourceOp::Adaptor;
39 LogicalResult matchAndRewrite(SourceOp op,
40 PatternRewriter &rewriter)
const override {
41 Location loc = op.getLoc();
43 for (Value in : op->getOperands())
45 stt && !stt->isIdentity() &&
46 stt->getEncoding().getDimToLvl().getNumSymbols() != 0)
51 SmallVector<Value> deMappedIns(op->getOperands());
52 for (Value &in : deMappedIns) {
55 ReinterpretMapOp::create(rewriter, loc, stt->getDemappedType(), in);
61 OpAdaptor adaptor(deMappedIns, op);
62 LogicalResult status =
63 static_cast<const SubClass *
>(
this)->rewriteOp(op, adaptor, rewriter);
64 return changed ?
success() : status;
70 explicit AffineDimCollector(
unsigned dimNum) : dims(dimNum) {};
71 void visitDimExpr(AffineDimExpr expr) { dims.set(expr.
getPosition()); }
76struct AffineExprAdmissibleVisitor
78 explicit AffineExprAdmissibleVisitor(
bool isOutput) : isOutput(isOutput) {};
81 void visitAddExpr(AffineBinaryOpExpr expr) {
85 void visitMulExpr(AffineBinaryOpExpr expr) {
91 void visitModExpr(AffineBinaryOpExpr expr) { admissible =
false; }
92 void visitFloorDivExpr(AffineBinaryOpExpr expr) { admissible =
false; }
93 void visitCeilDivExpr(AffineBinaryOpExpr expr) { admissible =
false; }
94 operator bool() {
return admissible; }
97 bool admissible =
true;
104using InadmissInfo = std::pair<BitVector, BitVector>;
116 AffineDimCollector collector(map.
getNumDims());
117 for (
unsigned lvl = 0, e = map.
getNumResults(); lvl < e; lvl++) {
118 AffineExprAdmissibleVisitor admissible(isOutput);
119 admissible.walkPostOrder(map.
getResult(lvl));
124 collector.walkPostOrder(map.
getResult(lvl));
127 ret.second = collector.dims;
162 auto [inAdLvls, usedDims] = info;
170 assert(lvl2Idx.getNumResults() <= idxMap.
getNumDims());
171 if (lvl2Idx.getNumResults() != idxMap.
getNumDims()) {
178 AffineDimCollector usedInLvl(idxMap.
getNumDims());
180 usedInLvl.walkPostOrder(e);
182 unsigned curUsedDimID = 0;
183 unsigned curUnusedDimID = lvl2Idx.getNumDims();
185 BitVector unused = usedInLvl.dims.flip();
186 for (
unsigned i = 0; i < idxMap.
getNumDims(); i++) {
190 results.push_back(lvl2Idx.getResult(curUsedDimID++));
193 AffineMap::get(lvl2Idx.getNumDims() + unused.count(), 0, results, ctx);
195 assert(lvl2Idx.getNumResults() == idxMap.
getNumDims());
202 unsigned curRepID = 0;
203 unsigned curOriID = inAdLvls.count();
208 for (
unsigned l : inAdLvls.set_bits()) {
218 AffineDimCollector collector(idxMap.
getNumDims());
219 collector.walkPostOrder(lvlExp);
221 assert(collector.dims.count() == 1);
222 transItTps.push_back(itTps[collector.dims.find_first()]);
225 for (
unsigned d = 0, e = idxMap.
getNumDims(); d < e; d++) {
226 if (usedDims.test(d)) {
230 results.push_back(lvl2Idx.getResult(d).replaceDims(dimRep));
236 transItTps.push_back(itTps[d]);
239 unsigned numDim = idxMap.
getNumDims() - usedDims.count() + inAdLvls.count();
241 itTps.assign(transItTps.begin(), transItTps.end());
249static std::optional<std::pair<ArrayAttr, ArrayAttr>>
255 for (
unsigned i = 0, e = idxMapArray.size(); i < e; i++) {
258 if (stt && !stt->isIdentity()) {
261 idxMapArray[i] = dim2Lvl.
compose(idxMapArray[i]);
269 if (ShapedType::isStatic(lvlSz)) {
277 cstMapping.try_emplace(divExp, c0);
281 cstMapping.try_emplace(modExp, lvlExp);
285 unsigned boundedNum = 0;
290 for (
OpOperand &operand : op->getOpOperands()) {
293 if (!stt || !stt->getEncoding())
296 unsigned tid = operand.getOperandNumber();
297 bool isOutput = &operand == op.getDpsInitOperand(0);
300 auto [inAdLvls, dimExprs] = inAdInfo;
301 for (
unsigned d : dimExprs.set_bits()) {
309 if (inAdLvls.count() != 0) {
314 unsigned position = 0;
315 for (
unsigned lvl : inAdLvls.set_bits()) {
317 populateCstMapping(cstMapping, position, lvlSz);
324 for (
unsigned tid = 0, e = idxMapArray.size(); tid < e; tid++) {
325 AffineMap transMap = idxMapArray[tid].compose(lvl2Idx);
326 idxMapArray[tid] = transMap.
replace(
331 boundedNum += inAdLvls.count();
337 llvm::map_to_vector(itTps, [ctx](
auto itTp) ->
Attribute {
338 return linalg::IteratorTypeAttr::get(ctx, itTp);
348 return ReinterpretMapOp::create(builder, val.
getLoc(), enc.withoutDimToLvl(),
355 return ReinterpretMapOp::create(builder, val.
getLoc(), enc, val);
361 assert(outs.size() == types.size());
362 for (
auto [r, t] : llvm::zip(ret, types))
363 if (r.getType() != t)
364 r = ReinterpretMapOp::create(rewriter, r.getLoc(), t, r);
375struct GenericOpReinterpretMap
376 :
public DemapInsRewriter<GenericOpReinterpretMap, linalg::GenericOp> {
378 using DemapInsRewriter::DemapInsRewriter;
379 LogicalResult rewriteOp(linalg::GenericOp linalgOp, OpAdaptor adaptor,
380 PatternRewriter &rewriter)
const {
383 if (linalgOp.getNumDpsInits() != 1 || !linalgOp.hasPureTensorSemantics() ||
392 linalgOp,
"the sparse kernel can not be sparsified.");
395 Value res = linalgOp.getResult(0);
397 auto [idxMap, itTp] = *transMap;
400 linalgOp.setIndexingMapsAttr(idxMap);
401 linalgOp.setIteratorTypesAttr(itTp);
403 linalgOp.getInputsMutable().assign(adaptor.getInputs());
404 linalgOp.getDpsInitsMutable().assign(adaptor.getOutputs());
405 res.
setType(adaptor.getOutputs()[0].getType());
409 if (stt && stt->hasEncoding()) {
410 Value t =
genRemap(rewriter, stt->getEncoding(), res);
418 GenericOpScheduler(MLIRContext *context,
420 : OpRewritePattern<linalg::GenericOp>(context), strategy(strategy) {}
422 LogicalResult matchAndRewrite(linalg::GenericOp linalgOp,
423 PatternRewriter &rewriter)
const override {
424 if (linalgOp.getNumDpsInits() != 1 || !linalgOp.hasPureTensorSemantics() ||
430 const StringRef sorted =
"sorted";
431 if (linalgOp->hasDiscardableAttr(sorted))
436 bool isAdmissible =
false;
442 const auto allMasks = {SortMask::kIncludeAll, SortMask::kIncludeDense,
443 SortMask::kIncludeDenseInput,
444 SortMask::kIncludeDenseOutput,
445 SortMask::kSparseOnly};
446 for (
const SortMask mask : allMasks) {
447 order = scheduler.sort(mask);
449 if (isAdmissibleOrder(linalgOp, order)) {
459 if (
failed(resolveCycle(scheduler, linalgOp, rewriter))) {
461 linalgOp,
"the sparse kernel can not be scheduled: loop detected.");
468 linalgOp,
"the sparse kernel can not be scheduled.");
473 linalgOp->setDiscardableAttr(sorted, rewriter.
getBoolAttr(
true));
482 ArrayAttr preItTypes = linalgOp.getIteratorTypesAttr();
483 SmallVector<Attribute> curItTypes;
484 curItTypes.reserve(preItTypes.size());
486 unsigned loopID = llvm::cast<AffineDimExpr>(expr).getPosition();
487 curItTypes.push_back(preItTypes[loopID]);
492 SmallVector<AffineMap> idxMaps = linalgOp.getIndexingMapsArray();
493 for (AffineMap &idxMap : idxMaps)
494 idxMap = idxMap.compose(order);
498 linalgOp.setIteratorTypesAttr(rewriter.
getArrayAttr(curItTypes));
506 static bool isAdmissibleOrder(linalg::GenericOp linalgOp, AffineMap order) {
510 OpOperand *
lhs = linalgOp.getDpsInitOperand(0);
512 const auto iteratorTypes = linalgOp.getIteratorTypesArray();
513 for (
const AffineExpr l : order.
getResults()) {
514 unsigned loopId = llvm::cast<AffineDimExpr>(l).getPosition();
516 cast<linalg::IteratorTypeAttr>(linalgOp.getIteratorTypes()[loopId]);
524 return static_cast<int64_t
>(nest) >= linalgOp.getRank(
lhs) - 1;
528 static LogicalResult resolveCycle(IterationGraphSorter &scheduler,
529 linalg::LinalgOp linalgOp,
530 PatternRewriter &rewriter) {
533 for (OpOperand *t : linalgOp.getDpsInputOperands()) {
534 Value tval = t->get();
538 AffineMap idxMap = linalgOp.getMatchingIndexingMap(t);
539 bool hasCompExpr = llvm::any_of(idxMap.
getResults(), [](AffineExpr exp) {
540 return !llvm::isa<AffineDimExpr>(exp);
542 if (!srcEnc || hasCompExpr)
546 AffineMap order = scheduler.
sort(SortMask::kSparseOnly, tval);
554 assert(stt.isIdentity());
557 idxMap = idxMap.
compose(order);
566 SmallVector<std::pair<unsigned, unsigned>> lvlSeq;
568 unsigned lvl = llvm::cast<AffineDimExpr>(expr).getPosition();
569 lvlSeq.push_back(std::make_pair(lvl, lvlSeq.size()));
571 llvm::sort(lvlSeq, llvm::less_first());
572 SmallVector<unsigned> perm =
573 llvm::to_vector(llvm::make_second_range(lvlSeq));
576 assert(!dimToLvl.isIdentity());
580 RankedTensorType dstTp = stt.withDimToLvl(dimToLvl).getRankedTensorType();
581 Value dst = ConvertOp::create(rewriter, tval.
getLoc(), dstTp, tval);
583 linalgOp->setOperand(t->getOperandNumber(), dst);
589 bufferization::DeallocTensorOp::create(rewriter, dst.
getLoc(), dst);
606template <
typename AllocOp>
608 using OpRewritePattern<AllocOp>::OpRewritePattern;
609 LogicalResult matchAndRewrite(AllocOp op,
610 PatternRewriter &rewriter)
const override {
614 Location loc = op.getLoc();
616 if (stt.getEncoding().getDimToLvl().getNumSymbols() != 0)
619 if constexpr (std::is_same_v<AllocOp, bufferization::AllocTensorOp>) {
625 if (Value
copy = op.getCopy()) {
626 Value demappedCopy =
genDemap(rewriter, stt.getEncoding(),
copy);
627 auto allocOp = AllocOp::create(rewriter, loc, stt.getDemappedType(),
629 Value t =
genRemap(rewriter, stt.getEncoding(), allocOp.getResult());
635 SmallVector<Value> maxDimCrds;
636 maxDimCrds.reserve(stt.getDimRank());
638 for (int64_t dimSz : stt.getDimShape()) {
639 if (ShapedType::isDynamic(dimSz)) {
640 Value maxCrd = arith::SubIOp::create(rewriter, loc, dynSz.front(),
642 maxDimCrds.push_back(maxCrd);
643 dynSz = dynSz.drop_front();
645 maxDimCrds.push_back(
constantIndex(rewriter, loc, dimSz - 1));
649 ValueRange maxLvlCrds = stt.translateCrds(rewriter, loc, maxDimCrds,
650 CrdTransDirectionKind::dim2lvl);
651 auto lvlShape = stt.getLvlShape();
652 SmallVector<Value> dynLvlSzs;
653 for (
unsigned i = 0, e = lvlShape.size(); i < e; i++) {
654 if (ShapedType::isDynamic(lvlShape[i])) {
655 Value sz = arith::AddIOp::create(rewriter, loc, maxLvlCrds[i],
657 dynLvlSzs.push_back(sz);
661 assert(dynSz.empty());
665 AllocOp::create(rewriter, loc, stt.getDemappedType(), dynLvlSzs);
667 Value t =
genRemap(rewriter, stt.getEncoding(), allocOp.getResult());
673struct TensorInsertDemapper
674 :
public DemapInsRewriter<TensorInsertDemapper, tensor::InsertOp> {
675 using DemapInsRewriter::DemapInsRewriter;
676 LogicalResult rewriteOp(tensor::InsertOp op, OpAdaptor adaptor,
677 PatternRewriter &rewriter)
const {
681 Location loc = op.getLoc();
683 ValueRange lvlCrd = stt.translateCrds(rewriter, loc, op.getIndices(),
684 CrdTransDirectionKind::dim2lvl);
685 auto insertOp = tensor::InsertOp::create(rewriter, loc, op.getScalar(),
686 adaptor.getDest(), lvlCrd);
688 Value out =
genRemap(rewriter, stt.getEncoding(), insertOp.getResult());
696 LogicalResult matchAndRewrite(AssembleOp op,
697 PatternRewriter &rewriter)
const override {
703 if (stt.getEncoding().getDimToLvl().getNumSymbols() != 0)
706 op, [&op, &stt]() { op.getResult().setType(stt.getDemappedType()); });
708 Value out =
genRemap(rewriter, stt.getEncoding(), op.getResult());
714struct SparseDisassembleDemapper
715 :
public DemapInsRewriter<SparseDisassembleDemapper, DisassembleOp> {
716 using DemapInsRewriter::DemapInsRewriter;
717 LogicalResult rewriteOp(DisassembleOp op, OpAdaptor adaptor,
718 PatternRewriter &rewriter)
const {
724 op.getTensorMutable().assign(adaptor.getTensor());
730struct ForeachOpDemapper
731 :
public DemapInsRewriter<ForeachOpDemapper, ForeachOp> {
732 using DemapInsRewriter::DemapInsRewriter;
733 LogicalResult rewriteOp(ForeachOp op, OpAdaptor adaptor,
734 PatternRewriter &rewriter)
const {
741 if (
auto constOp = op.getTensor().getDefiningOp<arith::ConstantOp>())
742 if (
auto attr = dyn_cast<SparseElementsAttr>(constOp.getValue()))
745 Location loc = op.getLoc();
748 SmallVector<Type> prevRetTps(op.getResultTypes());
751 op.getTensorMutable().assign(adaptor.getTensor());
752 op.getInitArgsMutable().assign(adaptor.getInitArgs());
754 for (
auto r : op.getResults())
756 r.setType(stt->getDemappedType());
760 SmallVector<Type> blockArgTps(lvlRank, rewriter.
getIndexType());
761 blockArgTps.push_back(srcStt.getElementType());
762 blockArgTps.append(adaptor.getInitArgs().getTypes().begin(),
763 adaptor.getInitArgs().getTypes().end());
764 Block *body = op.getBody();
767 for (Type t : blockArgTps)
774 ValueRange dimCrds = srcStt.translateCrds(rewriter, loc, lvlCrds,
775 CrdTransDirectionKind::lvl2dim);
777 body->
getArguments().take_front(srcStt.getDimRank()), dimCrds);
780 unsigned numInitArgs = op.getInitArgs().size();
788 SmallVector<Value> reMappedArgs =
795 if (numInitArgs != 0) {
799 stt && !stt->isIdentity()) {
801 genDemap(rewriter, stt->getEncoding(), yield.getSingleResult());
802 YieldOp::create(rewriter, loc, y);
809 SmallVector<Value> outs =
814 for (
auto [from, to] : llvm::zip(op.getResults(), outs))
828 patterns.
add<GenericOpReinterpretMap>(patterns.
getContext());
829 patterns.
add<GenericOpScheduler>(patterns.
getContext(), strategy);
833 patterns.
add<TensorAllocDemapper<bufferization::AllocTensorOp>,
834 TensorAllocDemapper<tensor::EmptyOp>, SparseAssembleDemapper,
835 SparseDisassembleDemapper, TensorInsertDemapper,
static void copy(Location loc, Value dst, Value src, Value size, OpBuilder &builder)
Copies the given number of bytes from src to dst pointers.
static Value genDemap(OpBuilder &builder, SparseTensorEncodingAttr enc, Value val)
static SmallVector< Value > remapValueRange(OpBuilder &rewriter, TypeRange types, ValueRange outs)
static AffineMap genReplaceDimToLvlMap(const InadmissInfo &info, AffineMap idxMap, SmallVector< utils::IteratorType > &itTps)
static std::optional< std::pair< ArrayAttr, ArrayAttr > > translateMap(linalg::GenericOp op, PatternRewriter &rewriter)
static Value genRemap(OpBuilder &builder, SparseTensorEncodingAttr enc, Value val)
static InadmissInfo collectInadmissInfo(AffineMap map, bool isOutput)
unsigned getPosition() const
See documentation for AffineExprVisitorBase.
Base type for affine expression.
A multi-dimensional affine map Affine map's are immutable like Type's, and they are uniqued.
MLIRContext * getContext() const
static AffineMap get(MLIRContext *context)
Returns a zero result affine map with no dimensions or symbols: () -> ().
unsigned getNumDims() const
ArrayRef< AffineExpr > getResults() const
unsigned getNumResults() const
AffineExpr getResult(unsigned idx) const
AffineMap replace(AffineExpr expr, AffineExpr replacement, unsigned numResultDims, unsigned numResultSyms) const
Sparse replace method.
static AffineMap getPermutationMap(ArrayRef< unsigned > permutation, MLIRContext *context)
Returns an AffineMap representing a permutation.
AffineMap compose(AffineMap map) const
Returns the AffineMap resulting from composing this with map.
bool isIdentity() const
Returns true if this affine map is an identity affine map.
bool isPermutation() const
Returns true if the AffineMap represents a symbol-less permutation map.
Attributes are known-constant values of operations.
BlockArgument getArgument(unsigned i)
unsigned getNumArguments()
Operation * getTerminator()
Get the terminator operation of this block.
BlockArgument addArgument(Type type, Location loc)
Add one value to the argument list.
void eraseArguments(unsigned start, unsigned num)
Erases 'num' arguments from the index 'start'.
BlockArgListType getArguments()
void eraseArgument(unsigned index)
Erase the argument at 'index' and remove it from the argument list.
BoolAttr getBoolAttr(bool value)
ArrayAttr getArrayAttr(ArrayRef< Attribute > value)
ArrayAttr getAffineMapArrayAttr(ArrayRef< AffineMap > values)
MLIRContext is the top-level object for a collection of MLIR operations.
This class helps build Operations.
void setInsertionPointToStart(Block *block)
Sets the insertion point to the start of the specified block.
void setInsertionPoint(Block *block, Block::iterator insertPoint)
Set the insertion point to the specified location.
void setInsertionPointToEnd(Block *block)
Sets the insertion point to the end of the specified block.
void setInsertionPointAfter(Operation *op)
Sets the insertion point to the node after the specified operation, which will cause subsequent inser...
This class represents an operand of an operation.
A special type of RewriterBase that coordinates the application of a rewrite pattern on the current I...
MLIRContext * getContext() const
RewritePatternSet & add(ConstructorArg &&arg, ConstructorArgs &&...args)
Add an instance of each of the pattern types 'Ts' to the pattern list with the given arguments.
virtual void replaceOp(Operation *op, ValueRange newValues)
Replace the results of the given (original) operation with the specified list of values (replacements...
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 replaceAllUsesExcept(Value from, Value to, Operation *exceptedUser)
Find uses of from and replace them with to except if the user is exceptedUser.
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.
virtual void startOpModification(Operation *op)
This method is used to notify the rewriter that an in-place operation modification is about to happen...
This class provides an abstraction over the various different ranges of value types.
This class provides an abstraction over the different types of ranges over Values.
type_range getTypes() const
This class represents an instance of an SSA value in the MLIR system, representing a computable value...
void setType(Type newType)
Mutate the type of this Value to be of the specified type.
Type getType() const
Return the type of this value.
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 IterationGraphSorter fromGenericOp(linalg::GenericOp genericOp, sparse_tensor::LoopOrderingStrategy strategy)
Factory method that constructs an iteration graph sorter for the given linalg.generic operation with ...
AffineMap sort(SortMask mask, Value ignored=nullptr)
Returns a permutation that represents the scheduled loop order.
Level getLvlRank() const
Returns the level-rank.
bool isReductionIterator(utils::IteratorType iteratorType)
Check if iterator type has "reduction" semantics.
Value constantIndex(OpBuilder &builder, Location loc, int64_t i)
Generates a constant of index type.
bool hasAnySparseOperandOrResult(Operation *op)
Returns true iff MLIR operand has any sparse operand or result.
uint64_t Level
The type of level identifiers and level-ranks.
LoopOrderingStrategy
Defines a strategy for loop ordering during sparse code generation.
AffineMap inferLvlToDim(AffineMap dimToLvl, MLIRContext *context)
Given the dimToLvl map, infers the lvlToDim map, or returns empty Affine map when inference fails.
SparseTensorEncodingAttr getSparseTensorEncoding(Type type)
Convenience method to get a sparse encoding attribute from a type.
std::optional< SparseTensorType > tryGetSparseTensorType(Value val)
bool hasAnyNonIdentityOperandsOrResults(Operation *op)
Returns true iff MLIR operation has any sparse tensor with non-identity dim2lvl maps.
SparseTensorType getSparseTensorType(Value val)
Convenience methods to obtain a SparseTensorType from a Value.
SortMask
Iteration graph sorting mask,.
bool hasAnySparseResult(Operation *op)
Returns true iff MLIR operand has any sparse result.
Include the generated interface declarations.
AffineMap inversePermutation(AffineMap map)
Returns a map of codomain to domain dimensions such that the first codomain dimension for a particula...
@ Mod
RHS of mod is always a constant or a symbolic expression with a positive value.
@ FloorDiv
RHS of floordiv is always a constant or a symbolic expression.
AffineExpr getAffineBinaryOpExpr(AffineExprKind kind, AffineExpr lhs, AffineExpr rhs)
ReinterpretMapScope
Defines a scope for reinterpret map pass.
AffineExpr getAffineConstantExpr(int64_t constant, MLIRContext *context)
llvm::DenseMap< KeyT, ValueT, KeyInfoT, BucketT > DenseMap
AffineExpr getAffineDimExpr(unsigned position, MLIRContext *context)
These free functions allow clients of the API to not use classes in detail.
void populateSparseReinterpretMap(RewritePatternSet &patterns, ReinterpretMapScope scope, sparse_tensor::LoopOrderingStrategy strategy=sparse_tensor::LoopOrderingStrategy::kDefault)
OpRewritePattern is a wrapper around RewritePattern that allows for matching and rewriting against an...
OpRewritePattern(MLIRContext *context, PatternBenefit benefit=1, ArrayRef< StringRef > generatedNames={})
Patterns must specify the root operation name they match against, and can also specify the benefit of...