30#include "llvm/ADT/SmallVectorExtras.h"
35#define GEN_PASS_DEF_LINALGELEMENTWISEOPFUSIONPASS
36#include "mlir/Dialect/Linalg/Passes.h.inc"
62 assert(invProducerResultIndexMap &&
63 "expected producer result indexing map to be invertible");
65 LinalgOp producer = cast<LinalgOp>(producerOpOperand->
getOwner());
67 AffineMap argMap = producer.getMatchingIndexingMap(producerOpOperand);
75 return t1.
compose(fusedConsumerArgIndexMap);
82 GenericOp producer, GenericOp consumer,
87 for (
auto &op : ops) {
88 for (
auto &opOperand : op->getOpOperands()) {
89 if (llvm::is_contained(opOperandsToIgnore, &opOperand)) {
92 indexingMaps.push_back(op.getMatchingIndexingMap(&opOperand));
95 if (indexingMaps.empty()) {
98 return producer.getNumLoops() == 0 && consumer.getNumLoops() == 0;
106 indexingMaps, producer.getContext())) !=
AffineMap();
115 GenericOp producer, GenericOp consumer,
OpOperand *fusedOperand) {
116 llvm::SmallDenseSet<int> preservedProducerResults;
120 opOperandsToIgnore.emplace_back(fusedOperand);
122 for (
const auto &producerResult : llvm::enumerate(producer->getResults())) {
123 auto *outputOperand = producer.getDpsInitOperand(producerResult.index());
124 opOperandsToIgnore.emplace_back(outputOperand);
125 if (producer.payloadUsesValueFromOperand(outputOperand) ||
127 opOperandsToIgnore) ||
128 llvm::any_of(producerResult.value().getUsers(), [&](
Operation *user) {
129 return user != consumer.getOperation();
131 preservedProducerResults.insert(producerResult.index());
134 (
void)opOperandsToIgnore.pop_back_val();
137 return preservedProducerResults;
146 auto consumer = dyn_cast<GenericOp>(fusedOperand->
getOwner());
149 if (!producer || !consumer)
155 if (!producer.hasPureTensorSemantics() ||
156 !isa<RankedTensorType>(fusedOperand->
get().
getType()))
161 if (producer.getNumParallelLoops() != producer.getNumLoops())
166 if (!consumer.isDpsInput(fusedOperand))
171 AffineMap consumerIndexMap = consumer.getMatchingIndexingMap(fusedOperand);
172 if (consumerIndexMap.
getNumResults() != producer.getNumLoops())
177 auto producerResult = cast<OpResult>(fusedOperand->
get());
179 producer.getIndexingMapMatchingResult(producerResult);
187 if ((consumer.getNumReductionLoops())) {
188 BitVector coveredDims(consumer.getNumLoops(),
false);
190 auto addToCoveredDims = [&](
AffineMap map) {
191 for (
auto result : map.getResults())
192 if (
auto dimExpr = dyn_cast<AffineDimExpr>(
result))
193 coveredDims[dimExpr.getPosition()] =
true;
197 llvm::zip(consumer->getOperands(), consumer.getIndexingMapsArray())) {
198 Value operand = std::get<0>(pair);
199 if (operand == fusedOperand->
get())
201 AffineMap operandMap = std::get<1>(pair);
202 addToCoveredDims(operandMap);
205 for (
OpOperand *operand : producer.getDpsInputOperands()) {
208 operand, producerResultIndexMap, consumerIndexMap);
209 addToCoveredDims(newIndexingMap);
211 if (!coveredDims.all())
223 unsigned nloops, llvm::SmallDenseSet<int> &preservedProducerResults) {
225 auto consumer = cast<GenericOp>(fusedOperand->
getOwner());
227 Block &producerBlock = producer->getRegion(0).
front();
228 Block &consumerBlock = consumer->getRegion(0).
front();
235 if (producer.hasIndexSemantics()) {
237 unsigned numFusedOpLoops = fusedOp.getNumLoops();
239 fusedIndices.reserve(numFusedOpLoops);
240 llvm::transform(llvm::seq<uint64_t>(0, numFusedOpLoops),
241 std::back_inserter(fusedIndices), [&](uint64_t dim) {
242 return IndexOp::create(rewriter, producer.getLoc(), dim);
244 for (IndexOp indexOp :
245 llvm::make_early_inc_range(producerBlock.
getOps<IndexOp>())) {
246 Value newIndex = affine::AffineApplyOp::create(
247 rewriter, producer.getLoc(),
248 consumerToProducerLoopsMap.
getSubMap(indexOp.getDim()), fusedIndices);
249 mapper.
map(indexOp.getResult(), newIndex);
253 assert(consumer.isDpsInput(fusedOperand) &&
254 "expected producer of input operand");
258 mapper.
map(bbArg, fusedBlock->
addArgument(bbArg.getType(), bbArg.getLoc()));
265 producerBlock.
getArguments().take_front(producer.getNumDpsInputs()))
266 mapper.
map(bbArg, fusedBlock->
addArgument(bbArg.getType(), bbArg.getLoc()));
271 .take_front(consumer.getNumDpsInputs())
273 mapper.
map(bbArg, fusedBlock->
addArgument(bbArg.getType(), bbArg.getLoc()));
276 for (
const auto &bbArg : llvm::enumerate(
277 producerBlock.
getArguments().take_back(producer.getNumDpsInits()))) {
278 if (!preservedProducerResults.count(bbArg.index()))
280 mapper.
map(bbArg.value(), fusedBlock->
addArgument(bbArg.value().getType(),
281 bbArg.value().getLoc()));
286 consumerBlock.
getArguments().take_back(consumer.getNumDpsInits()))
287 mapper.
map(bbArg, fusedBlock->
addArgument(bbArg.getType(), bbArg.getLoc()));
292 if (!isa<IndexOp>(op))
293 rewriter.
clone(op, mapper);
297 auto producerYieldOp = cast<linalg::YieldOp>(producerBlock.
getTerminator());
298 unsigned producerResultNumber =
299 cast<OpResult>(fusedOperand->
get()).getResultNumber();
301 mapper.
lookupOrDefault(producerYieldOp.getOperand(producerResultNumber));
305 if (
replacement == producerYieldOp.getOperand(producerResultNumber)) {
306 if (
auto bb = dyn_cast<BlockArgument>(
replacement))
307 assert(bb.getOwner() != &producerBlock &&
308 "yielded block argument must have been mapped");
310 assert(!producer->isAncestor(
replacement.getDefiningOp()) &&
311 "yielded value must have been mapped");
317 rewriter.
clone(op, mapper);
321 auto consumerYieldOp = cast<linalg::YieldOp>(consumerBlock.
getTerminator());
323 fusedYieldValues.reserve(producerYieldOp.getNumOperands() +
324 consumerYieldOp.getNumOperands());
325 for (
const auto &producerYieldVal :
326 llvm::enumerate(producerYieldOp.getOperands())) {
327 if (preservedProducerResults.count(producerYieldVal.index()))
328 fusedYieldValues.push_back(
331 for (
auto consumerYieldVal : consumerYieldOp.getOperands())
333 YieldOp::create(rewriter, fusedOp.getLoc(), fusedYieldValues);
337 "Ill-formed GenericOp region");
340FailureOr<mlir::linalg::ElementwiseOpFusionResult>
344 "expected elementwise operation pre-conditions to pass");
345 auto producerResult = cast<OpResult>(fusedOperand->
get());
346 auto producer = cast<GenericOp>(producerResult.getOwner());
347 auto consumer = cast<GenericOp>(fusedOperand->
getOwner());
349 assert(consumer.isDpsInput(fusedOperand) &&
350 "expected producer of input operand");
353 llvm::SmallDenseSet<int> preservedProducerResults =
361 fusedInputOperands.reserve(producer.getNumDpsInputs() +
362 consumer.getNumDpsInputs());
363 fusedOutputOperands.reserve(preservedProducerResults.size() +
364 consumer.getNumDpsInits());
365 fusedResultTypes.reserve(preservedProducerResults.size() +
366 consumer.getNumDpsInits());
367 fusedIndexMaps.reserve(producer->getNumOperands() +
368 consumer->getNumOperands());
371 auto consumerInputs = consumer.getDpsInputOperands();
372 auto *it = llvm::find_if(consumerInputs, [&](
OpOperand *operand) {
373 return operand == fusedOperand;
375 assert(it != consumerInputs.end() &&
"expected to find the consumer operand");
376 for (
OpOperand *opOperand : llvm::make_range(consumerInputs.begin(), it)) {
377 fusedInputOperands.push_back(opOperand->get());
378 fusedIndexMaps.push_back(consumer.getMatchingIndexingMap(opOperand));
382 producer.getIndexingMapMatchingResult(producerResult);
383 for (
OpOperand *opOperand : producer.getDpsInputOperands()) {
384 fusedInputOperands.push_back(opOperand->get());
387 opOperand, producerResultIndexMap,
388 consumer.getMatchingIndexingMap(fusedOperand));
389 fusedIndexMaps.push_back(map);
394 llvm::make_range(std::next(it), consumerInputs.end())) {
395 fusedInputOperands.push_back(opOperand->get());
396 fusedIndexMaps.push_back(consumer.getMatchingIndexingMap(opOperand));
400 for (
const auto &opOperand : llvm::enumerate(producer.getDpsInitsMutable())) {
401 if (!preservedProducerResults.count(opOperand.index()))
404 fusedOutputOperands.push_back(opOperand.value().get());
406 &opOperand.value(), producerResultIndexMap,
407 consumer.getMatchingIndexingMap(fusedOperand));
408 fusedIndexMaps.push_back(map);
409 fusedResultTypes.push_back(opOperand.value().get().getType());
413 for (
OpOperand &opOperand : consumer.getDpsInitsMutable()) {
414 fusedOutputOperands.push_back(opOperand.get());
415 fusedIndexMaps.push_back(consumer.getMatchingIndexingMap(&opOperand));
416 Type resultType = opOperand.get().getType();
417 if (!isa<MemRefType>(resultType))
418 fusedResultTypes.push_back(resultType);
422 auto fusedOp = GenericOp::create(
423 rewriter, consumer.getLoc(), fusedResultTypes, fusedInputOperands,
425 consumer.getIteratorTypes(),
428 if (!fusedOp.getShapesToLoopsMap()) {
434 fusedOp,
"fused op failed loop bound computation check");
440 consumer.getMatchingIndexingMap(fusedOperand);
444 assert(invProducerResultIndexMap &&
445 "expected producer result indexig map to be invertible");
448 invProducerResultIndexMap.
compose(consumerResultIndexMap);
451 rewriter, fusedOp, consumerToProducerLoopsMap, fusedOperand,
452 consumer.getNumLoops(), preservedProducerResults);
456 for (
auto [
index, producerResult] : llvm::enumerate(producer->getResults()))
457 if (preservedProducerResults.count(
index))
458 result.replacements[producerResult] = fusedOp->getResult(resultNum++);
459 for (
auto consumerResult : consumer->getResults())
460 result.replacements[consumerResult] = fusedOp->getResult(resultNum++);
471 controlFn(std::move(fun)) {}
473 LogicalResult matchAndRewrite(GenericOp genericOp,
476 for (
OpOperand &opOperand : genericOp->getOpOperands()) {
479 if (!controlFn(&opOperand))
482 Operation *producer = opOperand.get().getDefiningOp();
485 FailureOr<ElementwiseOpFusionResult> fusionResult =
487 if (failed(fusionResult))
491 for (
auto [origVal,
replacement] : fusionResult->replacements) {
529class SplitElementwiseOpWithConcatInputs :
public OpRewritePattern<GenericOp> {
531 using OpRewritePattern<GenericOp>::OpRewritePattern;
533 LogicalResult matchAndRewrite(GenericOp genericOp,
534 PatternRewriter &rewriter)
const override {
535 if (!genericOp.hasPureTensorSemantics() || !
isElementwise(genericOp) ||
536 genericOp.hasIndexSemantics())
539 SmallVector<tensor::ConcatOp> concatOps(genericOp.getNumDpsInputs());
540 SmallVector<OpOperand *> nonConcatInputs;
541 std::optional<unsigned> splitLoopDim;
557 SmallVector<SmallVector<int64_t>> partitionSizes;
559 for (
auto [index, operand] :
560 llvm::enumerate(genericOp.getDpsInputOperands())) {
561 auto concatOp = operand->get().getDefiningOp<tensor::ConcatOp>();
563 nonConcatInputs.push_back(operand);
569 if (!concatOp->hasOneUse())
571 "concat input has another consumer");
573 AffineMap inputMap = genericOp.getMatchingIndexingMap(operand);
575 dyn_cast<AffineDimExpr>(inputMap.
getResult(concatOp.getDim()));
578 genericOp,
"concat dimension does not map to a loop dimension");
580 unsigned currentSplitLoopDim = concatDimExpr.getPosition();
581 if (splitLoopDim && *splitLoopDim != currentSplitLoopDim)
583 genericOp,
"concat inputs partition different loop dimensions");
584 splitLoopDim = currentSplitLoopDim;
586 SmallVector<int64_t> currentPartitionSizes;
587 currentPartitionSizes.reserve(concatOp.getInputs().size());
588 for (Value input : concatOp.getInputs()) {
589 int64_t size = cast<RankedTensorType>(input.getType())
590 .getDimSize(concatOp.getDim());
591 if (ShapedType::isDynamic(size))
593 genericOp,
"concat partition size is dynamic");
594 currentPartitionSizes.push_back(size);
596 partitionSizes.push_back(std::move(currentPartitionSizes));
597 concatOps[index] = concatOp;
602 if (!llvm::all_equal(partitionSizes))
604 genericOp,
"concat inputs have different partition sizes");
609 AffineExpr splitDimExpr =
611 for (OpOperand *operand : nonConcatInputs) {
612 Type operandType = operand->get().getType();
615 if (isa<IntegerType, FloatType, IndexType, ComplexType>(operandType))
619 if (!isa<RankedTensorType>(operandType))
621 genericOp,
"non-concat shaped input is not a ranked tensor");
623 if (genericOp.getMatchingIndexingMap(operand).getResultPosition(
626 genericOp,
"non-concat input varies along the split dimension");
632 SmallVector<unsigned> outputConcatDims;
633 outputConcatDims.reserve(genericOp.getNumDpsInits());
634 for (OpOperand &output : genericOp.getDpsInitsMutable()) {
635 std::optional<unsigned> outputDim =
636 genericOp.getMatchingIndexingMap(&output).getResultPosition(
640 "elementwise output map must contain the split dimension");
641 outputConcatDims.push_back(*outputDim);
644 Location loc = genericOp.getLoc();
647 SmallVector<SmallVector<Value>> splitResults(genericOp->getNumResults());
648 SmallVector<int64_t> outputOffsets(genericOp.getNumDpsInits(), 0);
653 for (
auto [partitionIndex, partitionSize] :
654 llvm::enumerate(partitionSizes.front())) {
657 SmallVector<Value> inputs =
658 getPartitionInputs(genericOp, concatOps, partitionIndex);
662 PartitionOutputs outputs =
663 createPartitionOutputs(rewriter, loc, genericOp, outputConcatDims,
664 partitionSize, outputOffsets);
669 cloneGenericForPartition(rewriter, loc, genericOp, inputs, outputs);
670 for (
auto [resultIndex,
result] : llvm::enumerate(
splitOp->getResults()))
671 splitResults[resultIndex].push_back(
result);
676 SmallVector<Value> replacements;
677 replacements.reserve(genericOp->getNumResults());
678 for (
auto [resultIndex,
result] :
679 llvm::enumerate(genericOp->getResults())) {
680 replacements.push_back(tensor::ConcatOp::create(
681 rewriter, loc, cast<RankedTensorType>(
result.getType()),
682 outputConcatDims[resultIndex], splitResults[resultIndex]));
684 rewriter.
replaceOp(genericOp, replacements);
693 static SmallVector<Value>
694 getPartitionInputs(GenericOp genericOp, ArrayRef<tensor::ConcatOp> concatOps,
695 unsigned partitionIndex) {
696 SmallVector<Value> inputs;
697 inputs.reserve(genericOp.getNumDpsInputs());
698 for (
auto [index, operand] :
699 llvm::enumerate(genericOp.getDpsInputOperands())) {
700 if (!concatOps[index]) {
701 inputs.push_back(operand->get());
704 tensor::ConcatOp concatOp = concatOps[index];
705 inputs.push_back(concatOp.getInputs()[partitionIndex]);
710 struct PartitionOutputs {
711 SmallVector<Value> values;
712 SmallVector<Type> resultTypes;
721 static PartitionOutputs createPartitionOutputs(
722 PatternRewriter &rewriter, Location loc, GenericOp genericOp,
723 ArrayRef<unsigned> outputConcatDims, int64_t partitionSize,
724 MutableArrayRef<int64_t> outputOffsets) {
725 PartitionOutputs partitionOutputs;
726 partitionOutputs.values.reserve(genericOp.getNumDpsInits());
727 partitionOutputs.resultTypes.reserve(genericOp->getNumResults());
728 for (
auto [outputIndex, output] :
729 llvm::enumerate(genericOp.getDpsInitsMutable())) {
730 Value outputValue = output.get();
731 auto outputType = cast<RankedTensorType>(outputValue.
getType());
732 unsigned outputConcatDim = outputConcatDims[outputIndex];
733 SmallVector<int64_t> partitionShape(outputType.getShape());
734 partitionShape[outputConcatDim] = partitionSize;
736 RankedTensorType::get(partitionShape, outputType.getElementType(),
737 outputType.getEncoding());
739 SmallVector<OpFoldResult> sizes =
741 sizes[outputConcatDim] = rewriter.
getIndexAttr(partitionSize);
743 Value partitionOutput;
746 if (!genericOp.payloadUsesValueFromOperand(&output)) {
747 partitionOutput = tensor::EmptyOp::create(rewriter, loc, sizes,
748 outputType.getElementType(),
749 outputType.getEncoding());
754 SmallVector<OpFoldResult> offsets(outputType.getRank(),
756 SmallVector<OpFoldResult> strides(outputType.getRank(),
758 offsets[outputConcatDim] =
760 partitionOutput = tensor::ExtractSliceOp::create(
761 rewriter, loc, partitionType, outputValue, offsets, sizes, strides);
763 partitionOutputs.values.push_back(partitionOutput);
764 partitionOutputs.resultTypes.push_back(partitionType);
765 outputOffsets[outputIndex] += partitionSize;
767 return partitionOutputs;
774 static GenericOp cloneGenericForPartition(PatternRewriter &rewriter,
775 Location loc, GenericOp genericOp,
776 ArrayRef<Value> inputs,
777 const PartitionOutputs &outputs) {
778 GenericOp
splitOp = GenericOp::create(
779 rewriter, loc, outputs.resultTypes, inputs, outputs.values,
780 genericOp.getIndexingMapsArray(), genericOp.getIteratorTypesArray());
781 if (StringAttr doc = genericOp.getDocAttr())
783 if (StringAttr libraryCall = genericOp.getLibraryCallAttr())
785 splitOp->setDiscardableAttrs(genericOp->getDiscardableAttrDictionary());
858 linalgOp.getIteratorTypesArray();
859 AffineMap operandMap = linalgOp.getMatchingIndexingMap(fusableOpOperand);
860 return linalgOp.hasPureTensorSemantics() &&
861 llvm::all_of(linalgOp.getIndexingMaps().getValue(),
863 return cast<AffineMapAttr>(attr)
865 .isProjectedPermutation();
879 LogicalResult compute(LinalgOp linalgOp, OpOperand *fusableOpOperand,
880 ArrayRef<AffineMap> reassociationMaps,
881 ArrayRef<OpFoldResult> expandedShape,
882 PatternRewriter &rewriter);
883 unsigned getOrigOpNumDims()
const {
return reassociation.size(); }
884 unsigned getExpandedOpNumDims()
const {
return expandedOpNumDims; }
886 return reassociation[i];
888 ArrayRef<OpFoldResult> getExpandedShapeOfDim(
unsigned i)
const {
889 return expandedShapeMap[i];
891 ArrayRef<OpFoldResult> getOriginalShape()
const {
return originalLoopExtent; }
896 SmallVector<ReassociationIndices> reassociation;
899 SmallVector<SmallVector<OpFoldResult>> expandedShapeMap;
901 SmallVector<OpFoldResult> originalLoopExtent;
902 unsigned expandedOpNumDims;
906LogicalResult ExpansionInfo::compute(LinalgOp linalgOp,
911 if (reassociationMaps.empty())
913 AffineMap fusedIndexMap = linalgOp.getMatchingIndexingMap(fusableOpOperand);
915 OpBuilder::InsertionGuard g(rewriter);
917 originalLoopExtent = llvm::map_to_vector(
918 linalgOp.createLoopRanges(rewriter, linalgOp->getLoc()),
919 [](Range r) { return r.size; });
921 reassociation.clear();
922 expandedShapeMap.clear();
925 SmallVector<unsigned> numExpandedDims(fusedIndexMap.
getNumDims(), 1);
926 expandedShapeMap.resize(fusedIndexMap.
getNumDims());
927 for (
const auto &resultExpr : llvm::enumerate(fusedIndexMap.
getResults())) {
928 unsigned pos = cast<AffineDimExpr>(resultExpr.value()).getPosition();
929 AffineMap foldedDims = reassociationMaps[resultExpr.index()];
931 ArrayRef<OpFoldResult> shape =
932 expandedShape.slice(foldedDims.
getDimPosition(0), numExpandedDims[pos]);
933 expandedShapeMap[pos].assign(shape.begin(), shape.end());
936 for (
unsigned i : llvm::seq<unsigned>(0, fusedIndexMap.
getNumDims()))
937 if (expandedShapeMap[i].empty())
938 expandedShapeMap[i] = {originalLoopExtent[i]};
942 reassociation.reserve(fusedIndexMap.
getNumDims());
943 for (
const auto &numFoldedDim : llvm::enumerate(numExpandedDims)) {
944 auto seq = llvm::seq<int64_t>(sum, sum + numFoldedDim.value());
945 reassociation.emplace_back(seq.begin(), seq.end());
946 sum += numFoldedDim.value();
948 expandedOpNumDims = sum;
956 const ExpansionInfo &expansionInfo) {
959 unsigned pos = cast<AffineDimExpr>(expr).getPosition();
961 expansionInfo.getExpandedDims(pos), [&](
int64_t v) {
962 return builder.getAffineDimExpr(static_cast<unsigned>(v));
964 newExprs.append(expandedExprs.begin(), expandedExprs.end());
973static std::tuple<SmallVector<OpFoldResult>, RankedTensorType>
975 const ExpansionInfo &expansionInfo) {
978 unsigned dim = cast<AffineDimExpr>(expr).getPosition();
980 expansionInfo.getExpandedShapeOfDim(dim);
981 expandedShape.append(dimExpansion.begin(), dimExpansion.end());
984 std::tie(expandedStaticShape, std::ignore) =
986 return {expandedShape, RankedTensorType::get(expandedStaticShape,
987 originalType.getElementType())};
996static SmallVector<ReassociationIndices>
998 const ExpansionInfo &expansionInfo) {
1000 unsigned numReshapeDims = 0;
1002 unsigned dim = cast<AffineDimExpr>(expr).getPosition();
1003 auto numExpandedDims = expansionInfo.getExpandedDims(dim).size();
1005 llvm::seq<int64_t>(numReshapeDims, numReshapeDims + numExpandedDims));
1006 reassociation.emplace_back(std::move(
indices));
1007 numReshapeDims += numExpandedDims;
1009 return reassociation;
1019 const ExpansionInfo &expansionInfo) {
1021 for (IndexOp indexOp :
1022 llvm::make_early_inc_range(fusedRegion.
front().
getOps<IndexOp>())) {
1024 expansionInfo.getExpandedDims(indexOp.getDim());
1025 assert(!expandedDims.empty() &&
"expected valid expansion info");
1028 if (expandedDims.size() == 1 &&
1029 expandedDims.front() == (
int64_t)indexOp.getDim())
1036 expansionInfo.getExpandedShapeOfDim(indexOp.getDim()).drop_front();
1038 expandedIndices.reserve(expandedDims.size() - 1);
1040 expandedDims.drop_front(), std::back_inserter(expandedIndices),
1041 [&](
int64_t dim) { return IndexOp::create(rewriter, loc, dim); });
1043 IndexOp::create(rewriter, loc, expandedDims.front()).getResult();
1044 for (
auto [expandedShape, expandedIndex] :
1045 llvm::zip(expandedDimsShape, expandedIndices)) {
1050 rewriter, indexOp.getLoc(), idx +
acc *
shape,
1055 rewriter.
replaceOp(indexOp, newIndexVal);
1077 TransposeOp transposeOp,
1079 ExpansionInfo &expansionInfo) {
1082 auto reassoc = expansionInfo.getExpandedDims(perm);
1084 newPerm.push_back(dim);
1087 return TransposeOp::create(rewriter, transposeOp.getLoc(), expandedInput,
1098 expansionInfo.getExpandedOpNumDims(), utils::IteratorType::parallel);
1100 for (
auto [i, type] : llvm::enumerate(linalgOp.getIteratorTypesArray()))
1101 for (
auto j : expansionInfo.getExpandedDims(i))
1102 iteratorTypes[
j] = type;
1104 Operation *fused = GenericOp::create(rewriter, linalgOp.getLoc(), resultTypes,
1105 expandedOpOperands, outputs,
1106 expandedOpIndexingMaps, iteratorTypes);
1109 Region &originalRegion = linalgOp->getRegion(0);
1127 ExpansionInfo &expansionInfo) {
1130 .Case([&](TransposeOp transposeOp) {
1132 expandedOpOperands[0], outputs[0],
1135 .Case<FillOp, CopyOp>([&](
Operation *op) {
1136 return clone(rewriter, linalgOp, resultTypes,
1137 llvm::to_vector(llvm::concat<Value>(
1138 llvm::to_vector(expandedOpOperands),
1139 llvm::to_vector(outputs))));
1143 expandedOpOperands, outputs,
1144 expansionInfo, expandedOpIndexingMaps);
1151static std::optional<SmallVector<Value>>
1156 "preconditions for fuse operation failed");
1162 if (
auto expandingReshapeOp = dyn_cast<tensor::ExpandShapeOp>(reshapeOp)) {
1166 rewriter, expandingReshapeOp.getOutputShape(), linalgOp)))
1167 return std::nullopt;
1169 expandedShape = expandingReshapeOp.getMixedOutputShape();
1170 reassociationIndices = expandingReshapeOp.getReassociationMaps();
1171 src = expandingReshapeOp.getSrc();
1173 auto collapsingReshapeOp = dyn_cast<tensor::CollapseShapeOp>(reshapeOp);
1174 if (!collapsingReshapeOp)
1175 return std::nullopt;
1178 rewriter, collapsingReshapeOp->getLoc(), collapsingReshapeOp.getSrc());
1179 reassociationIndices = collapsingReshapeOp.getReassociationMaps();
1180 src = collapsingReshapeOp.getSrc();
1183 ExpansionInfo expansionInfo;
1184 if (failed(expansionInfo.compute(linalgOp, fusableOpOperand,
1185 reassociationIndices, expandedShape,
1187 return std::nullopt;
1190 llvm::map_to_vector<4>(linalgOp.getIndexingMapsArray(), [&](
AffineMap m) {
1191 return getIndexingMapInExpandedOp(rewriter, m, expansionInfo);
1199 expandedOpOperands.reserve(linalgOp.getNumDpsInputs());
1200 for (
OpOperand *opOperand : linalgOp.getDpsInputOperands()) {
1201 if (opOperand == fusableOpOperand) {
1202 expandedOpOperands.push_back(src);
1205 if (
auto opOperandType =
1206 dyn_cast<RankedTensorType>(opOperand->get().getType())) {
1207 AffineMap indexingMap = linalgOp.getMatchingIndexingMap(opOperand);
1209 RankedTensorType expandedOperandType;
1210 std::tie(expandedOperandShape, expandedOperandType) =
1212 if (expandedOperandType != opOperand->get().getType()) {
1216 if (failed(reshapeLikeShapesAreCompatible(
1217 [&](
const Twine &msg) {
1220 opOperandType.getShape(), expandedOperandType.getShape(),
1223 return std::nullopt;
1224 expandedOpOperands.push_back(tensor::ExpandShapeOp::create(
1225 rewriter, loc, expandedOperandType, opOperand->get(), reassociation,
1226 expandedOperandShape));
1230 expandedOpOperands.push_back(opOperand->get());
1234 for (
OpOperand &opOperand : linalgOp.getDpsInitsMutable()) {
1235 AffineMap indexingMap = linalgOp.getMatchingIndexingMap(&opOperand);
1236 auto opOperandType = cast<RankedTensorType>(opOperand.get().getType());
1238 RankedTensorType expandedOutputType;
1239 std::tie(expandedOutputShape, expandedOutputType) =
1241 if (expandedOutputType != opOperand.get().getType()) {
1244 if (failed(reshapeLikeShapesAreCompatible(
1245 [&](
const Twine &msg) {
1248 opOperandType.getShape(), expandedOutputType.getShape(),
1251 return std::nullopt;
1252 outputs.push_back(tensor::ExpandShapeOp::create(
1253 rewriter, loc, expandedOutputType, opOperand.get(), reassociation,
1254 expandedOutputShape));
1256 outputs.push_back(opOperand.get());
1263 outputs, expandedOpIndexingMaps, expansionInfo);
1267 for (
OpResult opResult : linalgOp->getOpResults()) {
1268 int64_t resultNumber = opResult.getResultNumber();
1269 if (resultTypes[resultNumber] != opResult.getType()) {
1272 linalgOp.getMatchingIndexingMap(
1273 linalgOp.getDpsInitOperand(resultNumber)),
1275 resultVals.push_back(tensor::CollapseShapeOp::create(
1276 rewriter, linalgOp.getLoc(), opResult.getType(),
1277 fusedOp->
getResult(resultNumber), reassociation));
1279 resultVals.push_back(fusedOp->
getResult(resultNumber));
1291class FoldWithProducerReshapeOpByExpansion
1292 :
public OpInterfaceRewritePattern<LinalgOp> {
1294 FoldWithProducerReshapeOpByExpansion(MLIRContext *context,
1296 PatternBenefit benefit = 1)
1297 : OpInterfaceRewritePattern<LinalgOp>(context, benefit),
1298 controlFoldingReshapes(std::move(foldReshapes)) {}
1300 LogicalResult matchAndRewrite(LinalgOp linalgOp,
1301 PatternRewriter &rewriter)
const override {
1302 for (OpOperand *opOperand : linalgOp.getDpsInputOperands()) {
1303 tensor::CollapseShapeOp reshapeOp =
1304 opOperand->get().getDefiningOp<tensor::CollapseShapeOp>();
1311 (!controlFoldingReshapes(opOperand)))
1314 std::optional<SmallVector<Value>> replacementValues =
1316 if (!replacementValues)
1318 rewriter.
replaceOp(linalgOp, *replacementValues);
1331 SmallVector<int64_t> paddedShape;
1334 SmallVector<OpFoldResult> lowPad;
1335 SmallVector<OpFoldResult> highPad;
1342static FailureOr<PadDimInfo>
1343computeExpandedPadding(tensor::PadOp padOp, ArrayRef<int64_t> expandedShape,
1344 ArrayRef<ReassociationIndices> reassociations,
1345 PatternRewriter &rewriter) {
1352 if (!padOp.getConstantPaddingValue())
1359 ArrayRef<int64_t> low = padOp.getStaticLow();
1360 ArrayRef<int64_t> high = padOp.getStaticHigh();
1361 for (
auto [reInd, l, h] : llvm::zip_equal(reassociations, low, high)) {
1362 if (reInd.size() != 1 && (l != 0 || h != 0))
1366 SmallVector<OpFoldResult> mixedLowPad(padOp.getMixedLowPad());
1367 SmallVector<OpFoldResult> mixedHighPad(padOp.getMixedHighPad());
1368 ArrayRef<int64_t> paddedShape = padOp.getResultType().getShape();
1369 PadDimInfo padDimInfo;
1370 padDimInfo.paddedShape.assign(expandedShape);
1371 padDimInfo.lowPad.assign(expandedShape.size(), rewriter.
getIndexAttr(0));
1372 padDimInfo.highPad.assign(expandedShape.size(), rewriter.
getIndexAttr(0));
1373 for (
auto [idx, reInd] : llvm::enumerate(reassociations)) {
1374 if (reInd.size() == 1) {
1375 padDimInfo.paddedShape[reInd[0]] = paddedShape[idx];
1376 padDimInfo.lowPad[reInd[0]] = mixedLowPad[idx];
1377 padDimInfo.highPad[reInd[0]] = mixedHighPad[idx];
1384class FoldPadWithProducerReshapeOpByExpansion
1385 :
public OpRewritePattern<tensor::PadOp> {
1387 FoldPadWithProducerReshapeOpByExpansion(MLIRContext *context,
1389 PatternBenefit benefit = 1)
1390 : OpRewritePattern<tensor::PadOp>(context, benefit),
1391 controlFoldingReshapes(std::move(foldReshapes)) {}
1393 LogicalResult matchAndRewrite(tensor::PadOp padOp,
1394 PatternRewriter &rewriter)
const override {
1395 tensor::CollapseShapeOp reshapeOp =
1396 padOp.getSource().getDefiningOp<tensor::CollapseShapeOp>();
1400 if (!controlFoldingReshapes(&padOp.getSourceMutable())) {
1402 "fusion blocked by control function");
1405 RankedTensorType expandedType = reshapeOp.getSrcType();
1406 SmallVector<ReassociationIndices> reassociations =
1407 reshapeOp.getReassociationIndices();
1408 FailureOr<PadDimInfo> maybeExpandedPadding = computeExpandedPadding(
1409 padOp, expandedType.getShape(), reassociations, rewriter);
1410 if (
failed(maybeExpandedPadding))
1412 PadDimInfo &expandedPadding = maybeExpandedPadding.value();
1414 Location loc = padOp->getLoc();
1415 RankedTensorType expandedPaddedType =
1416 padOp.getResultType().clone(expandedPadding.paddedShape);
1418 auto newPadOp = tensor::PadOp::create(
1419 rewriter, loc, expandedPaddedType, reshapeOp.getSrc(),
1420 expandedPadding.lowPad, expandedPadding.highPad,
1421 padOp.getConstantPaddingValue(), padOp.getNofold());
1424 padOp, padOp.getResultType(), newPadOp.getResult(), reassociations);
1433class FoldReshapeWithProducerPadOpByExpansion
1434 :
public OpRewritePattern<tensor::ExpandShapeOp> {
1436 FoldReshapeWithProducerPadOpByExpansion(MLIRContext *context,
1438 PatternBenefit benefit = 1)
1439 : OpRewritePattern<tensor::ExpandShapeOp>(context, benefit),
1440 controlFoldingReshapes(std::move(foldReshapes)) {}
1442 LogicalResult matchAndRewrite(tensor::ExpandShapeOp expandOp,
1443 PatternRewriter &rewriter)
const override {
1444 tensor::PadOp padOp = expandOp.getSrc().getDefiningOp<tensor::PadOp>();
1448 if (!controlFoldingReshapes(&expandOp.getSrcMutable())) {
1450 "fusion blocked by control function");
1453 RankedTensorType expandedType = expandOp.getResultType();
1454 SmallVector<ReassociationIndices> reassociations =
1455 expandOp.getReassociationIndices();
1456 FailureOr<PadDimInfo> maybeExpandedPadding = computeExpandedPadding(
1457 padOp, expandedType.getShape(), reassociations, rewriter);
1458 if (
failed(maybeExpandedPadding))
1460 PadDimInfo &expandedPadding = maybeExpandedPadding.value();
1462 Location loc = expandOp->getLoc();
1463 SmallVector<OpFoldResult> newExpandedSizes = expandOp.getMixedOutputShape();
1464 SmallVector<int64_t> newExpandedShape(expandedType.getShape());
1466 SmallVector<OpFoldResult> padSrcSizes =
1468 for (
auto [idx, reInd] : llvm::enumerate(reassociations)) {
1471 if (reInd.size() == 1) {
1472 newExpandedShape[reInd[0]] = padOp.getSourceType().getDimSize(idx);
1473 newExpandedSizes[reInd[0]] = padSrcSizes[idx];
1476 RankedTensorType newExpandedType = expandedType.clone(newExpandedShape);
1477 auto newExpandOp = tensor::ExpandShapeOp::create(
1478 rewriter, loc, newExpandedType, padOp.getSource(), reassociations,
1480 RankedTensorType expandedPaddedType =
1481 padOp.getResultType().clone(expandedPadding.paddedShape);
1483 auto newPadOp = tensor::PadOp::create(
1484 rewriter, loc, expandedPaddedType, newExpandOp.getResult(),
1485 expandedPadding.lowPad, expandedPadding.highPad,
1486 padOp.getConstantPaddingValue(), padOp.getNofold());
1488 rewriter.
replaceOp(expandOp, newPadOp.getResult());
1499struct FoldReshapeWithGenericOpByExpansion
1500 :
public OpRewritePattern<tensor::ExpandShapeOp> {
1502 FoldReshapeWithGenericOpByExpansion(MLIRContext *context,
1504 PatternBenefit benefit = 1)
1505 : OpRewritePattern<tensor::ExpandShapeOp>(context, benefit),
1506 controlFoldingReshapes(std::move(foldReshapes)) {}
1508 LogicalResult matchAndRewrite(tensor::ExpandShapeOp reshapeOp,
1509 PatternRewriter &rewriter)
const override {
1511 auto producerResult = dyn_cast<OpResult>(reshapeOp.getSrc());
1512 if (!producerResult) {
1514 "source not produced by an operation");
1517 auto producer = dyn_cast<LinalgOp>(producerResult.getOwner());
1520 "producer not a generic op");
1525 producer.getDpsInitOperand(producerResult.getResultNumber()))) {
1527 reshapeOp,
"failed preconditions of fusion with producer generic op");
1530 if (!controlFoldingReshapes(&reshapeOp.getSrcMutable())) {
1532 "fusion blocked by control function");
1535 std::optional<SmallVector<Value>> replacementValues =
1537 producer, reshapeOp,
1538 producer.getDpsInitOperand(producerResult.getResultNumber()),
1540 if (!replacementValues) {
1542 "fusion by expansion failed");
1549 Value reshapeReplacement =
1550 (*replacementValues)[cast<OpResult>(reshapeOp.getSrc())
1551 .getResultNumber()];
1552 if (
auto collapseOp =
1553 reshapeReplacement.
getDefiningOp<tensor::CollapseShapeOp>()) {
1554 reshapeReplacement = collapseOp.getSrc();
1556 rewriter.
replaceOp(reshapeOp, reshapeReplacement);
1557 rewriter.
replaceOp(producer, *replacementValues);
1579 "expected projected permutation");
1582 llvm::map_to_vector<4>(rangeReassociation, [&](
int64_t pos) ->
int64_t {
1583 return cast<AffineDimExpr>(indexingMap.
getResults()[pos]).getPosition();
1587 return domainReassociation;
1595 assert(!dimSequence.empty() &&
1596 "expected non-empty list for dimension sequence");
1603 llvm::SmallDenseSet<unsigned, 4> sequenceElements;
1604 sequenceElements.insert_range(dimSequence);
1606 unsigned dimSequenceStart = dimSequence[0];
1607 for (
const auto &expr : enumerate(indexingMap.
getResults())) {
1608 unsigned dimInMapStart = cast<AffineDimExpr>(expr.value()).getPosition();
1610 if (dimInMapStart == dimSequenceStart) {
1611 if (expr.index() + dimSequence.size() > indexingMap.
getNumResults())
1614 for (
const auto &dimInSequence : enumerate(dimSequence)) {
1616 cast<AffineDimExpr>(
1617 indexingMap.
getResult(expr.index() + dimInSequence.index()))
1619 if (dimInMap != dimInSequence.value())
1630 if (sequenceElements.count(dimInMapStart))
1639 return llvm::all_of(maps, [&](
AffineMap map) {
1696 if (!genericOp.hasPureTensorSemantics())
1699 if (!llvm::all_of(genericOp.getIndexingMapsArray(), [](
AffineMap map) {
1700 return map.isProjectedPermutation();
1707 genericOp.getReductionDims(reductionDims);
1709 llvm::SmallDenseSet<unsigned, 4> processedIterationDims;
1710 AffineMap indexingMap = genericOp.getMatchingIndexingMap(fusableOperand);
1711 auto iteratorTypes = genericOp.getIteratorTypesArray();
1714 assert(!foldedRangeDims.empty() &&
"unexpected empty reassociation");
1717 if (foldedRangeDims.size() == 1)
1725 if (llvm::any_of(foldedIterationSpaceDims, [&](
int64_t dim) {
1726 return processedIterationDims.count(dim);
1731 utils::IteratorType startIteratorType =
1732 iteratorTypes[foldedIterationSpaceDims[0]];
1736 if (llvm::any_of(foldedIterationSpaceDims, [&](
int64_t dim) {
1737 return iteratorTypes[dim] != startIteratorType;
1746 bool isContiguous =
false;
1747 for (
const auto &startDim : llvm::enumerate(reductionDims)) {
1749 if (startDim.value() != foldedIterationSpaceDims[0])
1753 if (startDim.index() + foldedIterationSpaceDims.size() >
1754 reductionDims.size())
1757 isContiguous =
true;
1758 for (
const auto &foldedDim :
1759 llvm::enumerate(foldedIterationSpaceDims)) {
1760 if (reductionDims[foldedDim.index() + startDim.index()] !=
1761 foldedDim.value()) {
1762 isContiguous =
false;
1773 if (llvm::any_of(genericOp.getIndexingMapsArray(),
1775 return !isDimSequencePreserved(indexingMap,
1776 foldedIterationSpaceDims);
1780 processedIterationDims.insert_range(foldedIterationSpaceDims);
1781 iterationSpaceReassociation.emplace_back(
1782 std::move(foldedIterationSpaceDims));
1785 return iterationSpaceReassociation;
1790class CollapsingInfo {
1792 LogicalResult
initialize(
unsigned origNumLoops,
1793 ArrayRef<ReassociationIndices> foldedIterationDims) {
1794 llvm::SmallDenseSet<int64_t, 4> processedDims;
1797 if (foldedIterationDim.empty())
1801 for (
auto dim : foldedIterationDim) {
1802 if (dim >= origNumLoops)
1804 if (processedDims.count(dim))
1806 processedDims.insert(dim);
1808 collapsedOpToOrigOpIterationDim.emplace_back(foldedIterationDim.begin(),
1809 foldedIterationDim.end());
1811 if (processedDims.size() > origNumLoops)
1816 for (
auto dim : llvm::seq<int64_t>(0, origNumLoops)) {
1817 if (processedDims.count(dim))
1822 llvm::sort(collapsedOpToOrigOpIterationDim,
1826 origOpToCollapsedOpIterationDim.resize(origNumLoops);
1827 for (
const auto &foldedDims :
1828 llvm::enumerate(collapsedOpToOrigOpIterationDim)) {
1829 for (
const auto &dim :
enumerate(foldedDims.value()))
1830 origOpToCollapsedOpIterationDim[dim.value()] =
1831 std::make_pair<int64_t, unsigned>(foldedDims.index(), dim.index());
1838 return collapsedOpToOrigOpIterationDim;
1861 ArrayRef<std::pair<int64_t, unsigned>> getOrigOpToCollapsedOpMapping()
const {
1862 return origOpToCollapsedOpIterationDim;
1866 unsigned getCollapsedOpIterationRank()
const {
1867 return collapsedOpToOrigOpIterationDim.size();
1873 SmallVector<ReassociationIndices> collapsedOpToOrigOpIterationDim;
1877 SmallVector<std::pair<int64_t, unsigned>> origOpToCollapsedOpIterationDim;
1883static SmallVector<utils::IteratorType>
1884getCollapsedOpIteratorTypes(ArrayRef<utils::IteratorType> iteratorTypes,
1885 const CollapsingInfo &collapsingInfo) {
1886 SmallVector<utils::IteratorType> collapsedIteratorTypes;
1888 collapsingInfo.getCollapsedOpToOrigOpMapping()) {
1889 assert(!foldedIterDims.empty() &&
1890 "reassociation indices expected to have non-empty sets");
1894 collapsedIteratorTypes.push_back(iteratorTypes[foldedIterDims[0]]);
1896 return collapsedIteratorTypes;
1902getCollapsedOpIndexingMap(AffineMap indexingMap,
1903 const CollapsingInfo &collapsingInfo) {
1904 MLIRContext *context = indexingMap.
getContext();
1906 "expected indexing map to be projected permutation");
1907 SmallVector<AffineExpr> resultExprs;
1908 auto origOpToCollapsedOpMapping =
1909 collapsingInfo.getOrigOpToCollapsedOpMapping();
1911 unsigned dim = cast<AffineDimExpr>(expr).getPosition();
1913 if (origOpToCollapsedOpMapping[dim].second != 0)
1917 resultExprs.push_back(
1920 return AffineMap::get(collapsingInfo.getCollapsedOpIterationRank(), 0,
1921 resultExprs, context);
1926static SmallVector<ReassociationIndices>
1927getOperandReassociation(AffineMap indexingMap,
1928 const CollapsingInfo &collapsingInfo) {
1929 unsigned counter = 0;
1930 SmallVector<ReassociationIndices> operandReassociation;
1931 auto origOpToCollapsedOpMapping =
1932 collapsingInfo.getOrigOpToCollapsedOpMapping();
1933 auto collapsedOpToOrigOpMapping =
1934 collapsingInfo.getCollapsedOpToOrigOpMapping();
1937 cast<AffineDimExpr>(indexingMap.
getResult(counter)).getPosition();
1941 unsigned numFoldedDims =
1942 collapsedOpToOrigOpMapping[origOpToCollapsedOpMapping[dim].first]
1944 if (origOpToCollapsedOpMapping[dim].second == 0) {
1945 auto range = llvm::seq<unsigned>(counter, counter + numFoldedDims);
1946 operandReassociation.emplace_back(range.begin(), range.end());
1948 counter += numFoldedDims;
1950 return operandReassociation;
1954static Value getCollapsedOpOperand(Location loc, LinalgOp op,
1955 OpOperand *opOperand,
1956 const CollapsingInfo &collapsingInfo,
1957 OpBuilder &builder) {
1958 AffineMap indexingMap = op.getMatchingIndexingMap(opOperand);
1959 SmallVector<ReassociationIndices> operandReassociation =
1960 getOperandReassociation(indexingMap, collapsingInfo);
1965 Value operand = opOperand->
get();
1966 if (operandReassociation.size() == indexingMap.
getNumResults())
1970 if (isa<MemRefType>(operand.
getType())) {
1971 return memref::CollapseShapeOp::create(builder, loc, operand,
1972 operandReassociation)
1975 return tensor::CollapseShapeOp::create(builder, loc, operand,
1976 operandReassociation)
1982static void generateCollapsedIndexingRegion(
1983 Location loc,
Block *block,
const CollapsingInfo &collapsingInfo,
1984 ArrayRef<OpFoldResult> loopRange, RewriterBase &rewriter) {
1985 OpBuilder::InsertionGuard g(rewriter);
1989 auto indexOps = llvm::to_vector(block->
getOps<linalg::IndexOp>());
1998 llvm::DenseMap<unsigned, Value> indexReplacementVals;
1999 for (
auto foldedDims :
2000 enumerate(collapsingInfo.getCollapsedOpToOrigOpMapping())) {
2003 linalg::IndexOp::create(rewriter, loc, foldedDims.index());
2004 for (
auto dim : llvm::reverse(foldedDimsRef.drop_front())) {
2007 indexReplacementVals[dim] =
2008 rewriter.
createOrFold<arith::RemSIOp>(loc, newIndexVal, loopDim);
2010 rewriter.
createOrFold<arith::DivSIOp>(loc, newIndexVal, loopDim);
2012 indexReplacementVals[foldedDims.value().front()] = newIndexVal;
2015 for (
auto indexOp : indexOps) {
2016 auto dim = indexOp.getDim();
2017 rewriter.
replaceOp(indexOp, indexReplacementVals[dim]);
2021static void collapseOperandsAndResults(LinalgOp op,
2022 const CollapsingInfo &collapsingInfo,
2023 RewriterBase &rewriter,
2024 SmallVectorImpl<Value> &inputOperands,
2025 SmallVectorImpl<Value> &outputOperands,
2026 SmallVectorImpl<Type> &resultTypes) {
2027 Location loc = op->getLoc();
2029 llvm::map_to_vector(op.getDpsInputOperands(), [&](OpOperand *opOperand) {
2030 return getCollapsedOpOperand(loc, op, opOperand, collapsingInfo,
2035 resultTypes.reserve(op.getNumDpsInits());
2036 outputOperands.reserve(op.getNumDpsInits());
2037 for (OpOperand &output : op.getDpsInitsMutable()) {
2039 getCollapsedOpOperand(loc, op, &output, collapsingInfo, rewriter);
2040 outputOperands.push_back(newOutput);
2043 if (!op.hasPureBufferSemantics())
2044 resultTypes.push_back(newOutput.
getType());
2049template <
typename OpTy>
2050static OpTy cloneToCollapsedOp(RewriterBase &rewriter, OpTy origOp,
2051 const CollapsingInfo &collapsingInfo) {
2058LinalgOp cloneToCollapsedOp<LinalgOp>(RewriterBase &rewriter, LinalgOp origOp,
2059 const CollapsingInfo &collapsingInfo) {
2060 SmallVector<Value> inputOperands, outputOperands;
2061 SmallVector<Type> resultTypes;
2062 collapseOperandsAndResults(origOp, collapsingInfo, rewriter, inputOperands,
2063 outputOperands, resultTypes);
2066 rewriter, origOp, resultTypes,
2067 llvm::to_vector(llvm::concat<Value>(inputOperands, outputOperands)));
2072GenericOp cloneToCollapsedOp<GenericOp>(RewriterBase &rewriter,
2074 const CollapsingInfo &collapsingInfo) {
2075 SmallVector<Value> inputOperands, outputOperands;
2076 SmallVector<Type> resultTypes;
2077 collapseOperandsAndResults(origOp, collapsingInfo, rewriter, inputOperands,
2078 outputOperands, resultTypes);
2079 SmallVector<AffineMap> indexingMaps(
2080 llvm::map_range(origOp.getIndexingMapsArray(), [&](AffineMap map) {
2081 return getCollapsedOpIndexingMap(map, collapsingInfo);
2084 SmallVector<utils::IteratorType> iteratorTypes(getCollapsedOpIteratorTypes(
2085 origOp.getIteratorTypesArray(), collapsingInfo));
2087 GenericOp collapsedOp = linalg::GenericOp::create(
2088 rewriter, origOp.getLoc(), resultTypes, inputOperands, outputOperands,
2089 indexingMaps, iteratorTypes,
2090 [](OpBuilder &builder, Location loc,
ValueRange args) {});
2091 Block *origOpBlock = &origOp->getRegion(0).front();
2092 Block *collapsedOpBlock = &collapsedOp->getRegion(0).front();
2093 rewriter.
mergeBlocks(origOpBlock, collapsedOpBlock,
2102cloneToCollapsedOp<BroadcastOp>(RewriterBase &rewriter, BroadcastOp origOp,
2103 const CollapsingInfo &collapsingInfo) {
2104 assert(origOp.getInput().getType().getRank() == 0 &&
"expected a 0-D input");
2106 SmallVector<Value> inputOperands, outputOperands;
2107 SmallVector<Type> resultTypes;
2108 collapseOperandsAndResults(origOp, collapsingInfo, rewriter, inputOperands,
2109 outputOperands, resultTypes);
2111 SmallVector<int64_t> newDimensions = {0};
2112 return BroadcastOp::create(rewriter, origOp.getLoc(), inputOperands[0],
2113 outputOperands[0], newDimensions);
2116static LinalgOp createCollapsedOp(LinalgOp op,
2117 const CollapsingInfo &collapsingInfo,
2118 RewriterBase &rewriter) {
2119 if (GenericOp genericOp = dyn_cast<GenericOp>(op.getOperation())) {
2120 return cloneToCollapsedOp(rewriter, genericOp, collapsingInfo);
2122 if (BroadcastOp broadcastOp = dyn_cast<BroadcastOp>(op.getOperation())) {
2123 return cloneToCollapsedOp(rewriter, broadcastOp, collapsingInfo);
2125 return cloneToCollapsedOp(rewriter, op, collapsingInfo);
2130 LinalgOp op, ArrayRef<ReassociationIndices> foldedIterationDims,
2131 RewriterBase &rewriter) {
2133 if (op.getNumLoops() <= 1 || foldedIterationDims.empty() ||
2135 return foldedDims.size() <= 1;
2139 CollapsingInfo collapsingInfo;
2141 collapsingInfo.initialize(op.getNumLoops(), foldedIterationDims))) {
2143 op,
"illegal to collapse specified dimensions");
2146 bool hasPureBufferSemantics = op.hasPureBufferSemantics();
2147 if (hasPureBufferSemantics &&
2148 !llvm::all_of(op->getOpOperands(), [&](OpOperand &opOperand) ->
bool {
2149 MemRefType memRefToCollapse =
2150 dyn_cast<MemRefType>(opOperand.get().getType());
2151 if (!memRefToCollapse)
2154 AffineMap indexingMap = op.getMatchingIndexingMap(&opOperand);
2155 SmallVector<ReassociationIndices> operandReassociation =
2156 getOperandReassociation(indexingMap, collapsingInfo);
2157 return memref::CollapseShapeOp::isGuaranteedCollapsible(
2158 memRefToCollapse, operandReassociation);
2161 "memref is not guaranteed collapsible");
2164 SmallVector<Range> loopRanges = op.createLoopRanges(rewriter, op.getLoc());
2165 auto opFoldIsConstantValue = [](OpFoldResult ofr, int64_t value) {
2166 if (
auto attr = llvm::dyn_cast_if_present<Attribute>(ofr))
2167 return cast<IntegerAttr>(attr).getInt() == value;
2170 actual.getSExtValue() == value;
2172 if (!llvm::all_of(loopRanges, [&](Range range) {
2173 return opFoldIsConstantValue(range.
offset, 0) &&
2174 opFoldIsConstantValue(range.
stride, 1);
2177 op,
"expected all loop ranges to have zero start and unit stride");
2180 LinalgOp collapsedOp = createCollapsedOp(op, collapsingInfo, rewriter);
2182 Location loc = op->getLoc();
2183 SmallVector<OpFoldResult> loopBound =
2184 llvm::map_to_vector(loopRanges, [](Range range) {
return range.
size; });
2186 if (collapsedOp.hasIndexSemantics()) {
2188 OpBuilder::InsertionGuard g(rewriter);
2190 generateCollapsedIndexingRegion(loc, &collapsedOp->getRegion(0).front(),
2191 collapsingInfo, loopBound, rewriter);
2196 SmallVector<Value> results;
2197 for (
const auto &originalResult : llvm::enumerate(op->getResults())) {
2198 Value collapsedOpResult = collapsedOp->getResult(originalResult.index());
2199 auto originalResultType =
2200 cast<ShapedType>(originalResult.value().getType());
2201 auto collapsedOpResultType = cast<ShapedType>(collapsedOpResult.
getType());
2202 if (collapsedOpResultType.getRank() != originalResultType.getRank()) {
2203 AffineMap indexingMap =
2204 op.getIndexingMapMatchingResult(originalResult.value());
2205 SmallVector<ReassociationIndices> reassociation =
2206 getOperandReassociation(indexingMap, collapsingInfo);
2209 "Expected indexing map to be a projected permutation for collapsing");
2210 SmallVector<OpFoldResult> resultShape =
2213 if (isa<MemRefType>(collapsedOpResult.
getType())) {
2214 result = memref::ExpandShapeOp::create(
2215 rewriter, loc, originalResultType, collapsedOpResult, reassociation,
2218 result = tensor::ExpandShapeOp::create(
2219 rewriter, loc, originalResultType, collapsedOpResult, reassociation,
2222 results.push_back(
result);
2224 results.push_back(collapsedOpResult);
2227 return CollapseResult{results, collapsedOp};
2234class FoldWithProducerReshapeOpByCollapsing
2235 :
public OpRewritePattern<GenericOp> {
2238 FoldWithProducerReshapeOpByCollapsing(MLIRContext *context,
2240 PatternBenefit benefit = 1)
2241 : OpRewritePattern<GenericOp>(context, benefit),
2242 controlFoldingReshapes(std::move(foldReshapes)) {}
2244 LogicalResult matchAndRewrite(GenericOp genericOp,
2245 PatternRewriter &rewriter)
const override {
2246 for (OpOperand &opOperand : genericOp->getOpOperands()) {
2247 tensor::ExpandShapeOp reshapeOp =
2252 SmallVector<ReassociationIndices> collapsableIterationDims =
2254 reshapeOp.getReassociationIndices());
2255 if (collapsableIterationDims.empty() ||
2256 !controlFoldingReshapes(&opOperand)) {
2261 genericOp, collapsableIterationDims, rewriter);
2262 if (!collapseResult) {
2264 genericOp,
"failed to do the fusion by collapsing transformation");
2267 rewriter.
replaceOp(genericOp, collapseResult->results);
2279struct FoldReshapeWithGenericOpByCollapsing
2280 :
public OpRewritePattern<tensor::CollapseShapeOp> {
2282 FoldReshapeWithGenericOpByCollapsing(MLIRContext *context,
2284 PatternBenefit benefit = 1)
2285 : OpRewritePattern<tensor::CollapseShapeOp>(context, benefit),
2286 controlFoldingReshapes(std::move(foldReshapes)) {}
2288 LogicalResult matchAndRewrite(tensor::CollapseShapeOp reshapeOp,
2289 PatternRewriter &rewriter)
const override {
2292 auto producerResult = dyn_cast<OpResult>(reshapeOp.getSrc());
2293 if (!producerResult) {
2295 "source not produced by an operation");
2299 auto producer = dyn_cast<GenericOp>(producerResult.getOwner());
2302 "producer not a generic op");
2305 SmallVector<ReassociationIndices> collapsableIterationDims =
2308 producer.getDpsInitOperand(producerResult.getResultNumber()),
2309 reshapeOp.getReassociationIndices());
2310 if (collapsableIterationDims.empty()) {
2312 reshapeOp,
"failed preconditions of fusion with producer generic op");
2315 if (!controlFoldingReshapes(&reshapeOp.getSrcMutable())) {
2317 "fusion blocked by control function");
2323 std::optional<CollapseResult> collapseResult =
2325 if (!collapseResult) {
2327 producer,
"failed to do the fusion by collapsing transformation");
2330 rewriter.
replaceOp(producer, collapseResult->results);
2342static FailureOr<PadDimInfo>
2343computeCollapsedPadding(tensor::PadOp padOp,
2344 ArrayRef<ReassociationIndices> reassociations,
2345 PatternRewriter &rewriter) {
2352 if (!padOp.getConstantPaddingValue())
2359 ArrayRef<int64_t> low = padOp.getStaticLow();
2360 ArrayRef<int64_t> high = padOp.getStaticHigh();
2361 for (
auto [idx, reInd] : llvm::enumerate(reassociations)) {
2362 for (int64_t dim : reInd) {
2363 if ((low[dim] != 0 || high[dim] != 0) && reInd.size() != 1)
2369 ArrayRef<int64_t> expandedPaddedShape = padOp.getType().getShape();
2370 PadDimInfo padDimInfo;
2371 padDimInfo.lowPad.assign(reassociations.size(), rewriter.
getIndexAttr(0));
2372 padDimInfo.highPad.assign(reassociations.size(), rewriter.
getIndexAttr(0));
2376 SmallVector<OpFoldResult> mixedLowPad(padOp.getMixedLowPad());
2377 SmallVector<OpFoldResult> mixedHighPad(padOp.getMixedHighPad());
2378 for (
auto [idx, reInd] : llvm::enumerate(reassociations)) {
2379 if (reInd.size() == 1) {
2380 padDimInfo.lowPad[idx] = mixedLowPad[reInd[0]];
2381 padDimInfo.highPad[idx] = mixedHighPad[reInd[0]];
2384 for (int64_t dim : reInd) {
2388 padDimInfo.paddedShape.push_back(collapsedSize.
asInteger());
2394class FoldPadWithProducerReshapeOpByCollapsing
2395 :
public OpRewritePattern<tensor::PadOp> {
2397 FoldPadWithProducerReshapeOpByCollapsing(MLIRContext *context,
2399 PatternBenefit benefit = 1)
2400 : OpRewritePattern<tensor::PadOp>(context, benefit),
2401 controlFoldingReshapes(std::move(foldReshapes)) {}
2403 LogicalResult matchAndRewrite(tensor::PadOp padOp,
2404 PatternRewriter &rewriter)
const override {
2405 tensor::ExpandShapeOp reshapeOp =
2406 padOp.getSource().getDefiningOp<tensor::ExpandShapeOp>();
2410 if (!controlFoldingReshapes(&padOp.getSourceMutable())) {
2412 "fusion blocked by control function");
2415 SmallVector<ReassociationIndices> reassociations =
2416 reshapeOp.getReassociationIndices();
2417 FailureOr<PadDimInfo> maybeCollapsedPadding =
2418 computeCollapsedPadding(padOp, reassociations, rewriter);
2419 if (
failed(maybeCollapsedPadding))
2421 PadDimInfo &collapsedPadding = maybeCollapsedPadding.value();
2423 SmallVector<OpFoldResult> expandedPaddedSizes =
2424 reshapeOp.getMixedOutputShape();
2425 AffineExpr d0, d1, d2;
2428 Location loc = reshapeOp->getLoc();
2429 for (
auto [reInd, l, h] :
2430 llvm::zip_equal(reassociations, collapsedPadding.lowPad,
2431 collapsedPadding.highPad)) {
2432 if (reInd.size() == 1) {
2433 expandedPaddedSizes[reInd[0]] = affine::makeComposedFoldedAffineApply(
2434 rewriter, loc, addMap, {l, h, expandedPaddedSizes[reInd[0]]});
2438 RankedTensorType collapsedPaddedType =
2439 padOp.getType().clone(collapsedPadding.paddedShape);
2440 auto newPadOp = tensor::PadOp::create(
2441 rewriter, loc, collapsedPaddedType, reshapeOp.getSrc(),
2442 collapsedPadding.lowPad, collapsedPadding.highPad,
2443 padOp.getConstantPaddingValue(), padOp.getNofold());
2446 padOp, padOp.getResultType(), newPadOp.getResult(), reassociations,
2447 expandedPaddedSizes);
2456class FoldReshapeWithProducerPadOpByCollapsing
2457 :
public OpRewritePattern<tensor::CollapseShapeOp> {
2459 FoldReshapeWithProducerPadOpByCollapsing(MLIRContext *context,
2461 PatternBenefit benefit = 1)
2462 : OpRewritePattern<tensor::CollapseShapeOp>(context, benefit),
2463 controlFoldingReshapes(std::move(foldReshapes)) {}
2465 LogicalResult matchAndRewrite(tensor::CollapseShapeOp reshapeOp,
2466 PatternRewriter &rewriter)
const override {
2467 tensor::PadOp padOp = reshapeOp.getSrc().getDefiningOp<tensor::PadOp>();
2471 if (!controlFoldingReshapes(&reshapeOp.getSrcMutable())) {
2473 "fusion blocked by control function");
2476 SmallVector<ReassociationIndices> reassociations =
2477 reshapeOp.getReassociationIndices();
2478 RankedTensorType collapsedPaddedType = reshapeOp.getResultType();
2479 FailureOr<PadDimInfo> maybeCollapsedPadding =
2480 computeCollapsedPadding(padOp, reassociations, rewriter);
2481 if (
failed(maybeCollapsedPadding))
2483 PadDimInfo &collapsedPadding = maybeCollapsedPadding.value();
2485 Location loc = reshapeOp->getLoc();
2486 auto newCollapseOp = tensor::CollapseShapeOp::create(
2487 rewriter, loc, padOp.getSource(), reassociations);
2489 auto newPadOp = tensor::PadOp::create(
2490 rewriter, loc, collapsedPaddedType, newCollapseOp.getResult(),
2491 collapsedPadding.lowPad, collapsedPadding.highPad,
2492 padOp.getConstantPaddingValue(), padOp.getNofold());
2494 rewriter.
replaceOp(reshapeOp, newPadOp.getResult());
2503template <
typename LinalgType>
2504class CollapseLinalgDimensions :
public OpRewritePattern<LinalgType> {
2506 CollapseLinalgDimensions(MLIRContext *context,
2508 PatternBenefit benefit = 1)
2509 : OpRewritePattern<LinalgType>(context, benefit),
2510 controlCollapseDimension(std::move(collapseDimensions)) {}
2512 LogicalResult matchAndRewrite(LinalgType op,
2513 PatternRewriter &rewriter)
const override {
2514 SmallVector<ReassociationIndices> collapsableIterationDims =
2515 controlCollapseDimension(op);
2516 if (collapsableIterationDims.empty())
2521 collapsableIterationDims)) {
2523 op,
"specified dimensions cannot be collapsed");
2526 std::optional<CollapseResult> collapseResult =
2528 if (!collapseResult) {
2531 rewriter.
replaceOp(op, collapseResult->results);
2548class FoldScalarOrSplatConstant :
public OpRewritePattern<GenericOp> {
2550 FoldScalarOrSplatConstant(MLIRContext *context, PatternBenefit benefit = 1)
2551 : OpRewritePattern<GenericOp>(context, benefit) {}
2553 LogicalResult matchAndRewrite(GenericOp genericOp,
2554 PatternRewriter &rewriter)
const override {
2555 if (!genericOp.hasPureTensorSemantics())
2557 for (OpOperand *opOperand : genericOp.getDpsInputOperands()) {
2559 TypedAttr constantAttr;
2560 auto isScalarOrSplatConstantOp = [&constantAttr](Operation *def) ->
bool {
2562 DenseElementsAttr splatAttr;
2565 splatAttr.
getType().getElementType().isIntOrFloat()) {
2571 IntegerAttr intAttr;
2573 constantAttr = intAttr;
2578 FloatAttr floatAttr;
2580 constantAttr = floatAttr;
2587 auto resultValue = dyn_cast<OpResult>(opOperand->
get());
2588 if (!def || !resultValue || !isScalarOrSplatConstantOp(def))
2594 SmallVector<AffineMap> fusedIndexMaps;
2595 SmallVector<Value> fusedOperands;
2596 SmallVector<Location> fusedLocs{genericOp.getLoc()};
2597 fusedIndexMaps.reserve(genericOp->getNumOperands());
2598 fusedOperands.reserve(genericOp.getNumDpsInputs());
2599 fusedLocs.reserve(fusedLocs.size() + genericOp.getNumDpsInputs());
2600 for (OpOperand *inputOperand : genericOp.getDpsInputOperands()) {
2601 if (inputOperand == opOperand)
2603 Value inputValue = inputOperand->get();
2604 fusedIndexMaps.push_back(
2605 genericOp.getMatchingIndexingMap(inputOperand));
2606 fusedOperands.push_back(inputValue);
2607 fusedLocs.push_back(inputValue.
getLoc());
2609 for (OpOperand &outputOperand : genericOp.getDpsInitsMutable())
2610 fusedIndexMaps.push_back(
2611 genericOp.getMatchingIndexingMap(&outputOperand));
2617 genericOp,
"fused op loop bound computation failed");
2621 Value scalarConstant =
2622 arith::ConstantOp::create(rewriter, def->
getLoc(), constantAttr);
2624 SmallVector<Value> outputOperands = genericOp.getOutputs();
2626 GenericOp::create(rewriter, rewriter.
getFusedLoc(fusedLocs),
2627 genericOp->getResultTypes(),
2631 genericOp.getIteratorTypes(),
2637 Region ®ion = genericOp->getRegion(0);
2642 Region &fusedRegion = fusedOp->getRegion(0);
2645 rewriter.
replaceOp(genericOp, fusedOp->getResults());
2663struct RemoveOutsDependency :
public OpRewritePattern<GenericOp> {
2664 using OpRewritePattern<GenericOp>::OpRewritePattern;
2666 LogicalResult matchAndRewrite(GenericOp op,
2667 PatternRewriter &rewriter)
const override {
2669 bool modifiedOutput =
false;
2670 Location loc = op.getLoc();
2671 for (OpOperand &opOperand : op.getDpsInitsMutable()) {
2672 if (!op.payloadUsesValueFromOperand(&opOperand)) {
2673 Value operandVal = opOperand.
get();
2674 auto operandType = dyn_cast<RankedTensorType>(operandVal.
getType());
2683 auto definingOp = operandVal.
getDefiningOp<tensor::EmptyOp>();
2686 modifiedOutput =
true;
2687 SmallVector<OpFoldResult> mixedSizes =
2689 Value emptyTensor = tensor::EmptyOp::create(
2690 rewriter, loc, mixedSizes, operandType.getElementType());
2694 if (!modifiedOutput) {
2704struct FoldFillWithGenericOp :
public OpRewritePattern<GenericOp> {
2705 using OpRewritePattern<GenericOp>::OpRewritePattern;
2707 LogicalResult matchAndRewrite(GenericOp genericOp,
2708 PatternRewriter &rewriter)
const override {
2709 if (!genericOp.hasPureTensorSemantics())
2711 bool fillFound =
false;
2712 Block &payload = genericOp.getRegion().front();
2713 for (OpOperand *opOperand : genericOp.getDpsInputOperands()) {
2714 if (!genericOp.payloadUsesValueFromOperand(opOperand))
2720 Value fillVal = fillOp.value();
2722 cast<RankedTensorType>(fillOp.result().getType()).getElementType();
2723 Value convertedVal =
2737 patterns.
add<FoldReshapeWithGenericOpByExpansion>(patterns.
getContext(),
2738 controlFoldingReshapes);
2739 patterns.
add<FoldPadWithProducerReshapeOpByExpansion>(patterns.
getContext(),
2740 controlFoldingReshapes);
2741 patterns.
add<FoldReshapeWithProducerPadOpByExpansion>(patterns.
getContext(),
2742 controlFoldingReshapes);
2743 patterns.
add<FoldWithProducerReshapeOpByExpansion>(patterns.
getContext(),
2744 controlFoldingReshapes);
2750 patterns.
add<FoldWithProducerReshapeOpByCollapsing>(patterns.
getContext(),
2751 controlFoldingReshapes);
2752 patterns.
add<FoldPadWithProducerReshapeOpByCollapsing>(
2753 patterns.
getContext(), controlFoldingReshapes);
2754 patterns.
add<FoldReshapeWithProducerPadOpByCollapsing>(
2755 patterns.
getContext(), controlFoldingReshapes);
2756 patterns.
add<FoldReshapeWithGenericOpByCollapsing>(patterns.
getContext(),
2757 controlFoldingReshapes);
2764 patterns.
add<FuseElementwiseOps>(context, controlElementwiseOpsFusion);
2765 patterns.
add<FoldFillWithGenericOp, FoldScalarOrSplatConstant,
2766 RemoveOutsDependency>(context);
2773 patterns.
add<SplitElementwiseOpWithConcatInputs>(patterns.
getContext());
2779 patterns.
add<CollapseLinalgDimensions<linalg::GenericOp>,
2780 CollapseLinalgDimensions<linalg::CopyOp>>(
2781 patterns.
getContext(), controlCollapseDimensions);
2796struct LinalgElementwiseOpFusionPass
2797 :
public impl::LinalgElementwiseOpFusionPassBase<
2798 LinalgElementwiseOpFusionPass> {
2799 using impl::LinalgElementwiseOpFusionPassBase<
2800 LinalgElementwiseOpFusionPass>::LinalgElementwiseOpFusionPassBase;
2801 void runOnOperation()
override {
2808 Operation *producer = fusedOperand->get().getDefiningOp();
2809 return producer && producer->
hasOneUse();
2819 affine::AffineApplyOp::getCanonicalizationPatterns(patterns, context);
2820 GenericOp::getCanonicalizationPatterns(patterns, context);
2821 tensor::ExpandShapeOp::getCanonicalizationPatterns(patterns, context);
2822 tensor::CollapseShapeOp::getCanonicalizationPatterns(patterns, context);
static bool isOpOperandCanBeDroppedAfterFusedLinalgs(GenericOp producer, GenericOp consumer, ArrayRef< OpOperand * > opOperandsToIgnore)
static AffineMap getIndexingMapOfProducerOperandsInCoordinatesOfFusedOp(OpOperand *producerOpOperand, AffineMap producerResultIndexMap, AffineMap fusedConsumerArgIndexMap)
Append to fusedOpIndexingMapAttrs the indexing maps for the operands of the producer to use in the fu...
static SmallVector< ReassociationIndices > getCollapsableIterationSpaceDims(GenericOp genericOp, OpOperand *fusableOperand, ArrayRef< ReassociationIndices > reassociation)
ArrayRef< ReassociationIndices > getCollapsedOpToOrigOpMapping() const
Return mapping from collapsed loop domain to original loop domain.
LogicalResult initialize(unsigned origNumLoops, ArrayRef< ReassociationIndices > foldedIterationDims)
static std::tuple< SmallVector< OpFoldResult >, RankedTensorType > getExpandedShapeAndType(RankedTensorType originalType, AffineMap indexingMap, const ExpansionInfo &expansionInfo)
Return the shape and type of the operand/result to use in the expanded op given the type in the origi...
static void updateExpandedGenericOpRegion(PatternRewriter &rewriter, Location loc, Region &fusedRegion, const ExpansionInfo &expansionInfo)
Update the body of an expanded linalg operation having index semantics.
static Operation * createExpandedTransposeOp(PatternRewriter &rewriter, TransposeOp transposeOp, Value expandedInput, Value output, ExpansionInfo &expansionInfo)
static SmallVector< ReassociationIndices > getReassociationForExpansion(AffineMap indexingMap, const ExpansionInfo &expansionInfo)
Returns the reassociation maps to use in the tensor.expand_shape operation to convert the operands of...
static bool isFusableWithReshapeByDimExpansion(LinalgOp linalgOp, OpOperand *fusableOpOperand)
Conditions for folding a structured linalg operation with a reshape op by expanding the iteration spa...
static Operation * createExpandedGenericOp(PatternRewriter &rewriter, LinalgOp linalgOp, TypeRange resultTypes, ArrayRef< Value > &expandedOpOperands, ArrayRef< Value > outputs, ExpansionInfo &expansionInfo, ArrayRef< AffineMap > expandedOpIndexingMaps)
static Operation * createExpandedOp(PatternRewriter &rewriter, LinalgOp linalgOp, TypeRange resultTypes, ArrayRef< Value > expandedOpOperands, ArrayRef< Value > outputs, ArrayRef< AffineMap > expandedOpIndexingMaps, ExpansionInfo &expansionInfo)
static ReassociationIndices getDomainReassociation(AffineMap indexingMap, ReassociationIndicesRef rangeReassociation)
For a given list of indices in the range of the indexingMap that are folded, return the indices of th...
static void generateFusedElementwiseOpRegion(RewriterBase &rewriter, GenericOp fusedOp, AffineMap consumerToProducerLoopsMap, OpOperand *fusedOperand, unsigned nloops, llvm::SmallDenseSet< int > &preservedProducerResults)
Generate the region of the fused tensor operation.
static std::optional< SmallVector< Value > > fuseWithReshapeByExpansion(LinalgOp linalgOp, Operation *reshapeOp, OpOperand *fusableOpOperand, PatternRewriter &rewriter)
Implements the fusion of a tensor.collapse_shape or a tensor.expand_shape op and a generic op as expl...
static AffineMap getIndexingMapInExpandedOp(OpBuilder &builder, AffineMap indexingMap, const ExpansionInfo &expansionInfo)
Return the indexing map to use in the expanded op for a given the indexingMap of the original operati...
*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`
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
unsigned getDimPosition(unsigned idx) const
Extracts the position of the dimensional expression at the given result, when the caller knows it is ...
static AffineMap get(MLIRContext *context)
Returns a zero result affine map with no dimensions or symbols: () -> ().
bool isProjectedPermutation(bool allowZeroInResults=false) const
Returns true if the AffineMap represents a subset (i.e.
unsigned getNumSymbols() const
unsigned getNumDims() const
ArrayRef< AffineExpr > getResults() const
unsigned getNumResults() const
AffineExpr getResult(unsigned idx) const
AffineMap getSubMap(ArrayRef< unsigned > resultPos) const
Returns the map consisting of the resultPos subset.
AffineMap compose(AffineMap map) const
Returns the AffineMap resulting from composing this with map.
bool isPermutation() const
Returns true if the AffineMap represents a symbol-less permutation map.
Attributes are known-constant values of operations.
This class represents an argument of a Block.
Block represents an ordered list of Operations.
BlockArgument getArgument(unsigned i)
unsigned getNumArguments()
iterator_range< op_iterator< OpT > > getOps()
Return an iterator range over the operations within this block that are of 'OpT'.
Operation * getTerminator()
Get the terminator operation of this block.
BlockArgument addArgument(Type type, Location loc)
Add one value to the argument list.
BlockArgListType getArguments()
iterator_range< iterator > without_terminator()
Return an iterator range over the operation within this block excluding the terminator operation at t...
IntegerAttr getIndexAttr(int64_t value)
Location getFusedLoc(ArrayRef< Location > locs, Attribute metadata=Attribute())
MLIRContext * getContext() const
ArrayAttr getAffineMapArrayAttr(ArrayRef< AffineMap > values)
std::enable_if_t<!std::is_base_of< Attribute, T >::value||std::is_same< Attribute, T >::value, T > getSplatValue() const
Return the splat value for this attribute.
bool isSplat() const
Returns true if this attribute corresponds to a splat, i.e.
ShapedType getType() const
Return the type of this ElementsAttr, guaranteed to be a vector or tensor with static shape.
This class allows control over how the GreedyPatternRewriteDriver works.
This is a utility class for mapping one set of IR entities to another.
auto lookupOrDefault(T from) const
Lookup a mapped value within the map.
void map(Value from, Value to)
Inserts a new mapping for 'from' to 'to'.
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.
Dialect * getLoadedDialect(StringRef name)
Get a registered IR dialect with the given namespace.
RAII guard to reset the insertion point of the builder when destroyed.
This class helps build Operations.
Block * createBlock(Region *parent, Region::iterator insertPt={}, TypeRange argTypes={}, ArrayRef< Location > locs={})
Add new block with 'argTypes' arguments and set the insertion point to the end of it.
Operation * clone(Operation &op, IRMapping &mapper)
Creates a deep copy of the specified operation, remapping any operands that use values outside of the...
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 cloneRegionBefore(Region ®ion, Region &parent, Region::iterator before, IRMapping &mapping)
Clone the blocks that belong to "region" before the given position in another region "parent".
void createOrFold(SmallVectorImpl< Value > &results, Location location, Args &&...args)
Create an operation of specific op type at the current insertion point, and immediately try to fold i...
void setInsertionPointAfterValue(Value val)
Sets the insertion point to the node after the specified value.
void setInsertionPointAfter(Operation *op)
Sets the insertion point to the node after the specified operation, which will cause subsequent inser...
This class represents a single result from folding an operation.
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.
Region & getRegion(unsigned index)
Returns the region held by this operation at position 'index'.
bool hasOneUse()
Returns true if this operation has exactly one use.
OpResult getResult(unsigned idx)
Get the 'idx'th result of this operation.
Location getLoc()
The source location the operation was defined or derived from.
MLIRContext * getContext()
Return the context this operation is associated with.
This class represents the benefit of a pattern match in a unitless scheme that ranges from 0 (very li...
A special type of RewriterBase that coordinates the application of a rewrite pattern on the current I...
This class contains a list of basic blocks and a link to the parent operation it is attached to.
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.
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...
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.
virtual void cancelOpModification(Operation *op)
This method cancels a pending in-place modification.
virtual 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 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,...
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...
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 various different ranges of value types.
Instances of the Type class are uniqued, have an immutable identifier and an optional mutable compone...
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.
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.
Operation * getOwner() const
Return the owner of this operand.
OpFoldResult makeComposedFoldedAffineApply(OpBuilder &b, Location loc, AffineMap map, ArrayRef< OpFoldResult > operands, bool composeAffineMin=false)
Constructs an AffineApplyOp that applies map to operands after composing the map with the maps of any...
constexpr void enumerate(std::tuple< Tys... > &tuple, CallbackT &&callback)
bool areDimSequencesPreserved(ArrayRef< AffineMap > maps, ArrayRef< ReassociationIndices > dimSequences)
Return true if all sequences of dimensions specified in dimSequences are contiguous in all the ranges...
bool isParallelIterator(utils::IteratorType iteratorType)
Check if iterator type has "parallel" semantics.
bool isDimSequencePreserved(AffineMap map, ReassociationIndicesRef dimSequence)
Return true if a given sequence of dimensions are contiguous in the range of the specified indexing m...
void populateFoldReshapeOpsByCollapsingPatterns(RewritePatternSet &patterns, const ControlFusionFn &controlFoldingReshapes)
Patterns to fold an expanding tensor.expand_shape operation with its producer generic operation by co...
FailureOr< ElementwiseOpFusionResult > fuseElementwiseOps(RewriterBase &rewriter, OpOperand *fusedOperand)
This transformation is intended to be used with a top-down traversal (from producer to consumer).
llvm::SmallDenseSet< int > getPreservedProducerResults(GenericOp producer, GenericOp consumer, OpOperand *fusedOperand)
Returns a set of indices of the producer's results which would be preserved after the fusion.
bool isReductionIterator(utils::IteratorType iteratorType)
Check if iterator type has "reduction" semantics.
std::function< SmallVector< ReassociationIndices >(linalg::LinalgOp)> GetCollapsableDimensionsFn
Function type to control generic op dimension collapsing.
bool isElementwise(LinalgOp op)
Check if a LinalgOp is an element-wise operation.
void populateCollapseDimensions(RewritePatternSet &patterns, const GetCollapsableDimensionsFn &controlCollapseDimensions)
Pattern to collapse dimensions in a linalg.generic op.
bool areElementwiseOpsFusable(OpOperand *fusedOperand)
Return true if two linalg.generic operations with producer/consumer relationship through fusedOperand...
void populateEraseUnusedOperandsAndResultsPatterns(RewritePatternSet &patterns)
Pattern to remove dead operands and results of linalg.generic operations.
std::function< bool(OpOperand *fusedOperand)> ControlFusionFn
Function type which is used to control when to stop fusion.
void populateSplitElementwiseOpsWithConcatInputsPatterns(RewritePatternSet &patterns)
Patterns that split elementwise linalg.generic operations at the boundaries of compatible tensor....
void populateFoldReshapeOpsByExpansionPatterns(RewritePatternSet &patterns, const ControlFusionFn &controlFoldingReshapes)
Patterns to fold an expanding (collapsing) tensor_reshape operation with its producer (consumer) gene...
void populateConstantFoldLinalgOperations(RewritePatternSet &patterns, const ControlFusionFn &controlFn)
Patterns to constant fold Linalg operations.
FailureOr< CollapseResult > collapseOpIterationDims(LinalgOp op, ArrayRef< ReassociationIndices > foldedIterationDims, RewriterBase &rewriter)
Collapses dimensions of linalg.generic/linalg.copy operation.
std::pair< TilingInterface, TilingInterface > splitOp(RewriterBase &rewriter, TilingInterface op, unsigned dimension, OpFoldResult splitPoint)
Split the given op into two parts along the given iteration space dimension at the specified splitPoi...
void populateElementwiseOpsFusionPatterns(RewritePatternSet &patterns, const ControlFusionFn &controlElementwiseOpFusion)
Patterns for fusing linalg operation on tensors.
SparseTensorEncodingAttr getSparseTensorEncoding(Type type)
Convenience method to get a sparse encoding attribute from a type.
void populateBubbleUpExpandShapePatterns(RewritePatternSet &patterns)
Populates patterns with patterns that bubble up tensor.expand_shape through tensor....
SmallVector< OpFoldResult > getMixedSizes(OpBuilder &builder, Location loc, Value value)
Return the dimensions of the given tensor value.
Include the generated interface declarations.
bool matchPattern(Value value, const Pattern &pattern)
Entry point for matching a pattern over a Value.
detail::constant_int_value_binder m_ConstantInt(IntegerAttr::ValueType *bind_value)
Matches a constant holding a scalar/vector/tensor integer (splat) and writes the integer value to bin...
AffineMap concatAffineMaps(ArrayRef< AffineMap > maps, MLIRContext *context)
Concatenates a list of maps into a single AffineMap, stepping over potentially empty maps.
Value convertScalarToDtype(OpBuilder &b, Location loc, Value operand, Type toType, bool isUnsignedCast)
Converts a scalar value operand to type toType.
ArrayRef< int64_t > ReassociationIndicesRef
void bindDims(MLIRContext *ctx, AffineExprTy &...exprs)
Bind a list of AffineExpr references to DimExpr at positions: [0 .
LogicalResult applyPatternsGreedily(Region ®ion, const FrozenRewritePatternSet &patterns, GreedyRewriteConfig config=GreedyRewriteConfig(), bool *changed=nullptr)
Rewrite ops in the given region, which must be isolated from above, by repeatedly applying the highes...
AffineMap inversePermutation(AffineMap map)
Returns a map of codomain to domain dimensions such that the first codomain dimension for a particula...
void bindSymbols(MLIRContext *ctx, AffineExprTy &...exprs)
Bind a list of AffineExpr references to SymbolExpr at positions: [0 .
llvm::TypeSwitch< T, ResultT > TypeSwitch
Value getValueOrCreateConstantIndexOp(OpBuilder &b, Location loc, OpFoldResult ofr)
Converts an OpFoldResult to a Value.
Operation * clone(OpBuilder &b, Operation *op, TypeRange newResultTypes, ValueRange newOperands)
SmallVector< T > applyPermutationMap(AffineMap map, llvm::ArrayRef< T > source)
Apply a permutation from map to source and return the result.
SmallVector< int64_t, 2 > ReassociationIndices
detail::constant_op_matcher m_Constant()
Matches a constant foldable operation.
AffineExpr getAffineDimExpr(unsigned position, MLIRContext *context)
These free functions allow clients of the API to not use classes in detail.
LogicalResult moveValueDefinitions(RewriterBase &rewriter, ValueRange values, Operation *insertionPoint, DominanceInfo &dominance)
Move definitions of values (and their transitive dependencies) before insertionPoint.
std::pair< SmallVector< int64_t >, SmallVector< Value > > decomposeMixedValues(ArrayRef< OpFoldResult > mixedValues)
Decompose a vector of mixed static or dynamic values into the corresponding pair of arrays.
SmallVector< int64_t > invertPermutationVector(ArrayRef< int64_t > permutation)
Helper method to apply to inverse a permutation.
OpRewritePattern is a wrapper around RewritePattern that allows for matching and rewriting against an...
static SaturatedInteger wrap(int64_t v)
Fuse two linalg.generic operations that have a producer-consumer relationship captured through fusedO...
Eliminates variable at the specified position using Fourier-Motzkin variable elimination.