MLIR 24.0.0git
DropUnitDims.cpp
Go to the documentation of this file.
1//===- DropUnitDims.cpp - Pass to drop use of unit-extent for broadcasting ===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// This file implements patterns/pass to remove usage of unit-extent dimensions
10// to specify broadcasting in favor of more canonical representation of the
11// computation
12//
13//===----------------------------------------------------------------------===//
14
16
27#include "mlir/IR/AffineExpr.h"
28#include "mlir/IR/AffineMap.h"
33#include "llvm/ADT/SmallBitVector.h"
34#include "llvm/Support/Debug.h"
35
36namespace mlir {
37#define GEN_PASS_DEF_LINALGFOLDUNITEXTENTDIMSPASS
38#include "mlir/Dialect/Linalg/Passes.h.inc"
39} // namespace mlir
40
41#define DEBUG_TYPE "linalg-drop-unit-dims"
42
43using namespace mlir;
44using namespace mlir::linalg;
45
46namespace {
47/// Pattern to move init operands to ins when all the loops are parallel and
48/// blockArgument corresponding to init is used in the region. This is a fix-up
49/// when unit reduction dimensions are all folded away. In this context, it
50/// becomes a elementwise generic op. E.g., it converts
51///
52/// %0 = tensor.empty() : tensor<1x1xf32>
53/// %1 = linalg.fill
54/// ins(%cst : f32)
55/// outs(%0 : tensor<1x1xf32>) -> tensor<1x1xf32>
56/// %2 = linalg.generic {indexing_maps = [affine_map<(d0) -> (0, d0, 0, 0)>,
57/// affine_map<(d0) -> (0, d0)>],
58/// iterator_types = ["parallel"]}
59/// ins(%arg0 : tensor<1x?x1x1xf32>)
60/// outs(%1 : tensor<1x1xf32>) {
61/// ^bb0(%in: f32, %out: f32):
62/// %3 = arith.addf %in, %out : f32
63/// linalg.yield %3 : f32
64/// } -> tensor<1x1xf32>
65///
66/// into
67///
68/// %0 = tensor.empty() : tensor<1x1xf32>
69/// %1 = linalg.fill
70/// ins(%cst : f32)
71/// outs(%0 : tensor<1x1xf32>) -> tensor<1x1xf32>
72/// %2 = tensor.empty() : tensor<1x1xf32>
73/// %3 = linalg.generic {indexing_maps = [affine_map<(d0) -> (0, d0, 0, 0)>,
74/// affine_map<(d0) -> (0, d0)>,
75/// affine_map<(d0) -> (0, d0)>],
76/// iterator_types = ["parallel"]}
77/// ins(%arg0, %1 : tensor<1x?x1x1xf32>, tensor<1x1xf32>)
78/// outs(%2 : tensor<1x1xf32>) {
79/// ^bb0(%in: f32, %in_0: f32, %out: f32):
80/// %4 = arith.addf %in, %in_0 : f32
81/// linalg.yield %4 : f32
82/// } -> tensor<1x1xf32>
83struct MoveInitOperandsToInput : public OpRewritePattern<GenericOp> {
85 LogicalResult matchAndRewrite(GenericOp genericOp,
86 PatternRewriter &rewriter) const override {
87 if (!genericOp.hasPureTensorSemantics())
88 return failure();
89 if (genericOp.getNumParallelLoops() != genericOp.getNumLoops())
90 return failure();
91
92 auto outputOperands = genericOp.getDpsInitsMutable();
93 SetVector<OpOperand *> candidates;
94 for (OpOperand &op : outputOperands) {
95 if (genericOp.getMatchingBlockArgument(&op).use_empty())
96 continue;
97 candidates.insert(&op);
98 }
99
100 if (candidates.empty())
101 return failure();
102
103 // Compute the modified indexing maps.
104 int64_t origNumInput = genericOp.getNumDpsInputs();
105 SmallVector<Value> newInputOperands = genericOp.getDpsInputs();
106 SmallVector<AffineMap> indexingMaps = genericOp.getIndexingMapsArray();
107 SmallVector<AffineMap> newIndexingMaps;
108 newIndexingMaps.append(indexingMaps.begin(),
109 std::next(indexingMaps.begin(), origNumInput));
110 for (OpOperand *op : candidates) {
111 newInputOperands.push_back(op->get());
112 newIndexingMaps.push_back(genericOp.getMatchingIndexingMap(op));
113 }
114 newIndexingMaps.append(std::next(indexingMaps.begin(), origNumInput),
115 indexingMaps.end());
116
117 Location loc = genericOp.getLoc();
118 SmallVector<Value> newOutputOperands =
119 llvm::to_vector(genericOp.getDpsInits());
120 for (OpOperand *op : candidates) {
121 OpBuilder::InsertionGuard guard(rewriter);
122 rewriter.setInsertionPointAfterValue(op->get());
123 auto elemType = cast<ShapedType>(op->get().getType()).getElementType();
124 auto empty = tensor::EmptyOp::create(
125 rewriter, loc, tensor::getMixedSizes(rewriter, loc, op->get()),
126 elemType);
127
128 unsigned start = genericOp.getDpsInits().getBeginOperandIndex();
129 newOutputOperands[op->getOperandNumber() - start] = empty.getResult();
130 }
131
132 auto newOp = GenericOp::create(
133 rewriter, loc, genericOp.getResultTypes(), newInputOperands,
134 newOutputOperands, newIndexingMaps, genericOp.getIteratorTypesArray(),
135 /*bodyBuild=*/nullptr, linalg::getPrunedAttributeList(genericOp));
136
137 OpBuilder::InsertionGuard guard(rewriter);
138 Region &region = newOp.getRegion();
139 Block *block = rewriter.createBlock(&region);
140 IRMapping mapper;
141 for (auto bbarg : genericOp.getRegionInputArgs())
142 mapper.map(bbarg, block->addArgument(bbarg.getType(), loc));
143
144 for (OpOperand *op : candidates) {
145 BlockArgument bbarg = genericOp.getMatchingBlockArgument(op);
146 mapper.map(bbarg, block->addArgument(bbarg.getType(), loc));
147 }
148
149 for (OpOperand &op : outputOperands) {
150 BlockArgument bbarg = genericOp.getMatchingBlockArgument(&op);
151 if (candidates.count(&op))
152 block->addArgument(bbarg.getType(), loc);
153 else
154 mapper.map(bbarg, block->addArgument(bbarg.getType(), loc));
155 }
156
157 for (auto &op : genericOp.getBody()->getOperations()) {
158 rewriter.clone(op, mapper);
159 }
160 rewriter.replaceOp(genericOp, newOp.getResults());
161
162 return success();
163 }
164};
165} // namespace
166
167//===---------------------------------------------------------------------===//
168// Drop loops that are unit-extents within Linalg operations.
169//===---------------------------------------------------------------------===//
170
171/// Implements a pass that canonicalizes the uses of unit-extent dimensions for
172/// broadcasting. For example,
173///
174/// ```mlir
175/// #accesses = [
176/// affine_map<(d0, d1) -> (0, d1)>,
177/// affine_map<(d0, d1) -> (d0, 0)>,
178/// affine_map<(d0, d1) -> (d0, d1)>
179/// ]
180///
181/// #trait = {
182/// indexing_maps = #accesses,
183/// iterator_types = ["parallel", "parallel"],
184/// library_call = "some_external_fn"
185/// }
186///
187/// func @broadcast_test(%arg0 : tensor<5xf32>, %arg1 : tensor<5xf32>) ->
188/// tensor<5x5xf32>
189/// {
190/// %0 = linalg.tensor_reshape %arg0 [affine_map<(d0, d1) -> (d0, d1)>] :
191/// tensor<5xf32> into tensor<1x5xf32>
192/// %1 = linalg.tensor_reshape %arg1 [affine_map<(d0, d1) -> (d0, d1)>] :
193/// tensor<5xf32> into tensor<5x1xf32>
194/// %2 = linalg.generic #trait %0, %1 {
195/// ^bb0(%arg2: f32, %arg3: f32):
196/// %3 = arith.addf %arg2, %arg3 : f32
197/// linalg.yield %3 : f32
198/// } : tensor<1x5xf32>, tensor<5x1xf32> -> tensor<5x5xf32>
199/// return %2 : tensor<5x5xf32>
200/// }
201///
202/// would canonicalize to
203///
204/// ```mlir
205/// #accesses = [
206/// affine_map<(d0, d1) -> (d1)>,
207/// affine_map<(d0, d1) -> (d0)>,
208/// affine_map<(d0, d1) -> (d0, d1)>
209/// ]
210///
211/// #trait = {
212/// indexing_maps = #accesses,
213/// iterator_types = ["parallel", "parallel"],
214/// library_call = "some_external_fn"
215/// }
216///
217/// func @broadcast_test(%arg0 : tensor<5xf32>, %arg1 : tensor<5xf32>) ->
218/// tensor<5x5xf32>
219/// {
220/// %0 = linalg.generic #trait %arg0, %arg1 {
221/// ^bb0(%arg2: f32, %arg3: f32):
222/// %3 = arith.addf %arg2, %arg3 : f32
223/// linalg.yield %3 : f32
224/// } : tensor<5xf32>, tensor<5xf32> -> tensor<5x5xf32>
225/// return %0 : tensor<5x5xf32>
226/// }
227
228/// Update the index accesses of linalg operations having index semantics.
229static void
230replaceUnitDimIndexOps(GenericOp genericOp,
231 const llvm::SmallDenseSet<unsigned> &unitDims,
232 RewriterBase &rewriter) {
233 for (IndexOp indexOp :
234 llvm::make_early_inc_range(genericOp.getBody()->getOps<IndexOp>())) {
235 OpBuilder::InsertionGuard guard(rewriter);
236 rewriter.setInsertionPoint(indexOp);
237 if (unitDims.count(indexOp.getDim()) != 0) {
238 rewriter.replaceOpWithNewOp<arith::ConstantIndexOp>(indexOp, 0);
239 } else {
240 // Update the dimension of the index operation if needed.
241 unsigned droppedDims = llvm::count_if(
242 unitDims, [&](unsigned dim) { return dim < indexOp.getDim(); });
243 if (droppedDims != 0)
244 rewriter.replaceOpWithNewOp<IndexOp>(indexOp,
245 indexOp.getDim() - droppedDims);
246 }
247 }
248}
249
250FailureOr<Value>
251ControlDropUnitDims::expandValue(RewriterBase &rewriter, Location loc,
252 Value result, Value origDest,
253 ArrayRef<ReassociationIndices> reassociation,
254 const ControlDropUnitDims &control) {
255 // There are no results for memref outputs.
256 auto origResultType = cast<RankedTensorType>(origDest.getType());
257 if (origResultType.getEncoding() != nullptr) {
258 // Do not expand tensors with encoding.
259 return failure();
260 }
261 if (control.rankReductionStrategy ==
263 unsigned rank = origResultType.getRank();
264 SmallVector<OpFoldResult> offsets(rank, rewriter.getIndexAttr(0));
265 SmallVector<OpFoldResult> sizes =
266 tensor::getMixedSizes(rewriter, loc, origDest);
267 SmallVector<OpFoldResult> strides(rank, rewriter.getIndexAttr(1));
268 return rewriter.createOrFold<tensor::InsertSliceOp>(
269 loc, result, origDest, offsets, sizes, strides);
270 }
271
272 assert(control.rankReductionStrategy ==
274 "unknown rank reduction strategy");
275 return tensor::ExpandShapeOp::create(rewriter, loc, origResultType, result,
276 reassociation)
277 .getResult();
278}
279
280FailureOr<Value>
281ControlDropUnitDims::collapseValue(RewriterBase &rewriter, Location loc,
282 Value operand, ArrayRef<int64_t> targetShape,
283 ArrayRef<ReassociationIndices> reassociation,
284 const ControlDropUnitDims &control) {
285 if (auto memrefType = dyn_cast<MemRefType>(operand.getType())) {
286 if (!memrefType.getLayout().isIdentity()) {
287 // Do not collapse memrefs with a non-identity layout.
288 return failure();
289 }
290 if (control.rankReductionStrategy ==
292 FailureOr<Value> rankReducingExtract =
293 memref::SubViewOp::rankReduceIfNeeded(rewriter, loc, operand,
294 targetShape);
295 assert(succeeded(rankReducingExtract) && "not a unit-extent collapse");
296 return *rankReducingExtract;
297 }
298
299 assert(
300 control.rankReductionStrategy ==
302 "unknown rank reduction strategy");
303 MemRefLayoutAttrInterface layout;
304 auto targetType = MemRefType::get(targetShape, memrefType.getElementType(),
305 layout, memrefType.getMemorySpace());
306 return memref::CollapseShapeOp::create(rewriter, loc, targetType, operand,
307 reassociation)
308 .getResult();
309 }
310 if (auto tensorType = dyn_cast<RankedTensorType>(operand.getType())) {
311 if (tensorType.getEncoding() != nullptr) {
312 // Do not collapse tensors with an encoding.
313 return failure();
314 }
315 if (control.rankReductionStrategy ==
317 FailureOr<Value> rankReducingExtract =
318 tensor::ExtractSliceOp::rankReduceIfNeeded(rewriter, loc, operand,
319 targetShape);
320 assert(succeeded(rankReducingExtract) && "not a unit-extent collapse");
321 return *rankReducingExtract;
322 }
323
324 assert(
325 control.rankReductionStrategy ==
327 "unknown rank reduction strategy");
328 auto targetType =
329 RankedTensorType::get(targetShape, tensorType.getElementType());
330 return tensor::CollapseShapeOp::create(rewriter, loc, targetType, operand,
331 reassociation)
332 .getResult();
333 }
334 llvm_unreachable("unsupported operand type");
335}
336
337/// Compute the modified metadata for an operands of operation
338/// whose unit dims are being dropped. Return the new indexing map
339/// to use, the shape of the operand in the replacement op
340/// and the `reassocation` to use to go from original operand shape
341/// to modified operand shape.
348 MLIRContext *context, IndexingMapOpInterface op, OpOperand *opOperand,
349 llvm::SmallDenseMap<unsigned, unsigned> &oldDimsToNewDimsMap,
350 ArrayRef<AffineExpr> dimReplacements) {
352 ReassociationIndices reassociationGroup;
353 SmallVector<AffineExpr> newIndexExprs;
354 AffineMap indexingMap = op.getMatchingIndexingMap(opOperand);
355 SmallVector<int64_t> operandShape = op.getStaticOperandShape(opOperand);
356 ArrayRef<AffineExpr> exprs = indexingMap.getResults();
357
358 auto isUnitDim = [&](unsigned dim) {
359 if (auto dimExpr = dyn_cast<AffineDimExpr>(exprs[dim])) {
360 unsigned oldPosition = dimExpr.getPosition();
361 return !oldDimsToNewDimsMap.count(oldPosition) &&
362 (operandShape[dim] == 1);
363 }
364 // Handle the other case where the shape is 1, and is accessed using a
365 // constant 0.
366 if (operandShape[dim] == 1) {
367 // Use the new expression after replacing dimensions that will be dropped
368 // here to handle cases where an affine expression with multiple
369 // dimensions (e.g., `d0 + d2`) can be simplified to 0 after dropping all
370 // dimensions used in the expression (`d0` and `d2` in this example).
371 AffineExpr newExpr = exprs[dim].replaceDims(dimReplacements);
372 auto constAffineExpr = dyn_cast<AffineConstantExpr>(newExpr);
373 return constAffineExpr && constAffineExpr.getValue() == 0;
374 }
375 return false;
376 };
377
378 unsigned dim = 0;
379 while (dim < operandShape.size() && isUnitDim(dim))
380 reassociationGroup.push_back(dim++);
381 while (dim < operandShape.size()) {
382 assert(!isUnitDim(dim) && "expected non unit-extent");
383 reassociationGroup.push_back(dim);
384 AffineExpr newExpr = exprs[dim].replaceDims(dimReplacements);
385 newIndexExprs.push_back(newExpr);
386 info.targetShape.push_back(operandShape[dim]);
387 ++dim;
388 // Fold all following dimensions that are unit-extent.
389 while (dim < operandShape.size() && isUnitDim(dim)) {
390 reassociationGroup.push_back(dim++);
391 }
392 info.reassociation.push_back(reassociationGroup);
393 reassociationGroup.clear();
394 }
395 info.indexMap =
396 AffineMap::get(oldDimsToNewDimsMap.size(), indexingMap.getNumSymbols(),
397 newIndexExprs, context);
398 return info;
399}
400
401FailureOr<DropUnitDimsResult>
402linalg::dropUnitDims(RewriterBase &rewriter, IndexingMapOpInterface op,
403 const DroppedUnitDimsBuilder &droppedUnitDimsBuilder,
405 auto dpsOp = dyn_cast<DestinationStyleOpInterface>(op.getOperation());
406 if (!dpsOp) {
407 return rewriter.notifyMatchFailure(
408 op, "op should implement DestinationStyleOpInterface");
409 }
410
411 SmallVector<AffineMap> indexingMaps = op.getIndexingMapsArray();
412 if (indexingMaps.empty())
413 return failure();
414
415 // 1. Check if any of the iteration dimensions are unit-trip count. They will
416 // end up being unit-trip count if they are used to index into a unit-dim
417 // tensor/memref.
418 AffineMap invertedMap =
419 inversePermutation(concatAffineMaps(indexingMaps, rewriter.getContext()));
420 if (!invertedMap) {
421 return rewriter.notifyMatchFailure(op,
422 "invalid indexing maps for operation");
423 }
424
425 SmallVector<int64_t> allShapesSizes;
426 for (OpOperand &opOperand : op->getOpOperands())
427 llvm::append_range(allShapesSizes, op.getStaticOperandShape(&opOperand));
428
429 // 1a. Get the allowed list of dimensions to drop from the `options`.
430 SmallVector<unsigned> allowedUnitDims = options.controlFn(op);
431 if (allowedUnitDims.empty()) {
432 return rewriter.notifyMatchFailure(
433 op, "control function returns no allowed unit dims to prune");
434 }
435 llvm::SmallDenseSet<unsigned> unitDimsFilter(allowedUnitDims.begin(),
436 allowedUnitDims.end());
437 llvm::SmallDenseSet<unsigned> unitDims;
438 for (const auto &expr : enumerate(invertedMap.getResults())) {
439 if (AffineDimExpr dimExpr = dyn_cast<AffineDimExpr>(expr.value())) {
440 if (allShapesSizes[dimExpr.getPosition()] == 1 &&
441 unitDimsFilter.count(expr.index()))
442 unitDims.insert(expr.index());
443 }
444 }
445
446 // 2. Compute the new loops of the modified op by dropping the one-trip
447 // count loops.
448 llvm::SmallDenseMap<unsigned, unsigned> oldDimToNewDimMap;
449 SmallVector<AffineExpr> dimReplacements;
450 unsigned newDims = 0;
451 for (auto index : llvm::seq<int64_t>(op.getStaticLoopRanges().size())) {
452 if (unitDims.count(index)) {
453 dimReplacements.push_back(
454 getAffineConstantExpr(0, rewriter.getContext()));
455 } else {
456 oldDimToNewDimMap[index] = newDims;
457 dimReplacements.push_back(
458 getAffineDimExpr(newDims, rewriter.getContext()));
459 newDims++;
460 }
461 }
462
463 // 3. For each of the operands, find the
464 // - modified affine map to use.
465 // - shape of the operands after the unit-dims are dropped.
466 // - the reassociation indices used to convert from the original
467 // operand type to modified operand (needed only when using reshapes
468 // for rank reduction strategy)
469 // Note that the indexing maps might need changing even if there are no
470 // unit dimensions that are dropped to handle cases where `0` is used to
471 // access a unit-extent tensor. Consider moving this out of this specific
472 // transformation as a stand-alone transformation. Kept here right now due
473 // to legacy.
474 SmallVector<AffineMap> newIndexingMaps;
477 SmallVector<bool> collapsed;
478 for (OpOperand &opOperand : op->getOpOperands()) {
479 auto indexingMap = op.getMatchingIndexingMap(&opOperand);
480 auto replacementInfo =
481 dropUnitExtentFromOperandMetadata(rewriter.getContext(), op, &opOperand,
482 oldDimToNewDimMap, dimReplacements);
483 reassociations.push_back(replacementInfo.reassociation);
484 newIndexingMaps.push_back(replacementInfo.indexMap);
485 targetShapes.push_back(replacementInfo.targetShape);
486 collapsed.push_back(!(replacementInfo.indexMap.getNumResults() ==
487 indexingMap.getNumResults()));
488 }
489
490 // Abort if the indexing maps of the result operation are not invertible
491 // (i.e. not legal) or if no dimension was reduced.
492 if (newIndexingMaps == indexingMaps ||
494 concatAffineMaps(newIndexingMaps, rewriter.getContext())))
495 return failure();
496
497 Location loc = op.getLoc();
498 // 4. For each of the operands, collapse the operand to convert
499 // from original shape to shape in the modified operation if needed,
500 // either through use of reshapes or rank-reducing slices as
501 // specified in `options`.
502 // Abort if one of the operands cannot be collapsed.
503 SmallVector<Value> newOperands;
504 for (OpOperand &opOperand : op->getOpOperands()) {
505 int64_t idx = opOperand.getOperandNumber();
506 if (!collapsed[idx]) {
507 newOperands.push_back(opOperand.get());
508 continue;
509 }
510 FailureOr<Value> collapsed =
511 options.collapseFn(rewriter, loc, opOperand.get(), targetShapes[idx],
512 reassociations[idx], options);
513 if (failed(collapsed)) {
514 // Abort if the operand could not be collapsed.
515 return failure();
516 }
517 newOperands.push_back(collapsed.value());
518 }
519
520 IndexingMapOpInterface replacementOp = droppedUnitDimsBuilder(
521 loc, rewriter, op, newOperands, newIndexingMaps, unitDims);
522
523 // 6. If any result type changes, insert a reshape/slice to convert from the
524 // original type to the new type.
525 // Abort the transformation if the result cannot be expanded back to its
526 // original shape.
527 SmallVector<Value> resultReplacements;
528 for (auto [index, result] : llvm::enumerate(replacementOp->getResults())) {
529 unsigned opOperandIndex = index + dpsOp.getNumDpsInputs();
530 Value origDest = dpsOp.getDpsInitOperand(index)->get();
531 if (!collapsed[opOperandIndex]) {
532 resultReplacements.push_back(result);
533 continue;
534 }
535 FailureOr<Value> expanded =
536 options.expandFn(rewriter, loc, result, origDest,
537 reassociations[opOperandIndex], options);
538 if (failed(expanded)) {
539 // Abort if expansion is not successful.
540 return failure();
541 }
542 resultReplacements.push_back(expanded.value());
543 }
544
545 return DropUnitDimsResult{replacementOp, resultReplacements};
546}
547
548FailureOr<DropUnitDimsResult>
549linalg::dropUnitDims(RewriterBase &rewriter, GenericOp genericOp,
551
553 [](Location loc, OpBuilder &b, IndexingMapOpInterface op,
554 ArrayRef<Value> newOperands, ArrayRef<AffineMap> newIndexingMaps,
555 const llvm::SmallDenseSet<unsigned> &droppedDims)
556 -> IndexingMapOpInterface {
557 auto genericOp = cast<GenericOp>(op);
558 // Compute the iterator types of the modified op by dropping the one-trip
559 // count loops.
560 SmallVector<utils::IteratorType> newIteratorTypes;
561 for (auto [index, attr] :
562 llvm::enumerate(genericOp.getIteratorTypesArray())) {
563 if (!droppedDims.count(index))
564 newIteratorTypes.push_back(attr);
565 }
566
567 // Create the `linalg.generic` operation with the new operands,
568 // indexing maps, iterator types and result types.
569 ArrayRef<Value> newInputs =
570 ArrayRef<Value>(newOperands).take_front(genericOp.getNumDpsInputs());
571 ArrayRef<Value> newOutputs =
572 ArrayRef<Value>(newOperands).take_back(genericOp.getNumDpsInits());
573 SmallVector<Type> resultTypes;
574 resultTypes.reserve(genericOp.getNumResults());
575 for (unsigned i : llvm::seq<unsigned>(0, genericOp.getNumResults()))
576 resultTypes.push_back(newOutputs[i].getType());
577 GenericOp replacementOp =
578 GenericOp::create(b, loc, resultTypes, newInputs, newOutputs,
579 newIndexingMaps, newIteratorTypes);
580 b.cloneRegionBefore(genericOp.getRegion(), replacementOp.getRegion(),
581 replacementOp.getRegion().begin());
582 // 5a. Replace `linalg.index` operations that refer to the dropped unit
583 // dimensions.
584 IRRewriter rewriter(b);
585 replaceUnitDimIndexOps(replacementOp, droppedDims, rewriter);
586
587 return replacementOp;
588 };
589
590 return dropUnitDims(rewriter, genericOp, build, options);
591}
592
593namespace {
594struct DropUnitDims : public OpRewritePattern<GenericOp> {
595 DropUnitDims(MLIRContext *context, ControlDropUnitDims options = {},
596 PatternBenefit benefit = 1)
597 : OpRewritePattern(context, benefit), options(std::move(options)) {}
598
599 LogicalResult matchAndRewrite(GenericOp genericOp,
600 PatternRewriter &rewriter) const override {
601 FailureOr<DropUnitDimsResult> result =
602 dropUnitDims(rewriter, genericOp, options);
603 if (failed(result)) {
604 return failure();
605 }
606 rewriter.replaceOp(genericOp, result->replacements);
607 return success();
608 }
609
610private:
611 ControlDropUnitDims options;
612};
613} // namespace
614
615//===---------------------------------------------------------------------===//
616// Drop dimensions that are unit-extents within tensor operations.
617//===---------------------------------------------------------------------===//
618
619namespace {
620struct DropPadUnitDims : public OpRewritePattern<tensor::PadOp> {
621 DropPadUnitDims(MLIRContext *context, ControlDropUnitDims options = {},
622 PatternBenefit benefit = 1)
623 : OpRewritePattern(context, benefit), options(std::move(options)) {}
624
625 LogicalResult matchAndRewrite(tensor::PadOp padOp,
626 PatternRewriter &rewriter) const override {
627 // 1a. Get the allowed list of dimensions to drop from the `options`.
628 SmallVector<unsigned> allowedUnitDims = options.controlFn(padOp);
629 if (allowedUnitDims.empty()) {
630 return rewriter.notifyMatchFailure(
631 padOp, "control function returns no allowed unit dims to prune");
632 }
633
634 if (padOp.getSourceType().getEncoding()) {
635 return rewriter.notifyMatchFailure(
636 padOp, "cannot collapse dims of tensor with encoding");
637 }
638
639 // Fail for non-constant padding values. The body of the pad could
640 // depend on the padding indices and/or properties of the padded
641 // tensor so for now we fail.
642 // TODO: Support non-constant padding values.
643 Value paddingVal = padOp.getConstantPaddingValue();
644 if (!paddingVal) {
645 return rewriter.notifyMatchFailure(
646 padOp, "unimplemented: non-constant padding value");
647 }
648
649 ArrayRef<int64_t> sourceShape = padOp.getSourceType().getShape();
650 ArrayRef<int64_t> resultShape = padOp.getResultType().getShape();
651 int64_t padRank = sourceShape.size();
652
653 auto isStaticZero = [](OpFoldResult f) {
654 return getConstantIntValue(f) == 0;
655 };
656
657 llvm::SmallDenseSet<unsigned> unitDimsFilter(allowedUnitDims.begin(),
658 allowedUnitDims.end());
659 llvm::SmallDenseSet<unsigned> unitDims;
660 SmallVector<int64_t> newShape;
661 SmallVector<int64_t> newResultShape;
662 SmallVector<OpFoldResult> newLowPad;
663 SmallVector<OpFoldResult> newHighPad;
664 for (const auto [dim, size, outSize, low, high] : zip_equal(
665 llvm::seq(static_cast<int64_t>(0), padRank), sourceShape,
666 resultShape, padOp.getMixedLowPad(), padOp.getMixedHighPad())) {
667 if (unitDimsFilter.contains(dim) && size == 1 && isStaticZero(low) &&
668 isStaticZero(high)) {
669 unitDims.insert(dim);
670 } else {
671 newShape.push_back(size);
672 newResultShape.push_back(outSize);
673 newLowPad.push_back(low);
674 newHighPad.push_back(high);
675 }
676 }
677
678 if (unitDims.empty()) {
679 return rewriter.notifyMatchFailure(padOp, "no unit dims to collapse");
680 }
681
682 ReassociationIndices reassociationGroup;
683 SmallVector<ReassociationIndices> reassociationMap;
684 int64_t dim = 0;
685 while (dim < padRank && unitDims.contains(dim))
686 reassociationGroup.push_back(dim++);
687 while (dim < padRank) {
688 assert(!unitDims.contains(dim) && "expected non unit-extent");
689 reassociationGroup.push_back(dim);
690 dim++;
691 // Fold all following dimensions that are unit-extent.
692 while (dim < padRank && unitDims.contains(dim))
693 reassociationGroup.push_back(dim++);
694 reassociationMap.push_back(reassociationGroup);
695 reassociationGroup.clear();
696 }
697
698 FailureOr<Value> collapsedSource =
699 options.collapseFn(rewriter, padOp.getLoc(), padOp.getSource(),
700 newShape, reassociationMap, options);
701 if (failed(collapsedSource)) {
702 return rewriter.notifyMatchFailure(padOp, "Failed to collapse source");
703 }
704
705 auto newResultType = RankedTensorType::get(
706 newResultShape, padOp.getResultType().getElementType());
707 auto newPadOp = tensor::PadOp::create(
708 rewriter, padOp.getLoc(), /*result=*/newResultType,
709 collapsedSource.value(), newLowPad, newHighPad, paddingVal,
710 padOp.getNofold());
711
712 Value dest = padOp.getResult();
713 if (options.rankReductionStrategy ==
714 ControlDropUnitDims::RankReductionStrategy::ExtractInsertSlice) {
715 SmallVector<OpFoldResult> expandedSizes;
716 int64_t numUnitDims = 0;
717 for (auto dim : llvm::seq(static_cast<int64_t>(0), padRank)) {
718 if (unitDims.contains(dim)) {
719 expandedSizes.push_back(rewriter.getIndexAttr(1));
720 numUnitDims++;
721 continue;
722 }
723 expandedSizes.push_back(tensor::getMixedSize(
724 rewriter, padOp.getLoc(), newPadOp, dim - numUnitDims));
725 }
726 dest = tensor::EmptyOp::create(rewriter, padOp.getLoc(), expandedSizes,
727 padOp.getResultType().getElementType());
728 }
729
730 FailureOr<Value> expandedValue =
731 options.expandFn(rewriter, padOp.getLoc(), newPadOp.getResult(), dest,
732 reassociationMap, options);
733 if (failed(expandedValue)) {
734 return rewriter.notifyMatchFailure(padOp, "Failed to expand result");
735 }
736 rewriter.replaceOp(padOp, expandedValue.value());
737 return success();
738 }
739
740private:
741 ControlDropUnitDims options;
742};
743} // namespace
744
745namespace {
746/// Convert `extract_slice` operations to rank-reduced versions.
747struct RankReducedExtractSliceOp
748 : public OpRewritePattern<tensor::ExtractSliceOp> {
749 using OpRewritePattern<tensor::ExtractSliceOp>::OpRewritePattern;
750
751 LogicalResult matchAndRewrite(tensor::ExtractSliceOp sliceOp,
752 PatternRewriter &rewriter) const override {
753 RankedTensorType resultType = sliceOp.getType();
754 SmallVector<OpFoldResult> targetShape;
755 for (auto size : resultType.getShape())
756 targetShape.push_back(rewriter.getIndexAttr(size));
757 auto reassociation = getReassociationMapForFoldingUnitDims(targetShape);
758 if (!reassociation ||
759 reassociation->size() == static_cast<size_t>(resultType.getRank()))
760 return failure();
761
762 SmallVector<OpFoldResult> offsets = sliceOp.getMixedOffsets();
763 SmallVector<OpFoldResult> strides = sliceOp.getMixedStrides();
764 SmallVector<OpFoldResult> sizes = sliceOp.getMixedSizes();
765 SmallVector<int64_t> staticSizes;
766 std::tie(staticSizes, std::ignore) = decomposeMixedValues(sizes);
767 llvm::SmallBitVector droppedDims = getPositionsOfShapeOne(
768 sizes.size() - reassociation->size(), staticSizes);
769 RankedTensorType rankReducedType =
770 tensor::inferSliceType(sliceOp.getSourceType(), sizes, droppedDims);
771
772 Location loc = sliceOp.getLoc();
773 Value newSlice = tensor::ExtractSliceOp::create(
774 rewriter, loc, rankReducedType, sliceOp.getSource(), offsets, sizes,
775 strides);
776 rewriter.replaceOpWithNewOp<tensor::ExpandShapeOp>(
777 sliceOp, resultType, newSlice, *reassociation);
778 return success();
779 }
780};
781
782/// Convert `insert_slice` operations to rank-reduced versions.
783/// This patterns works with both InsertSliceOp and ParallelInsertSliceOp.
784template <typename InsertOpTy>
785struct RankReducedInsertSliceOp : public OpRewritePattern<InsertOpTy> {
786 using OpRewritePattern<InsertOpTy>::OpRewritePattern;
787
788 LogicalResult matchAndRewrite(InsertOpTy insertSliceOp,
789 PatternRewriter &rewriter) const override {
790 RankedTensorType sourceType = insertSliceOp.getSourceType();
791 SmallVector<OpFoldResult> targetShape;
792 for (auto size : sourceType.getShape())
793 targetShape.push_back(rewriter.getIndexAttr(size));
794 auto reassociation = getReassociationMapForFoldingUnitDims(targetShape);
795 if (!reassociation ||
796 reassociation->size() == static_cast<size_t>(sourceType.getRank()))
797 return failure();
798
799 Location loc = insertSliceOp.getLoc();
800 tensor::CollapseShapeOp reshapedSource;
801 {
802 OpBuilder::InsertionGuard g(rewriter);
803 // The only difference between InsertSliceOp and ParallelInsertSliceOp
804 // is the insertion point is just before the ParallelCombiningOp in the
805 // parallel case.
806 if (std::is_same<InsertOpTy, tensor::ParallelInsertSliceOp>::value)
807 rewriter.setInsertionPoint(insertSliceOp->getParentOp());
808 reshapedSource = tensor::CollapseShapeOp::create(
809 rewriter, loc, insertSliceOp.getSource(), *reassociation);
810 }
811 rewriter.replaceOpWithNewOp<InsertOpTy>(
812 insertSliceOp, reshapedSource, insertSliceOp.getDest(),
813 insertSliceOp.getMixedOffsets(), insertSliceOp.getMixedSizes(),
814 insertSliceOp.getMixedStrides());
815 return success();
816 }
817};
818} // namespace
819
820/// Patterns that are used to canonicalize the use of unit-extent dims for
821/// broadcasting.
824 auto *context = patterns.getContext();
825 patterns.add<DropUnitDims>(context, options);
826 patterns.add<DropPadUnitDims>(context, options);
827}
828
831 auto *context = patterns.getContext();
832 bool reassociativeReshape =
833 options.rankReductionStrategy ==
835 if (reassociativeReshape) {
836 patterns.add<RankReducedExtractSliceOp,
837 RankReducedInsertSliceOp<tensor::InsertSliceOp>,
838 RankReducedInsertSliceOp<tensor::ParallelInsertSliceOp>>(
839 context);
840 tensor::CollapseShapeOp::getCanonicalizationPatterns(patterns, context);
841 tensor::ExpandShapeOp::getCanonicalizationPatterns(patterns, context);
842 }
843 linalg::FillOp::getCanonicalizationPatterns(patterns, context);
844 tensor::EmptyOp::getCanonicalizationPatterns(patterns, context);
848}
849
851 RewritePatternSet &patterns) {
852 patterns.add<MoveInitOperandsToInput>(patterns.getContext());
853}
854
855namespace {
856/// Pass that removes unit-extent dims within generic ops.
857struct LinalgFoldUnitExtentDimsPass
858 : public impl::LinalgFoldUnitExtentDimsPassBase<
859 LinalgFoldUnitExtentDimsPass> {
860 using impl::LinalgFoldUnitExtentDimsPassBase<
861 LinalgFoldUnitExtentDimsPass>::LinalgFoldUnitExtentDimsPassBase;
862 void runOnOperation() override {
863 Operation *op = getOperation();
864 MLIRContext *context = op->getContext();
866 if (useRankReducingSlices) {
867 options.rankReductionStrategy = linalg::ControlDropUnitDims::
868 RankReductionStrategy::ExtractInsertSlice;
869 }
870
871 // Apply fold unit extent dims patterns with walk-based driver.
872 {
873 RewritePatternSet patterns(context);
875 walkAndApplyPatterns(op, std::move(patterns));
876 }
877
878 // Apply canonicalization patterns with greedy driver.
879 {
880 RewritePatternSet patterns(context);
883 options);
884 (void)applyPatternsGreedily(op, std::move(patterns));
885 }
886 }
887};
888
889} // namespace
890
891namespace {
892
893/// Returns reassociation indices for collapsing/expanding a
894/// tensor of rank `rank` at position `pos`.
895static SmallVector<ReassociationIndices>
896getReassociationForReshapeAtDim(int64_t rank, int64_t pos) {
897 SmallVector<ReassociationIndices> reassociation(rank - 1, {0, 1});
898 bool lastDim = pos == rank - 1;
899 if (rank > 2) {
900 for (int64_t i = 0; i < rank - 1; i++) {
901 if (i == pos || (lastDim && i == pos - 1))
902 reassociation[i] = ReassociationIndices{i, i + 1};
903 else if (i < pos)
904 reassociation[i] = ReassociationIndices{i};
905 else
906 reassociation[i] = ReassociationIndices{i + 1};
907 }
908 }
909 return reassociation;
910}
911
912/// Returns a collapsed `val` where the collapsing occurs at dim `pos`.
913/// If `pos < 0`, then don't collapse.
914static Value collapseSingletonDimAt(PatternRewriter &rewriter, Value val,
915 int64_t pos) {
916 if (pos < 0)
917 return val;
918 auto valType = cast<ShapedType>(val.getType());
919 SmallVector<int64_t> collapsedShape(valType.getShape());
920 collapsedShape.erase(collapsedShape.begin() + pos);
921 ControlDropUnitDims control{};
922 FailureOr<Value> collapsed = control.collapseFn(
923 rewriter, val.getLoc(), val, collapsedShape,
924 getReassociationForReshapeAtDim(valType.getRank(), pos), control);
925 assert(llvm::succeeded(collapsed) && "Collapsing the value failed");
926 return collapsed.value();
927}
928
929/// Base class for all rank reduction patterns for contraction ops
930/// with unit dimensions. All patterns should convert one named op
931/// to another named op. Intended to reduce only one iteration space dim
932/// at a time.
933/// Reducing multiple dims will happen with recusive application of
934/// pattern rewrites.
935template <typename FromOpTy, typename ToOpTy>
936struct RankReduceContractionOps : OpRewritePattern<FromOpTy> {
937 using OpRewritePattern<FromOpTy>::OpRewritePattern;
938
939 /// Collapse all collapsable operands.
940 SmallVector<Value>
941 collapseOperands(PatternRewriter &rewriter, ArrayRef<Value> operands,
942 ArrayRef<int64_t> operandCollapseDims) const {
943 assert(operandCollapseDims.size() == 3 && operands.size() == 3 &&
944 "expected 3 operands and dims");
945 return llvm::map_to_vector(
946 llvm::zip(operands, operandCollapseDims), [&](auto pair) {
947 return collapseSingletonDimAt(rewriter, std::get<0>(pair),
948 std::get<1>(pair));
949 });
950 }
951
952 /// Expand result tensor.
953 Value expandResult(PatternRewriter &rewriter, Value result,
954 RankedTensorType expandedType, int64_t dim) const {
955 return tensor::ExpandShapeOp::create(
956 rewriter, result.getLoc(), expandedType, result,
957 getReassociationForReshapeAtDim(expandedType.getRank(), dim));
958 }
959
960 LogicalResult matchAndRewrite(FromOpTy contractionOp,
961 PatternRewriter &rewriter) const override {
962 if (contractionOp.hasUserDefinedMaps()) {
963 return rewriter.notifyMatchFailure(
964 contractionOp, "ops with user-defined maps are not supported");
965 }
966
967 auto loc = contractionOp.getLoc();
968 auto inputs = contractionOp.getDpsInputs();
969 auto inits = contractionOp.getDpsInits();
970 if (inputs.size() != 2 || inits.size() != 1)
971 return rewriter.notifyMatchFailure(contractionOp,
972 "expected 2 inputs and 1 init");
973 auto lhs = inputs[0];
974 auto rhs = inputs[1];
975 auto init = inits[0];
976 SmallVector<Value> operands{lhs, rhs, init};
977
978 SmallVector<int64_t> operandUnitDims;
979 if (failed(getOperandUnitDims(contractionOp, operandUnitDims)))
980 return rewriter.notifyMatchFailure(contractionOp,
981 "no reducable dims found");
982
983 SmallVector<Value> collapsedOperands =
984 collapseOperands(rewriter, operands, operandUnitDims);
985 Value collapsedLhs = collapsedOperands[0];
986 Value collapsedRhs = collapsedOperands[1];
987 Value collapsedInit = collapsedOperands[2];
988 SmallVector<Type, 1> collapsedResultTy;
989 if (isa<RankedTensorType>(collapsedInit.getType()))
990 collapsedResultTy.push_back(collapsedInit.getType());
991 ToOpTy collapsedOp;
992 if constexpr (std::is_same_v<FromOpTy, BatchMatmulOp> &&
993 std::is_same_v<ToOpTy, MatmulOp>) {
994 if (TypeFnAttr castAttr = contractionOp.getCastAttr()) {
995 collapsedOp = ToOpTy::create(rewriter, loc, collapsedResultTy,
996 ValueRange{collapsedLhs, collapsedRhs},
997 ValueRange{collapsedInit}, castAttr);
998 } else {
999 collapsedOp = ToOpTy::create(rewriter, loc, collapsedResultTy,
1000 ValueRange{collapsedLhs, collapsedRhs},
1001 ValueRange{collapsedInit});
1002 }
1003 } else {
1004 collapsedOp = ToOpTy::create(rewriter, loc, collapsedResultTy,
1005 ValueRange{collapsedLhs, collapsedRhs},
1006 ValueRange{collapsedInit});
1007 }
1008 for (auto attr : contractionOp->getDiscardableAttrDictionary()) {
1009 if (attr.getName() == LinalgDialect::kMemoizedIndexingMapsAttrName ||
1010 attr.getName() == "indexing_maps")
1011 continue;
1012 collapsedOp->setDiscardableAttr(attr.getName(), attr.getValue());
1013 }
1014
1015 auto results = contractionOp.getResults();
1016 assert(results.size() < 2 && "expected at most one result");
1017 if (results.empty()) {
1018 rewriter.replaceOp(contractionOp, collapsedOp);
1019 } else {
1020 rewriter.replaceOp(
1021 contractionOp,
1022 expandResult(rewriter, collapsedOp.getResultTensors()[0],
1023 cast<RankedTensorType>(results[0].getType()),
1024 operandUnitDims[2]));
1025 }
1026
1027 return success();
1028 }
1029
1030 /// Populate `operandUnitDims` with 3 indices indicating the unit dim
1031 /// for each operand that should be collapsed in this pattern. If an
1032 /// operand shouldn't be collapsed, the index should be negative.
1033 virtual LogicalResult
1034 getOperandUnitDims(LinalgOp op,
1035 SmallVectorImpl<int64_t> &operandUnitDims) const = 0;
1036};
1037
1038/// Patterns for unbatching batched contraction ops
1039template <typename FromOpTy, typename ToOpTy>
1040struct RankReduceToUnBatched : RankReduceContractionOps<FromOpTy, ToOpTy> {
1041 using RankReduceContractionOps<FromOpTy, ToOpTy>::RankReduceContractionOps;
1042
1043 /// Look for unit batch dims to collapse.
1044 LogicalResult
1045 getOperandUnitDims(LinalgOp op,
1046 SmallVectorImpl<int64_t> &operandUnitDims) const override {
1047 FailureOr<ContractionDimensions> maybeContractionDims =
1049 if (failed(maybeContractionDims)) {
1050 LLVM_DEBUG(llvm::dbgs() << "could not infer contraction dims");
1051 return failure();
1052 }
1053 const ContractionDimensions &contractionDims = maybeContractionDims.value();
1054
1055 if (contractionDims.batch.size() != 1)
1056 return failure();
1057 auto batchDim = contractionDims.batch[0];
1058 SmallVector<std::pair<Value, unsigned>, 3> bOperands;
1059 op.mapIterationSpaceDimToAllOperandDims(batchDim, bOperands);
1060 if (bOperands.size() != 3 || llvm::any_of(bOperands, [](auto pair) {
1061 return cast<ShapedType>(std::get<0>(pair).getType())
1062 .getShape()[std::get<1>(pair)] != 1;
1063 })) {
1064 LLVM_DEBUG(llvm::dbgs() << "specified unit dims not found");
1065 return failure();
1066 }
1067
1068 operandUnitDims = SmallVector<int64_t>{std::get<1>(bOperands[0]),
1069 std::get<1>(bOperands[1]),
1070 std::get<1>(bOperands[2])};
1071 return success();
1072 }
1073};
1074
1075/// Patterns for reducing non-batch dimensions
1076template <typename FromOpTy, typename ToOpTy>
1077struct RankReduceMatmul : RankReduceContractionOps<FromOpTy, ToOpTy> {
1078 using RankReduceContractionOps<FromOpTy, ToOpTy>::RankReduceContractionOps;
1079
1080 /// Helper for determining whether the lhs/init or rhs/init are reduced.
1081 static bool constexpr reduceLeft =
1082 (std::is_same_v<FromOpTy, BatchMatmulOp> &&
1083 std::is_same_v<ToOpTy, BatchVecmatOp>) ||
1084 (std::is_same_v<FromOpTy, MatmulOp> &&
1085 std::is_same_v<ToOpTy, VecmatOp>) ||
1086 (std::is_same_v<FromOpTy, MatvecOp> && std::is_same_v<ToOpTy, DotOp>);
1087
1088 /// Look for non-batch spatial dims to collapse.
1089 LogicalResult
1090 getOperandUnitDims(LinalgOp op,
1091 SmallVectorImpl<int64_t> &operandUnitDims) const override {
1092 FailureOr<ContractionDimensions> maybeContractionDims =
1094 if (failed(maybeContractionDims)) {
1095 LLVM_DEBUG(llvm::dbgs() << "could not infer contraction dims");
1096 return failure();
1097 }
1098 const ContractionDimensions &contractionDims = maybeContractionDims.value();
1099
1100 if constexpr (reduceLeft) {
1101 auto m = contractionDims.m[0];
1102 SmallVector<std::pair<Value, unsigned>, 2> mOperands;
1103 op.mapIterationSpaceDimToAllOperandDims(m, mOperands);
1104 if (mOperands.size() != 2)
1105 return failure();
1106 if (llvm::all_of(mOperands, [](auto pair) {
1107 return cast<ShapedType>(std::get<0>(pair).getType())
1108 .getShape()[std::get<1>(pair)] == 1;
1109 })) {
1110 operandUnitDims = SmallVector<int64_t>{std::get<1>(mOperands[0]), -1,
1111 std::get<1>(mOperands[1])};
1112 return success();
1113 }
1114 } else {
1115 auto n = contractionDims.n[0];
1116 SmallVector<std::pair<Value, unsigned>, 2> nOperands;
1117 op.mapIterationSpaceDimToAllOperandDims(n, nOperands);
1118 if (nOperands.size() != 2)
1119 return failure();
1120 if (llvm::all_of(nOperands, [](auto pair) {
1121 return cast<ShapedType>(std::get<0>(pair).getType())
1122 .getShape()[std::get<1>(pair)] == 1;
1123 })) {
1124 operandUnitDims = SmallVector<int64_t>{-1, std::get<1>(nOperands[0]),
1125 std::get<1>(nOperands[1])};
1126 return success();
1127 }
1128 }
1129 LLVM_DEBUG(llvm::dbgs() << "specified unit dims not found");
1130 return failure();
1131 }
1132};
1133
1134} // namespace
1135
1137 RewritePatternSet &patterns) {
1138 MLIRContext *context = patterns.getContext();
1139 // Unbatching patterns for unit batch size
1140 patterns.add<RankReduceToUnBatched<BatchMatmulOp, MatmulOp>>(context);
1141 patterns.add<RankReduceToUnBatched<BatchMatvecOp, MatvecOp>>(context);
1142 patterns.add<RankReduceToUnBatched<BatchVecmatOp, VecmatOp>>(context);
1143
1144 // Non-batch rank 1 reducing patterns
1145 patterns.add<RankReduceMatmul<MatmulOp, VecmatOp>>(context);
1146 patterns.add<RankReduceMatmul<MatmulOp, MatvecOp>>(context);
1147 // Batch rank 1 reducing patterns
1148 patterns.add<RankReduceMatmul<BatchMatmulOp, BatchVecmatOp>>(context);
1149 patterns.add<RankReduceMatmul<BatchMatmulOp, BatchMatvecOp>>(context);
1150
1151 // Non-batch rank 0 reducing patterns
1152 patterns.add<RankReduceMatmul<MatvecOp, DotOp>>(context);
1153 patterns.add<RankReduceMatmul<VecmatOp, DotOp>>(context);
1154}
return success()
static void replaceUnitDimIndexOps(GenericOp genericOp, const llvm::SmallDenseSet< unsigned > &unitDims, RewriterBase &rewriter)
Implements a pass that canonicalizes the uses of unit-extent dimensions for broadcasting.
static UnitExtentReplacementInfo dropUnitExtentFromOperandMetadata(MLIRContext *context, IndexingMapOpInterface op, OpOperand *opOperand, llvm::SmallDenseMap< unsigned, unsigned > &oldDimsToNewDimsMap, ArrayRef< AffineExpr > dimReplacements)
lhs
b
Return true if permutation is a valid permutation of the outer_dims_perm (case OuterOrInnerPerm::Oute...
static llvm::ManagedStatic< PassManagerOptions > options
A dimensional identifier appearing in an affine expression.
Definition AffineExpr.h:223
Base type for affine expression.
Definition AffineExpr.h:68
A multi-dimensional affine map Affine map's are immutable like Type's, and they are uniqued.
Definition AffineMap.h:46
static AffineMap get(MLIRContext *context)
Returns a zero result affine map with no dimensions or symbols: () -> ().
unsigned getNumSymbols() const
ArrayRef< AffineExpr > getResults() const
This class represents an argument of a Block.
Definition Value.h:306
Block represents an ordered list of Operations.
Definition Block.h:33
BlockArgument addArgument(Type type, Location loc)
Add one value to the argument list.
Definition Block.cpp:158
IntegerAttr getIndexAttr(int64_t value)
Definition Builders.cpp:116
MLIRContext * getContext() const
Definition Builders.h:56
This is a utility class for mapping one set of IR entities to another.
Definition IRMapping.h:26
void map(Value from, Value to)
Inserts a new mapping for 'from' to 'to'.
Definition IRMapping.h:30
This class coordinates rewriting a piece of IR outside of a pattern rewrite, providing a way to keep ...
This class defines the main interface for locations in MLIR and acts as a non-nullable wrapper around...
Definition Location.h:76
MLIRContext is the top-level object for a collection of MLIR operations.
Definition MLIRContext.h:63
RAII guard to reset the insertion point of the builder when destroyed.
Definition Builders.h:351
This class helps build Operations.
Definition Builders.h:210
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.
Definition Builders.cpp:439
Operation * clone(Operation &op, IRMapping &mapper)
Creates a deep copy of the specified operation, remapping any operands that use values outside of the...
Definition Builders.cpp:581
void setInsertionPoint(Block *block, Block::iterator insertPoint)
Set the insertion point to the specified location.
Definition Builders.h:401
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...
Definition Builders.h:528
void setInsertionPointAfterValue(Value val)
Sets the insertion point to the node after the specified value.
Definition Builders.h:424
This class represents an operand of an operation.
Definition Value.h:254
Operation is the basic unit of execution within MLIR.
Definition Operation.h:87
MLIRContext * getContext()
Return the context this operation is associated with.
Definition Operation.h:233
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.
Definition Region.h:26
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...
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,...
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 represents an instance of an SSA value in the MLIR system, representing a computable value...
Definition Value.h:96
Type getType() const
Return the type of this value.
Definition Value.h:105
Location getLoc() const
Return the location of this value.
Definition Value.cpp:24
Specialization of arith.constant op that returns an integer of index type.
Definition Arith.h:93
void populateMoveInitOperandsToInputPattern(RewritePatternSet &patterns)
A pattern that converts init operands to input operands.
std::function< IndexingMapOpInterface( Location loc, OpBuilder &, IndexingMapOpInterface, ArrayRef< Value > newOperands, ArrayRef< AffineMap > newIndexingMaps, const llvm::SmallDenseSet< unsigned > &droppedDims)> DroppedUnitDimsBuilder
Definition Transforms.h:629
void populateContractionOpRankReducingPatterns(RewritePatternSet &patterns)
Adds patterns that reduce the rank of named contraction ops that have unit dimensions in the operand(...
std::optional< SmallVector< ReassociationIndices > > getReassociationMapForFoldingUnitDims(ArrayRef< OpFoldResult > mixedSizes)
Get the reassociation maps to fold the result of a extract_slice (or source of a insert_slice) operat...
Definition Utils.cpp:2908
void populateFoldUnitExtentDimsPatterns(RewritePatternSet &patterns, ControlDropUnitDims &options)
Patterns to fold unit-extent dimensions in operands/results of linalg ops on tensors and memref.
FailureOr< ContractionDimensions > inferContractionDims(LinalgOp linalgOp)
Find at least 2 parallel (m and n) and 1 reduction (k) dimension candidates that form a matmul subcom...
FailureOr< DropUnitDimsResult > dropUnitDims(RewriterBase &rewriter, IndexingMapOpInterface op, const DroppedUnitDimsBuilder &droppedUnitDimsBuilder, const ControlDropUnitDims &options)
Drop unit extent dimensions from the op and its operands.
SmallVector< NamedAttribute > getPrunedAttributeList(OpTy op)
Returns an attribute list that excludes pre-defined attributes.
Definition Utils.h:402
void populateFoldUnitExtentDimsCanonicalizationPatterns(RewritePatternSet &patterns, ControlDropUnitDims &options)
Populates canonicalization patterns that simplify IR after folding unit-extent dimensions.
void populateResolveRankedShapedTypeResultDimsPatterns(RewritePatternSet &patterns)
Appends patterns that resolve memref.dim operations with values that are defined by operations that i...
void populateResolveShapedTypeResultDimsPatterns(RewritePatternSet &patterns)
Appends patterns that resolve memref.dim operations with values that are defined by operations that i...
detail::InFlightRemark failed(Location loc, RemarkOpts opts)
Report an optimization remark that failed.
Definition Remarks.h:732
void populateFoldTensorEmptyPatterns(RewritePatternSet &patterns, bool foldSingleUseOnly=false)
Populates patterns with patterns that fold tensor.empty with its consumers.
RankedTensorType inferSliceType(RankedTensorType sourceTensorType, ArrayRef< int64_t > staticSizes, const llvm::SmallBitVector &droppedDims)
Infer a slice type for the given sizes and exact dropped-dimension mask.
OpFoldResult getMixedSize(OpBuilder &builder, Location loc, Value value, int64_t dim)
Return the dimension of the given tensor value.
Definition TensorOps.cpp:82
SmallVector< OpFoldResult > getMixedSizes(OpBuilder &builder, Location loc, Value value)
Return the dimensions of the given tensor value.
Definition TensorOps.cpp:91
Include the generated interface declarations.
AffineMap concatAffineMaps(ArrayRef< AffineMap > maps, MLIRContext *context)
Concatenates a list of maps into a single AffineMap, stepping over potentially empty maps.
std::optional< int64_t > getConstantIntValue(OpFoldResult ofr)
If ofr is a constant integer or an IntegerAttr, return the integer.
Type getType(OpFoldResult ofr)
Returns the int type of the integer in ofr.
Definition Utils.cpp:310
LogicalResult applyPatternsGreedily(Region &region, 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...
llvm::SetVector< T, Vector, Set, N > SetVector
Definition LLVM.h:125
AffineExpr getAffineConstantExpr(int64_t constant, MLIRContext *context)
void walkAndApplyPatterns(Operation *op, const FrozenRewritePatternSet &patterns, RewriterBase::Listener *listener=nullptr)
A fast walk-based pattern rewrite driver.
llvm::SmallBitVector getPositionsOfShapeOne(unsigned rank, ArrayRef< int64_t > shape)
Definition Utils.cpp:93
SmallVector< int64_t, 2 > ReassociationIndices
Definition Utils.h:27
AffineExpr getAffineDimExpr(unsigned position, MLIRContext *context)
These free functions allow clients of the API to not use classes in detail.
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.
Compute the modified metadata for an operands of operation whose unit dims are being dropped.
SmallVector< ReassociationIndices > reassociation
SmallVector< int64_t > targetShape
OpRewritePattern is a wrapper around RewritePattern that allows for matching and rewriting against an...
SmallVector< unsigned, 2 > batch
Transformation to drop unit-extent dimensions from linalg.generic operations.
Definition Transforms.h:521
RankReductionStrategy rankReductionStrategy
Definition Transforms.h:524
CollapseFnTy collapseFn
Function to control how operands are collapsed into their new target shape after dropping unit extent...
Definition Transforms.h:568