18#include "llvm/ADT/MapVector.h"
19#include "llvm/ADT/STLExtras.h"
20#include "llvm/Support/DebugLog.h"
21#include "llvm/Support/InterleavedRange.h"
24#define DEBUG_TYPE "vector-unroll"
34 if (
auto constExpr = dyn_cast<AffineConstantExpr>(expr))
35 return constExpr.getValue() == 0;
40 for (
const auto &dim : llvm::enumerate(permutationMap.
getResults())) {
41 int64_t elementOffset = elementOffsets[dim.index()];
42 if (isBroadcast(dim.value()) || elementOffset == 0)
44 unsigned pos = cast<AffineDimExpr>(dim.value()).getPosition();
49 affine::AffineApplyOp::create(builder, loc, map,
indices[pos]);
61 assert(offsets.size() <= originalIndices.size() &&
62 "Offsets should not exceed the number of original indices");
65 auto start =
indices.size() - offsets.size();
66 for (
auto [i, offset] : llvm::enumerate(offsets)) {
68 indices[start + i] = arith::AddIOp::create(
69 rewriter, loc, originalIndices[start + i],
85 return builder.
create(state);
90static std::optional<SmallVector<int64_t>>
93 if (
options.filterConstraint && failed(
options.filterConstraint(op))) {
94 LDBG() <<
"--no filter constraint -> BAIL";
98 "vector unrolling expects the native shape or native"
99 "shape call back function to be set");
100 auto unrollableVectorOp = dyn_cast<VectorUnrollOpInterface>(op);
101 if (!unrollableVectorOp) {
102 LDBG() <<
"--not an unrollable op -> BAIL";
105 auto maybeUnrollShape = unrollableVectorOp.getShapeForUnroll();
106 if (!maybeUnrollShape) {
107 LDBG() <<
"--could not get shape of op " << *op <<
" -> BAIL";
110 LDBG() <<
"--vector op shape: " << llvm::interleaved(*maybeUnrollShape);
112 std::optional<SmallVector<int64_t>> targetShape =
options.nativeShape(op);
114 LDBG() <<
"--no unrolling target shape defined " << *op <<
"-> SKIP";
117 LDBG() <<
"--target shape: " << llvm::interleaved(*targetShape);
120 if (!maybeShapeRatio) {
121 LDBG() <<
"--could not compute integral shape ratio -> BAIL";
124 if (llvm::all_of(*maybeShapeRatio, [](
int64_t v) {
return v == 1; })) {
125 LDBG() <<
"--no unrolling needed -> SKIP";
128 LDBG() <<
"--found an integral shape ratio to unroll to -> SUCCESS";
136 llvm::to_vector(llvm::seq<int64_t>(0,
static_cast<int64_t>(numLoops)));
137 if (
options.traversalOrderCallback !=
nullptr) {
138 std::optional<SmallVector<int64_t>> order =
139 options.traversalOrderCallback(op);
141 loopOrder = std::move(*order);
149struct UnrollTransferReadPattern
151 UnrollTransferReadPattern(MLIRContext *context,
152 const vector::UnrollVectorOptions &options,
153 PatternBenefit benefit = 1)
154 : OpRewritePattern<vector::TransferReadOp>(context, benefit),
157 LogicalResult matchAndRewrite(vector::TransferReadOp readOp,
158 PatternRewriter &rewriter)
const override {
160 if (readOp.getTransferRank() == 0)
162 if (readOp.getMask())
167 auto sourceVectorType = readOp.getVectorType();
168 SmallVector<int64_t> strides(targetShape->size(), 1);
169 Location loc = readOp.getLoc();
170 ArrayRef<int64_t> originalSize = sourceVectorType.getShape();
174 arith::ConstantOp::create(rewriter, loc, sourceVectorType,
177 VectorType::get(*targetShape, sourceVectorType.getElementType());
178 SmallVector<Value> originalIndices(readOp.getIndices().begin(),
179 readOp.getIndices().end());
180 SmallVector<int64_t> loopOrder =
182 for (SmallVector<int64_t> elementOffsets :
183 StaticTileOffsetRange(originalSize, *targetShape, loopOrder)) {
185 sliceTransferIndices(elementOffsets, originalIndices,
186 readOp.getPermutationMap(), loc, rewriter);
187 auto slicedRead = vector::TransferReadOp::create(
188 rewriter, loc, targetType, readOp.getBase(),
indices,
189 readOp.getPermutationMapAttr(), readOp.getPadding(), readOp.getMask(),
190 readOp.getInBoundsAttr());
193 loc, slicedRead,
result, elementOffsets, strides);
200 vector::UnrollVectorOptions options;
203struct UnrollTransferWritePattern
205 UnrollTransferWritePattern(MLIRContext *context,
206 const vector::UnrollVectorOptions &options,
207 PatternBenefit benefit = 1)
208 : OpRewritePattern<vector::TransferWriteOp>(context, benefit),
211 LogicalResult matchAndRewrite(vector::TransferWriteOp writeOp,
212 PatternRewriter &rewriter)
const override {
214 if (writeOp.getTransferRank() == 0)
217 if (writeOp.getMask())
222 auto sourceVectorType = writeOp.getVectorType();
223 SmallVector<int64_t> strides(targetShape->size(), 1);
224 Location loc = writeOp.getLoc();
225 ArrayRef<int64_t> originalSize = sourceVectorType.getShape();
229 if (originalSize.size() != targetShape->size())
232 "expected source input vector rank to match target shape rank");
234 SmallVector<Value> originalIndices(writeOp.getIndices().begin(),
235 writeOp.getIndices().end());
236 SmallVector<int64_t> loopOrder =
239 for (SmallVector<int64_t> elementOffsets :
240 StaticTileOffsetRange(originalSize, *targetShape, loopOrder)) {
241 Value slicedVector = rewriter.
createOrFold<vector::ExtractStridedSliceOp>(
242 loc, writeOp.getVector(), elementOffsets, *targetShape, strides);
244 sliceTransferIndices(elementOffsets, originalIndices,
245 writeOp.getPermutationMap(), loc, rewriter);
246 Operation *slicedWrite = vector::TransferWriteOp::create(
247 rewriter, loc, slicedVector,
248 resultTensor ? resultTensor : writeOp.getBase(),
indices,
249 writeOp.getPermutationMapAttr(), writeOp.getInBoundsAttr());
252 resultTensor = slicedWrite->
getResult(0);
255 rewriter.
replaceOp(writeOp, resultTensor);
262 vector::UnrollVectorOptions options;
265struct OffsetMapInfo {
266 static unsigned getHashValue(
const SmallVector<int64_t> &v) {
267 return static_cast<unsigned>(llvm::hash_combine_range(v));
270 static bool isEqual(
const SmallVector<int64_t> &
lhs,
271 const SmallVector<int64_t> &
rhs) {
276struct UnrollContractionPattern
278 UnrollContractionPattern(MLIRContext *context,
279 const vector::UnrollVectorOptions &options,
280 PatternBenefit benefit = 1)
281 : OpRewritePattern<vector::ContractionOp>(context, benefit),
284 LogicalResult matchAndRewrite(vector::ContractionOp contractOp,
285 PatternRewriter &rewriter)
const override {
289 auto dstVecType = cast<VectorType>(contractOp.getResultType());
290 SmallVector<int64_t> originalSize = *contractOp.getShapeForUnroll();
292 Location loc = contractOp.getLoc();
293 unsigned accIndex = vector::ContractionOp::getAccOperandIndex();
294 AffineMap dstAffineMap = contractOp.getIndexingMapsArray()[accIndex];
296 SmallVector<int64_t>, Value,
297 llvm::DenseMap<SmallVector<int64_t>, unsigned, OffsetMapInfo>>
301 contractOp.getIteratorTypes().size(), contractOp, options);
303 for (SmallVector<int64_t> offsets :
304 StaticTileOffsetRange(originalSize, *targetShape, loopOrder)) {
305 SmallVector<Value> slicesOperands(contractOp.getNumOperands());
308 auto extractOperand = [&](
unsigned index, Value operand,
309 AffineMap permutationMap,
310 ArrayRef<int64_t> operandOffets) {
312 permutationMap, ArrayRef<int64_t>(*targetShape));
313 SmallVector<int64_t> operandStrides(operandOffets.size(), 1);
314 slicesOperands[index] =
316 loc, operand, operandOffets, operandShape, operandStrides);
320 AffineMap lhsPermutationMap = contractOp.getIndexingMapsArray()[0];
321 SmallVector<int64_t> lhsOffets =
323 extractOperand(0, contractOp.getLhs(), lhsPermutationMap, lhsOffets);
326 AffineMap rhsPermutationMap = contractOp.getIndexingMapsArray()[1];
327 SmallVector<int64_t> rhsOffets =
329 extractOperand(1, contractOp.getRhs(), rhsPermutationMap, rhsOffets);
331 AffineMap accPermutationMap = contractOp.getIndexingMapsArray()[2];
332 SmallVector<int64_t> accOffets =
336 auto *accIt = accCache.find(accOffets);
337 if (accIt != accCache.end())
338 slicesOperands[2] = accIt->second;
340 extractOperand(2, contractOp.getAcc(), accPermutationMap, accOffets);
342 SmallVector<int64_t> dstShape =
344 auto targetType = VectorType::get(dstShape, dstVecType.getElementType());
346 rewriter, loc, contractOp, slicesOperands, targetType);
348 SmallVector<int64_t> dstOffets =
352 accCache[dstOffets] = newOp->
getResult(0);
355 Value
result = arith::ConstantOp::create(rewriter, loc, dstVecType,
357 for (
const auto &it : accCache) {
358 SmallVector<int64_t> dstStrides(it.first.size(), 1);
360 loc, it.second,
result, it.first, dstStrides);
367 vector::UnrollVectorOptions options;
370struct UnrollMultiReductionPattern
372 UnrollMultiReductionPattern(MLIRContext *context,
373 const vector::UnrollVectorOptions &options,
374 PatternBenefit benefit = 1)
375 : OpRewritePattern<vector::MultiDimReductionOp>(context, benefit),
378 LogicalResult matchAndRewrite(vector::MultiDimReductionOp reductionOp,
379 PatternRewriter &rewriter)
const override {
380 std::optional<SmallVector<int64_t>> targetShape =
384 SmallVector<int64_t> originalSize = *reductionOp.getShapeForUnroll();
385 Location loc = reductionOp.getLoc();
386 auto resultType = reductionOp->getResult(0).getType();
391 SmallVector<int64_t> adjustedTargetShape(originalSize.size(), 1);
392 llvm::copy(*targetShape, adjustedTargetShape.end() - targetShape->size());
397 if (resultType.isIntOrFloat()) {
398 Value accumulator = reductionOp.getAcc();
399 for (SmallVector<int64_t> offsets :
400 StaticTileOffsetRange(originalSize, adjustedTargetShape)) {
401 SmallVector<int64_t> operandStrides(offsets.size(), 1);
402 Value slicedOperand =
404 loc, reductionOp.getSource(), offsets, adjustedTargetShape,
407 rewriter, loc, reductionOp, {slicedOperand, accumulator},
411 rewriter.
replaceOp(reductionOp, accumulator);
417 SmallVector<int64_t>, Value,
418 llvm::DenseMap<SmallVector<int64_t>, unsigned, OffsetMapInfo>>
423 for (SmallVector<int64_t> offsets :
424 StaticTileOffsetRange(originalSize, adjustedTargetShape)) {
425 SmallVector<Value> operands;
426 SmallVector<int64_t> operandStrides(offsets.size(), 1);
427 Value slicedOperand =
429 loc, reductionOp.getSource(), offsets, adjustedTargetShape,
431 operands.push_back(slicedOperand);
432 SmallVector<int64_t> dstShape;
433 SmallVector<int64_t> destOffset;
434 for (
size_t i : llvm::seq(
size_t(0), adjustedTargetShape.size())) {
435 if (!reductionOp.isReducedDim(i)) {
436 destOffset.push_back(offsets[i]);
437 dstShape.push_back(adjustedTargetShape[i]);
441 SmallVector<int64_t> accStrides(destOffset.size(), 1);
444 auto *accIt = accCache.find(destOffset);
445 if (accIt != accCache.end())
448 acc = rewriter.
createOrFold<vector::ExtractStridedSliceOp>(
449 loc, reductionOp.getAcc(), destOffset, dstShape, accStrides);
450 operands.push_back(acc);
451 auto targetType = VectorType::get(
452 dstShape, reductionOp.getSourceVectorType().getElementType());
454 operands, targetType);
456 accCache[destOffset] =
result;
459 Value
result = arith::ConstantOp::create(
460 rewriter, loc, reductionOp.getDestType(),
462 for (
const auto &it : accCache) {
463 SmallVector<int64_t> dstStrides(it.first.size(), 1);
465 loc, it.second,
result, it.first, dstStrides);
472 vector::UnrollVectorOptions options;
476 UnrollElementwisePattern(MLIRContext *context,
477 const vector::UnrollVectorOptions &options,
478 PatternBenefit benefit = 1)
479 : RewritePattern(MatchAnyOpTypeTag(), benefit, context),
482 LogicalResult matchAndRewrite(Operation *op,
483 PatternRewriter &rewriter)
const override {
489 int64_t targetShapeRank = targetShape->size();
491 SmallVector<int64_t> originalSize =
492 *cast<VectorUnrollOpInterface>(op).getShapeForUnroll();
493 int64_t originalShapeRank = originalSize.size();
495 Location loc = op->
getLoc();
498 SmallVector<int64_t> adjustedTargetShape(originalShapeRank);
499 int64_t rankDiff = originalShapeRank - targetShapeRank;
500 std::fill(adjustedTargetShape.begin(),
501 adjustedTargetShape.begin() + rankDiff, 1);
502 std::copy(targetShape->begin(), targetShape->end(),
503 adjustedTargetShape.begin() + rankDiff);
505 int64_t adjustedTargetShapeRank = adjustedTargetShape.size();
507 Value
result = arith::ConstantOp::create(rewriter, loc, dstVecType,
509 SmallVector<int64_t> strides(adjustedTargetShapeRank, 1);
510 VectorType unrolledVecType =
511 VectorType::get(*targetShape, dstVecType.getElementType());
514 for (SmallVector<int64_t> offsets :
515 StaticTileOffsetRange(originalSize, adjustedTargetShape)) {
516 SmallVector<Value> extractOperands;
518 auto vecType = dyn_cast<VectorType>(operand.get().getType());
520 extractOperands.push_back(operand.get());
523 Value extracted = rewriter.
createOrFold<vector::ExtractStridedSliceOp>(
524 loc, operand.get(), offsets, adjustedTargetShape, strides);
527 if (adjustedTargetShapeRank > targetShapeRank) {
529 loc, VectorType::get(*targetShape, vecType.getElementType()),
532 extractOperands.push_back(extracted);
536 rewriter, loc, op, extractOperands, unrolledVecType);
538 Value computeResult = newOp->
getResult(0);
541 SmallVector<int64_t> insertStrides =
542 (adjustedTargetShapeRank > targetShapeRank)
543 ? SmallVector<int64_t>(targetShapeRank, 1)
547 loc, computeResult,
result, offsets, insertStrides);
554 vector::UnrollVectorOptions options;
557struct UnrollReductionPattern :
public OpRewritePattern<vector::ReductionOp> {
558 UnrollReductionPattern(MLIRContext *context,
559 const vector::UnrollVectorOptions &options,
560 PatternBenefit benefit = 1)
561 : OpRewritePattern<vector::ReductionOp>(context, benefit),
564 LogicalResult matchAndRewrite(vector::ReductionOp reductionOp,
565 PatternRewriter &rewriter)
const override {
566 std::optional<SmallVector<int64_t>> targetShape =
570 SmallVector<int64_t> originalSize = *reductionOp.getShapeForUnroll();
573 Location loc = reductionOp.getLoc();
574 Value accumulator =
nullptr;
575 for (SmallVector<int64_t> offsets :
576 StaticTileOffsetRange(originalSize, *targetShape)) {
577 SmallVector<int64_t> strides(offsets.size(), 1);
578 Value slicedOperand =
580 loc, reductionOp.getVector(), offsets, *targetShape, strides);
582 rewriter, loc, reductionOp, slicedOperand, reductionOp.getType());
595 rewriter.
replaceOp(reductionOp, accumulator);
600 const vector::UnrollVectorOptions options;
603struct UnrollTransposePattern :
public OpRewritePattern<vector::TransposeOp> {
604 UnrollTransposePattern(MLIRContext *context,
605 const vector::UnrollVectorOptions &options,
606 PatternBenefit benefit = 1)
607 : OpRewritePattern<vector::TransposeOp>(context, benefit),
610 LogicalResult matchAndRewrite(vector::TransposeOp transposeOp,
611 PatternRewriter &rewriter)
const override {
612 if (transposeOp.getResultVectorType().getRank() == 0)
617 auto originalVectorType = transposeOp.getResultVectorType();
618 SmallVector<int64_t> strides(targetShape->size(), 1);
619 Location loc = transposeOp.getLoc();
620 ArrayRef<int64_t> originalSize = originalVectorType.getShape();
624 arith::ConstantOp::create(rewriter, loc, originalVectorType,
626 ArrayRef<int64_t> permutation = transposeOp.getPermutation();
629 for (SmallVector<int64_t> elementOffsets :
630 StaticTileOffsetRange(originalSize, *targetShape)) {
631 SmallVector<int64_t> permutedOffsets(elementOffsets.size());
632 SmallVector<int64_t> permutedShape(elementOffsets.size());
634 for (
auto indices : llvm::enumerate(permutation)) {
635 permutedOffsets[
indices.value()] = elementOffsets[
indices.index()];
638 Value slicedOperand =
640 loc, transposeOp.getVector(), permutedOffsets, permutedShape,
642 Value transposedSlice = rewriter.
createOrFold<vector::TransposeOp>(
643 loc, slicedOperand, permutation);
645 loc, transposedSlice,
result, elementOffsets, strides);
652 vector::UnrollVectorOptions options;
656 UnrollGatherPattern(MLIRContext *context,
657 const vector::UnrollVectorOptions &options,
658 PatternBenefit benefit = 1)
659 : OpRewritePattern<vector::GatherOp>(context, benefit), options(options) {
662 LogicalResult matchAndRewrite(vector::GatherOp gatherOp,
663 PatternRewriter &rewriter)
const override {
664 VectorType sourceVectorType = gatherOp.getVectorType();
665 if (sourceVectorType.getRank() == 0)
670 SmallVector<int64_t> strides(targetShape->size(), 1);
671 Location loc = gatherOp.getLoc();
672 ArrayRef<int64_t> originalSize = gatherOp.getVectorType().getShape();
676 arith::ConstantOp::create(rewriter, loc, sourceVectorType,
679 VectorType::get(*targetShape, sourceVectorType.getElementType());
681 SmallVector<int64_t> loopOrder =
683 for (SmallVector<int64_t> elementOffsets :
684 StaticTileOffsetRange(originalSize, *targetShape, loopOrder)) {
688 Value indexSubVec = rewriter.
createOrFold<vector::ExtractStridedSliceOp>(
689 loc, gatherOp.getIndices(), elementOffsets, *targetShape, strides);
690 Value maskSubVec = rewriter.
createOrFold<vector::ExtractStridedSliceOp>(
691 loc, gatherOp.getMask(), elementOffsets, *targetShape, strides);
692 Value passThruSubVec =
694 loc, gatherOp.getPassThru(), elementOffsets, *targetShape,
696 auto slicedGather = vector::GatherOp::create(
697 rewriter, loc, targetType, gatherOp.getBase(), gatherOp.getOffsets(),
698 indexSubVec, maskSubVec, passThruSubVec);
701 loc, slicedGather,
result, elementOffsets, strides);
708 vector::UnrollVectorOptions options;
712 UnrollLoadPattern(MLIRContext *context,
713 const vector::UnrollVectorOptions &options,
714 PatternBenefit benefit = 1)
715 : OpRewritePattern<vector::LoadOp>(context, benefit), options(options) {}
717 LogicalResult matchAndRewrite(vector::LoadOp loadOp,
718 PatternRewriter &rewriter)
const override {
719 VectorType vecType = loadOp.getVectorType();
725 Location loc = loadOp.getLoc();
726 ArrayRef<int64_t> originalShape = vecType.getShape();
727 SmallVector<int64_t> strides(targetShape->size(), 1);
729 Value
result = arith::ConstantOp::create(rewriter, loc, vecType,
732 SmallVector<int64_t> loopOrder =
736 VectorType::get(*targetShape, vecType.getElementType());
738 for (SmallVector<int64_t> offsets :
739 StaticTileOffsetRange(originalShape, *targetShape, loopOrder)) {
742 Value slicedLoad = vector::LoadOp::create(rewriter, loc, targetVecType,
745 loc, slicedLoad,
result, offsets, strides);
752 vector::UnrollVectorOptions options;
756 UnrollStorePattern(MLIRContext *context,
757 const vector::UnrollVectorOptions &options,
758 PatternBenefit benefit = 1)
759 : OpRewritePattern<vector::StoreOp>(context, benefit), options(options) {}
761 LogicalResult matchAndRewrite(vector::StoreOp storeOp,
762 PatternRewriter &rewriter)
const override {
763 VectorType vecType = storeOp.getVectorType();
769 Location loc = storeOp.getLoc();
770 ArrayRef<int64_t> originalShape = vecType.getShape();
771 SmallVector<int64_t> strides(targetShape->size(), 1);
773 Value base = storeOp.getBase();
774 Value vector = storeOp.getValueToStore();
776 SmallVector<int64_t> loopOrder =
779 for (SmallVector<int64_t> offsets :
780 StaticTileOffsetRange(originalShape, *targetShape, loopOrder)) {
783 Value slice = rewriter.
createOrFold<vector::ExtractStridedSliceOp>(
784 loc, vector, offsets, *targetShape, strides);
785 vector::StoreOp::create(rewriter, loc, slice, base,
indices);
792 vector::UnrollVectorOptions options;
795struct UnrollBroadcastPattern :
public OpRewritePattern<vector::BroadcastOp> {
796 UnrollBroadcastPattern(MLIRContext *context,
797 const vector::UnrollVectorOptions &options,
798 PatternBenefit benefit = 1)
799 : OpRewritePattern<vector::BroadcastOp>(context, benefit),
802 LogicalResult matchAndRewrite(vector::BroadcastOp broadcastOp,
803 PatternRewriter &rewriter)
const override {
808 Location loc = broadcastOp.getLoc();
809 VectorType srcType = dyn_cast<VectorType>(broadcastOp.getSourceType());
810 VectorType resType = broadcastOp.getResultVectorType();
811 VectorType targetType =
812 resType.cloneWith(*targetShape, resType.getElementType());
813 Value
result = arith::ConstantOp::create(rewriter, loc, resType,
816 SmallVector<int64_t> originalShape = *broadcastOp.getShapeForUnroll();
817 SmallVector<int64_t> strides(originalShape.size(), 1);
819 for (SmallVector<int64_t> offsets :
820 StaticTileOffsetRange(originalShape, *targetShape)) {
824 newSrc = broadcastOp.getSource();
827 int64_t rank = srcType.getRank();
828 SmallVector<int64_t> srcOffsets(offsets.end() - rank, offsets.end());
829 SmallVector<int64_t> srcShape(targetShape->end() - rank,
831 SmallVector<int64_t> srcStrides(strides.end() - rank, strides.end());
833 for (int64_t i = 0; i < rank; ++i) {
834 if (srcType.getDimSize(i) == 1) {
839 newSrc = rewriter.
createOrFold<vector::ExtractStridedSliceOp>(
840 loc, broadcastOp.getSource(), srcOffsets, srcShape, srcStrides);
855 vector::UnrollVectorOptions options;
875struct UnrollToElements final :
public OpRewritePattern<vector::ToElementsOp> {
876 UnrollToElements(MLIRContext *context,
877 const vector::UnrollVectorOptions &options,
878 PatternBenefit benefit = 1)
879 : OpRewritePattern<vector::ToElementsOp>(context, benefit),
882 LogicalResult matchAndRewrite(vector::ToElementsOp op,
883 PatternRewriter &rewriter)
const override {
886 FailureOr<SmallVector<Value>>
result =
891 SmallVector<Value> vectors = *
result;
893 SmallVector<Value> results;
894 for (Value vector : vectors) {
896 vector::ToElementsOp::create(rewriter, op.getLoc(), vector);
897 llvm::append_range(results, subElements.getResults());
904 vector::UnrollVectorOptions options;
934 UnrollStepPattern(MLIRContext *context,
935 const vector::UnrollVectorOptions &options,
936 PatternBenefit benefit = 1)
937 : OpRewritePattern<vector::StepOp>(context, benefit), options(options) {}
939 LogicalResult matchAndRewrite(vector::StepOp stepOp,
940 PatternRewriter &rewriter)
const override {
941 std::optional<SmallVector<int64_t>> targetShape =
946 VectorType vecType = stepOp.getType();
947 if (vecType.isScalable()) {
951 int64_t originalSize = vecType.getShape()[0];
952 Location loc = stepOp.getLoc();
953 SmallVector<int64_t> strides(1, 1);
955 Value
result = arith::ConstantOp::create(rewriter, loc, vecType,
959 VectorType::get(*targetShape, vecType.getElementType());
960 Value baseStep = vector::StepOp::create(rewriter, loc, targetVecType);
961 for (
const SmallVector<int64_t> &offsets :
962 StaticTileOffsetRange({originalSize}, *targetShape)) {
963 Value bcastOffset = arith::ConstantOp::create(
964 rewriter, loc, targetVecType,
967 IntegerAttr::get(targetVecType.getElementType(), offsets[0])));
969 arith::AddIOp::create(rewriter, loc, baseStep, bcastOffset);
972 loc, tileStep,
result, offsets, strides);
979 vector::UnrollVectorOptions options;
1000 UnrollFromElements(MLIRContext *context,
1001 const vector::UnrollVectorOptions &options,
1002 PatternBenefit benefit = 1)
1003 : OpRewritePattern<vector::FromElementsOp>(context, benefit),
1006 LogicalResult matchAndRewrite(vector::FromElementsOp op,
1007 PatternRewriter &rewriter)
const override {
1010 auto unrollFromElementsFn = [&](PatternRewriter &rewriter, Location loc,
1011 VectorType subTy, int64_t index) {
1012 size_t subTyNumElements = subTy.getNumElements();
1013 assert((index + 1) * subTyNumElements <= allElements.size() &&
1016 allElements.slice(index * subTyNumElements, subTyNumElements);
1017 return vector::FromElementsOp::create(rewriter, loc, subTy, subElements);
1024 vector::UnrollVectorOptions options;
1060struct UnrollCreateMaskPattern :
public OpRewritePattern<vector::CreateMaskOp> {
1061 UnrollCreateMaskPattern(MLIRContext *context,
1062 const vector::UnrollVectorOptions &options,
1063 PatternBenefit benefit = 1)
1064 : OpRewritePattern<vector::CreateMaskOp>(context, benefit),
1067 LogicalResult matchAndRewrite(vector::CreateMaskOp createMaskOp,
1068 PatternRewriter &rewriter)
const override {
1073 VectorType resultType = createMaskOp.getVectorType();
1074 SmallVector<int64_t> originalSize = *createMaskOp.getShapeForUnroll();
1075 Location loc = createMaskOp.getLoc();
1077 Value
result = arith::ConstantOp::create(rewriter, loc, resultType,
1079 VectorType targetVectorType =
1080 VectorType::get(*targetShape, rewriter.
getI1Type());
1081 SmallVector<int64_t> strides(targetShape->size(), 1);
1085 for (SmallVector<int64_t> offsets :
1086 StaticTileOffsetRange(originalSize, *targetShape)) {
1087 SmallVector<Value> unrolledOperands;
1089 for (
auto [i, originalMaskOperand] :
1090 llvm::enumerate(createMaskOp.getOperands())) {
1093 Value adjustedMaskSize = rewriter.
createOrFold<arith::SubIOp>(
1094 loc, originalMaskOperand, offsetVal);
1096 Value unrolledDimSize =
1099 rewriter.
createOrFold<arith::MaxSIOp>(loc, adjustedMaskSize, zero);
1100 Value unrolledOperand = rewriter.
createOrFold<arith::MinSIOp>(
1101 loc, nonNegative, unrolledDimSize);
1102 unrolledOperands.push_back(unrolledOperand);
1105 auto unrolledMask = rewriter.
createOrFold<vector::CreateMaskOp>(
1106 loc, targetVectorType, unrolledOperands);
1108 loc, unrolledMask,
result, offsets, strides);
1115 vector::UnrollVectorOptions options;
1150struct UnrollConstantMaskPattern
1152 UnrollConstantMaskPattern(MLIRContext *context,
1153 const vector::UnrollVectorOptions &options,
1154 PatternBenefit benefit = 1)
1155 : OpRewritePattern<vector::ConstantMaskOp>(context, benefit),
1158 LogicalResult matchAndRewrite(vector::ConstantMaskOp constantMaskOp,
1159 PatternRewriter &rewriter)
const override {
1160 std::optional<SmallVector<int64_t>> targetShape =
1165 VectorType resultType = constantMaskOp.getVectorType();
1166 SmallVector<int64_t> originalSize = *constantMaskOp.getShapeForUnroll();
1167 Location loc = constantMaskOp.getLoc();
1169 Value
result = arith::ConstantOp::create(rewriter, loc, resultType,
1171 VectorType targetVectorType =
1172 VectorType::get(*targetShape, rewriter.
getI1Type());
1173 SmallVector<int64_t> strides(targetShape->size(), 1);
1177 for (
const SmallVector<int64_t> &offsets :
1178 StaticTileOffsetRange(originalSize, *targetShape)) {
1179 SmallVector<int64_t> unrolledMaskDims;
1181 for (
auto [i, originalMaskDim] :
1182 llvm::enumerate(constantMaskOp.getMaskDimSizes())) {
1185 int64_t adjustedMaskSize =
1186 std::max(originalMaskDim - offsets[i],
static_cast<int64_t
>(0));
1187 int64_t unrolledMaskDim =
1188 std::min(adjustedMaskSize,
static_cast<int64_t
>((*targetShape)[i]));
1189 unrolledMaskDims.push_back(unrolledMaskDim);
1192 auto unrolledMask = rewriter.
createOrFold<vector::ConstantMaskOp>(
1193 loc, targetVectorType, unrolledMaskDims);
1195 loc, unrolledMask,
result, offsets, strides);
1202 vector::UnrollVectorOptions options;
1220 if (extractShape.empty() ||
shape.empty() ||
1221 extractShape.size() >
shape.size())
1224 while (extractShape.size() > 1 && extractShape.front() == 1)
1225 extractShape = extractShape.drop_front();
1227 while (
shape.size() > 1 &&
shape.front() == 1) {
1231 size_t rankDiff =
shape.size() - extractShape.size();
1232 if (!llvm::equal(extractShape.drop_front(),
shape.drop_front(rankDiff + 1)))
1235 int64_t extractElements = ShapedType::getNumElements(extractShape);
1236 int64_t shapeElements = ShapedType::getNumElements(
shape);
1237 return shapeElements % extractElements == 0;
1259static std::optional<SmallVector<int64_t>>
1263 int64_t remainingElements = targetElements;
1266 for (
int i = sourceShape.size() - 1; i >= 0 && remainingElements > 1; --i) {
1267 int64_t takeFromDim = std::min(remainingElements, sourceShape[i]);
1268 extractShape.insert(extractShape.begin(), takeFromDim);
1270 if (remainingElements % takeFromDim != 0)
1271 return std::nullopt;
1272 remainingElements /= takeFromDim;
1276 while (extractShape.size() < sourceShape.size())
1277 extractShape.insert(extractShape.begin(), 1);
1279 if (ShapedType::getNumElements(extractShape) != targetElements)
1280 return std::nullopt;
1282 return extractShape;
1298struct ShapeCastReassociationGroup {
1299 int64_t srcBegin, srcEnd;
1300 int64_t resBegin, resEnd;
1306static std::optional<SmallVector<ShapeCastReassociationGroup>>
1311 int64_t srcRank = sourceShape.size(), resRank = resultShape.size();
1312 while (si < srcRank && ri < resRank) {
1313 int64_t srcBegin = si, resBegin = ri;
1314 int64_t srcProd = sourceShape[si++];
1315 int64_t resProd = resultShape[ri++];
1317 while (srcProd != resProd) {
1318 if (srcProd < resProd) {
1320 return std::nullopt;
1321 srcProd *= sourceShape[si++];
1324 return std::nullopt;
1325 resProd *= resultShape[ri++];
1329 while (si < srcRank && sourceShape[si] == 1)
1331 while (ri < resRank && resultShape[ri] == 1)
1333 groups.push_back({srcBegin, si, resBegin, ri});
1335 if (si != srcRank || ri != resRank)
1336 return std::nullopt;
1387struct UnrollShapeCastPattern :
public OpRewritePattern<vector::ShapeCastOp> {
1388 UnrollShapeCastPattern(MLIRContext *context,
1389 const vector::UnrollVectorOptions &options,
1390 PatternBenefit benefit = 1)
1391 : OpRewritePattern<vector::ShapeCastOp>(context, benefit),
1394 LogicalResult matchAndRewrite(vector::ShapeCastOp shapeCastOp,
1395 PatternRewriter &rewriter)
const override {
1396 std::optional<SmallVector<int64_t>> targetShape =
1401 VectorType sourceType = shapeCastOp.getSourceVectorType();
1402 VectorType resultType = shapeCastOp.getResultVectorType();
1403 ArrayRef<int64_t> sourceShape = sourceType.getShape();
1404 ArrayRef<int64_t> resultShape = resultType.getShape();
1408 std::optional<SmallVector<ShapeCastReassociationGroup>> groups =
1409 computeShapeCastGroups(sourceShape, resultShape);
1412 shapeCastOp,
"cannot align source and result reassociation groups");
1416 SmallVector<int64_t> paddedTarget(resultShape.size(), 1);
1417 llvm::copy(*targetShape,
1418 paddedTarget.end() -
static_cast<int64_t
>(targetShape->size()));
1421 SmallVector<int64_t> extractShapeStorage;
1422 for (
const ShapeCastReassociationGroup &g : *groups) {
1423 ArrayRef<int64_t> resSub =
1424 resultShape.slice(g.resBegin, g.resEnd - g.resBegin);
1425 ArrayRef<int64_t> tgtSub = ArrayRef<int64_t>(paddedTarget)
1426 .slice(g.resBegin, g.resEnd - g.resBegin);
1427 if (!isContiguous(tgtSub, resSub))
1429 shapeCastOp,
"target shape is not contiguous within a "
1430 "reassociation group of the result vector shape");
1432 ArrayRef<int64_t> srcSub =
1433 sourceShape.slice(g.srcBegin, g.srcEnd - g.srcBegin);
1434 int64_t groupTargetElements = ShapedType::getNumElements(tgtSub);
1435 std::optional<SmallVector<int64_t>> groupExtract =
1436 calculateSourceExtractShape(srcSub, groupTargetElements);
1439 shapeCastOp,
"cannot extract the target number of elements "
1440 "contiguously from a source reassociation group");
1441 extractShapeStorage.append(groupExtract->begin(), groupExtract->end());
1443 ArrayRef<int64_t> extractShape = extractShapeStorage;
1445 Location loc = shapeCastOp.getLoc();
1448 Value
result = arith::ConstantOp::create(rewriter, loc, resultType,
1451 VectorType targetType =
1452 VectorType::get(*targetShape, sourceType.getElementType());
1455 SmallVector<int64_t> insertStrides(targetShape->size(), 1);
1457 for (SmallVector<int64_t> resultOffsets :
1458 StaticTileOffsetRange(resultShape, *targetShape)) {
1459 SmallVector<int64_t> sourceOffsets =
1460 calculateSourceOffsets(resultOffsets, sourceShape, resultShape);
1461 Value sourceChunk = rewriter.
createOrFold<vector::ExtractStridedSliceOp>(
1462 loc, shapeCastOp.getSource(), sourceOffsets, extractShape,
1464 Value targetChunk = rewriter.
createOrFold<vector::ShapeCastOp>(
1465 loc, targetType, sourceChunk);
1467 loc, targetChunk,
result, resultOffsets, insertStrides);
1475 vector::UnrollVectorOptions options;
1493 UnrollBitCastPattern(MLIRContext *context,
1494 const vector::UnrollVectorOptions &options,
1495 PatternBenefit benefit = 1)
1496 : OpRewritePattern<vector::BitCastOp>(context, benefit),
1499 LogicalResult matchAndRewrite(vector::BitCastOp bitCastOp,
1500 PatternRewriter &rewriter)
const override {
1504 "failed to get target shape");
1506 VectorType sourceType = bitCastOp.getSourceVectorType();
1507 VectorType resultType = bitCastOp.getResultVectorType();
1508 ArrayRef<int64_t> resultShape = resultType.getShape();
1509 Location loc = bitCastOp.getLoc();
1511 if (targetShape->size() != resultShape.size())
1513 bitCastOp,
"target shape rank must match result rank");
1515 unsigned sourceElementBits = sourceType.getElementTypeBitWidth();
1516 unsigned resultElementBits = resultType.getElementTypeBitWidth();
1518 SmallVector<int64_t> sourceSliceShape(targetShape->begin(),
1519 targetShape->end());
1520 int64_t lastDim = sourceSliceShape.size() - 1;
1522 sourceSliceShape[lastDim] =
1523 ((*targetShape)[lastDim] * resultElementBits) / sourceElementBits;
1525 Value
result = arith::ConstantOp::create(rewriter, loc, resultType,
1527 SmallVector<int64_t> resultStrides(targetShape->size(), 1);
1528 SmallVector<int64_t> sourceStrides(sourceSliceShape.size(), 1);
1530 VectorType targetType =
1531 VectorType::get(*targetShape, resultType.getElementType());
1533 for (SmallVector<int64_t> resultOffsets :
1534 StaticTileOffsetRange(resultShape, *targetShape)) {
1535 SmallVector<int64_t> sourceOffsets = resultOffsets;
1536 sourceOffsets[lastDim] =
1537 (resultOffsets[lastDim] * resultElementBits) / sourceElementBits;
1539 Value sourceSlice = rewriter.
createOrFold<vector::ExtractStridedSliceOp>(
1540 loc, bitCastOp.getSource(), sourceOffsets, sourceSliceShape,
1542 Value bitcastSlice = rewriter.
createOrFold<vector::BitCastOp>(
1543 loc, targetType, sourceSlice);
1545 loc, bitcastSlice,
result, resultOffsets, resultStrides);
1553 vector::UnrollVectorOptions options;
1573struct UnrollInterleavePattern :
public OpRewritePattern<vector::InterleaveOp> {
1574 UnrollInterleavePattern(MLIRContext *context,
1575 const vector::UnrollVectorOptions &options,
1576 PatternBenefit benefit = 1)
1577 : OpRewritePattern<vector::InterleaveOp>(context, benefit),
1580 LogicalResult matchAndRewrite(vector::InterleaveOp interleaveOp,
1581 PatternRewriter &rewriter)
const override {
1585 "failed to get target shape");
1587 VectorType resultType = interleaveOp.getResultVectorType();
1588 ArrayRef<int64_t> resultShape = resultType.getShape();
1589 Location loc = interleaveOp.getLoc();
1591 if (targetShape->size() != resultShape.size())
1593 interleaveOp,
"target shape rank must match result rank");
1595 SmallVector<int64_t> sourceSliceShape(targetShape->begin(),
1596 targetShape->end());
1597 int64_t lastDim = sourceSliceShape.size() - 1;
1598 sourceSliceShape[lastDim] = (*targetShape)[lastDim] / 2;
1600 Value
result = arith::ConstantOp::create(rewriter, loc, resultType,
1602 SmallVector<int64_t> resultStrides(targetShape->size(), 1);
1603 SmallVector<int64_t> sourceStrides(sourceSliceShape.size(), 1);
1605 VectorType targetType =
1606 VectorType::get(*targetShape, resultType.getElementType());
1608 for (SmallVector<int64_t> resultOffsets :
1609 StaticTileOffsetRange(resultShape, *targetShape)) {
1610 SmallVector<int64_t> sourceOffsets = resultOffsets;
1611 sourceOffsets[lastDim] = resultOffsets[lastDim] / 2;
1613 Value lhsSlice = rewriter.
createOrFold<vector::ExtractStridedSliceOp>(
1614 loc, interleaveOp.getLhs(), sourceOffsets, sourceSliceShape,
1616 Value rhsSlice = rewriter.
createOrFold<vector::ExtractStridedSliceOp>(
1617 loc, interleaveOp.getRhs(), sourceOffsets, sourceSliceShape,
1619 Value interleaveSlice = rewriter.
createOrFold<vector::InterleaveOp>(
1620 loc, targetType, lhsSlice, rhsSlice);
1622 loc, interleaveSlice,
result, resultOffsets, resultStrides);
1630 vector::UnrollVectorOptions options;
1651struct UnrollDeinterleavePattern
1653 UnrollDeinterleavePattern(MLIRContext *context,
1654 const vector::UnrollVectorOptions &options,
1655 PatternBenefit benefit = 1)
1656 : OpRewritePattern<vector::DeinterleaveOp>(context, benefit),
1659 LogicalResult matchAndRewrite(vector::DeinterleaveOp deinterleaveOp,
1660 PatternRewriter &rewriter)
const override {
1664 "failed to get target shape");
1666 VectorType resultType = deinterleaveOp.getResultVectorType();
1667 ArrayRef<int64_t> resultShape = resultType.getShape();
1668 Location loc = deinterleaveOp.getLoc();
1670 if (targetShape->size() != resultShape.size())
1672 deinterleaveOp,
"target shape rank must match result rank");
1674 SmallVector<int64_t> sourceSliceShape(targetShape->begin(),
1675 targetShape->end());
1676 int64_t lastDim = sourceSliceShape.size() - 1;
1677 sourceSliceShape[lastDim] = (*targetShape)[lastDim] * 2;
1679 Value resultOdd = arith::ConstantOp::create(
1680 rewriter, loc, resultType, rewriter.
getZeroAttr(resultType));
1681 Value resultEven = arith::ConstantOp::create(
1682 rewriter, loc, resultType, rewriter.
getZeroAttr(resultType));
1683 SmallVector<int64_t> resultStrides(targetShape->size(), 1);
1684 SmallVector<int64_t> sourceStrides(sourceSliceShape.size(), 1);
1686 for (SmallVector<int64_t> resultOffsets :
1687 StaticTileOffsetRange(resultShape, *targetShape)) {
1688 SmallVector<int64_t> sourceOffsets = resultOffsets;
1689 sourceOffsets[lastDim] = resultOffsets[lastDim] * 2;
1691 Value sourceSlice = rewriter.
createOrFold<vector::ExtractStridedSliceOp>(
1692 loc, deinterleaveOp.getSource(), sourceOffsets, sourceSliceShape,
1695 auto deinterleaveSlice =
1696 vector::DeinterleaveOp::create(rewriter, loc, sourceSlice);
1698 resultOdd = rewriter.
createOrFold<vector::InsertStridedSliceOp>(
1699 loc, deinterleaveSlice.getRes1(), resultOdd, resultOffsets,
1701 resultEven = rewriter.
createOrFold<vector::InsertStridedSliceOp>(
1702 loc, deinterleaveSlice.getRes2(), resultEven, resultOffsets,
1711 vector::UnrollVectorOptions options;
1716void mlir::vector::populateVectorUnrollPatterns(
1719 patterns.
add<UnrollTransferReadPattern, UnrollTransferWritePattern,
1720 UnrollContractionPattern, UnrollElementwisePattern,
1721 UnrollReductionPattern, UnrollMultiReductionPattern,
1722 UnrollTransposePattern, UnrollGatherPattern, UnrollLoadPattern,
1723 UnrollStorePattern, UnrollBroadcastPattern, UnrollFromElements,
1724 UnrollToElements, UnrollStepPattern, UnrollShapeCastPattern,
1725 UnrollCreateMaskPattern, UnrollConstantMaskPattern,
1726 UnrollBitCastPattern, UnrollInterleavePattern,
1731void mlir::vector::populateVectorToElementsUnrollPatterns(
1737void mlir::vector::populateVectorFromElementsUnrollPatterns(
static LogicalResult extractStrides(AffineExpr e, AffineExpr multiplicativeFactor, MutableArrayRef< AffineExpr > strides, AffineExpr &offset)
Takes a single AffineExpr e and populates the strides array with the strides expressions for each dim...
static llvm::ManagedStatic< PassManagerOptions > options
static SmallVector< Value > sliceLoadStoreIndices(PatternRewriter &rewriter, Location loc, OperandRange originalIndices, ArrayRef< int64_t > offsets)
static std::optional< SmallVector< int64_t > > getTargetShape(const vector::UnrollVectorOptions &options, Operation *op)
Return the target shape for unrolling for the given op.
static SmallVector< int64_t > getUnrollOrder(unsigned numLoops, Operation *op, const vector::UnrollVectorOptions &options)
static Operation * cloneOpWithOperandsAndTypes(OpBuilder &builder, Location loc, Operation *op, ArrayRef< Value > operands, ArrayRef< Type > resultTypes)
Base type for affine expression.
A multi-dimensional affine map Affine map's are immutable like Type's, and they are uniqued.
static AffineMap get(MLIRContext *context)
Returns a zero result affine map with no dimensions or symbols: () -> ().
ArrayRef< AffineExpr > getResults() const
TypedAttr getZeroAttr(Type type)
MLIRContext * getContext() const
static DenseElementsAttr get(ShapedType type, ArrayRef< Attribute > values)
Constructs a dense elements attribute from an array of element values.
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.
This class helps build Operations.
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...
Operation * create(const OperationState &state)
Creates an operation given the fields represented as an OperationState.
This class implements the operand iterators for the Operation class.
StringRef getStringRef() const
Return the name of this operation. This always succeeds.
Operation is the basic unit of execution within MLIR.
OpResult getResult(unsigned idx)
Get the 'idx'th result of this operation.
Location getLoc()
The source location the operation was defined or derived from.
MutableArrayRef< OpOperand > getOpOperands()
Attribute getPropertiesAsAttribute()
Return the properties converted to an attribute.
OperationName getName()
The name of an operation is the key identifier for it.
DictionaryAttr getDiscardableAttrDictionary()
Return all of the discardable attributes on this operation as a DictionaryAttr.
result_range getResults()
unsigned getNumResults()
Return the number of results held by this operation.
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...
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.
RewritePattern is the common base class for all DAG to DAG replacements.
virtual void replaceOp(Operation *op, ValueRange newValues)
Replace the results of the given (original) operation with the specified list of values (replacements...
virtual void eraseOp(Operation *op)
This method erases an operation that is known to have no uses.
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,...
Type getType() const
Return the type of this value.
static ConstantIndexOp create(OpBuilder &builder, Location location, int64_t value)
bool hasElementwiseMappableTraits(Operation *op)
Together, Elementwise, Scalarizable, Vectorizable, and Tensorizable provide an easy way for scalar op...
Value makeArithReduction(OpBuilder &b, Location loc, CombiningKind kind, Value v1, Value acc, arith::FastMathFlagsAttr fastmath=nullptr, Value mask=nullptr)
Returns the result value of reducing two scalar/vector values with the corresponding arith operation.
FailureOr< SmallVector< Value > > unrollVectorValue(TypedValue< VectorType >, RewriterBase &)
Generic utility for unrolling values of type vector<NxAxBx...> to N values of type vector<AxBx....
LogicalResult unrollVectorOp(Operation *op, PatternRewriter &rewriter, UnrollVectorOpFn unrollFn)
Include the generated interface declarations.
SmallVector< int64_t > computeStrides(ArrayRef< int64_t > sizes)
SmallVector< int64_t > delinearize(int64_t linearIndex, ArrayRef< int64_t > strides)
Given the strides together with a linear index in the dimension space, return the vector-space offset...
std::conditional_t< std::is_same_v< Ty, mlir::Type >, mlir::Value, detail::TypedValue< Ty > > TypedValue
If Ty is mlir::Type this will select Value instead of having a wrapper around it.
AffineExpr getAffineConstantExpr(int64_t constant, MLIRContext *context)
SmallVector< T > applyPermutationMap(AffineMap map, llvm::ArrayRef< T > source)
Apply a permutation from map to source and return the result.
int64_t linearize(ArrayRef< int64_t > offsets, ArrayRef< int64_t > basis)
Return the linearized index of 'offsets' w.r.t.
std::optional< SmallVector< int64_t > > computeShapeRatio(ArrayRef< int64_t > shape, ArrayRef< int64_t > subShape)
Return the multi-dimensional integral ratio of subShape to the trailing dimensions of shape.
AffineExpr getAffineDimExpr(unsigned position, MLIRContext *context)
These free functions allow clients of the API to not use classes in detail.
OpRewritePattern is a wrapper around RewritePattern that allows for matching and rewriting against an...
This represents an operation in an abstracted form, suitable for use with the builder APIs.
Attribute propertiesAttr
This Attribute is used to opaquely construct the properties of the operation.
Options that control the vector unrolling.