MLIR: lib/Conversion/VectorToSCF/VectorToSCF.cpp Source File (original) (raw)
1
2
3
4
5
6
7
8
9
10
11
12
13 #include
14 #include
15 #include <type_traits>
16
18
33
34 namespace mlir {
35 #define GEN_PASS_DEF_CONVERTVECTORTOSCF
36 #include "mlir/Conversion/Passes.h.inc"
37 }
38
39 using namespace mlir;
40 using vector::TransferReadOp;
41 using vector::TransferWriteOp;
42
43 namespace {
44
45
46 static const char kPassLabel[] = "__vector_to_scf_lowering__";
47
48
49 static bool isTensorOp(VectorTransferOpInterface xferOp) {
50 if (isa(xferOp.getShapedType())) {
51 if (isavector::TransferWriteOp(xferOp)) {
52
53 assert(xferOp->getNumResults() > 0);
54 }
55 return true;
56 }
57 return false;
58 }
59
60
61
62 template
64 explicit VectorToSCFPattern(MLIRContext *context,
67
68 LogicalResult checkLowerTensors(VectorTransferOpInterface xferOp,
70 if (isTensorOp(xferOp) && .lowerTensors) {
72 xferOp, "lowering tensor transfers is disabled");
73 }
74 return success();
75 }
76
78 };
79
80
81
82
83 template
84 static std::optional<int64_t> unpackedDim(OpTy xferOp) {
85
86 assert(xferOp.getTransferRank() > 0 && "unexpected 0-d transfer");
87 auto map = xferOp.getPermutationMap();
88 if (auto expr = dyn_cast(map.getResult(0))) {
89 return expr.getPosition();
90 }
91 assert(xferOp.isBroadcastDim(0) &&
92 "Expected AffineDimExpr or AffineConstantExpr");
93 return std::nullopt;
94 }
95
96
97
98
99 template
101
102 assert(xferOp.getTransferRank() > 0 && "unexpected 0-d transfer");
103 auto map = xferOp.getPermutationMap();
104 return AffineMap::get(map.getNumDims(), 0, map.getResults().drop_front(),
106 }
107
108
109
110
111
112
113
114 template
117 typename OpTy::Adaptor adaptor(xferOp);
118
119 auto dim = unpackedDim(xferOp);
120 auto prevIndices = adaptor.getIndices();
121 indices.append(prevIndices.begin(), prevIndices.end());
122
123 Location loc = xferOp.getLoc();
124 bool isBroadcast = !dim.has_value();
125 if (!isBroadcast) {
127 bindDims(xferOp.getContext(), d0, d1);
128 Value offset = adaptor.getIndices()[*dim];
129 indices[*dim] =
131 }
132 }
133
134 static void maybeYieldValue(OpBuilder &b, Location loc, bool hasRetVal,
136 if (hasRetVal) {
137 assert(value && "Expected non-empty value");
138 b.createscf::YieldOp(loc, value);
139 } else {
140 b.createscf::YieldOp(loc);
141 }
142 }
143
144
145
146
147
148
149
150 template
152 if (!xferOp.getMask())
154 if (xferOp.getMaskType().getRank() != 1)
156 if (xferOp.isBroadcastDim(0))
158
159 Location loc = xferOp.getLoc();
160 return b.createvector::ExtractElementOp(loc, xferOp.getMask(), iv);
161 }
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187 template
188 static Value generateInBoundsCheck(
189 OpBuilder &b, OpTy xferOp, Value iv, std::optional<int64_t> dim,
193 bool hasRetVal = !resultTypes.empty();
194 Value cond;
195
196
197 bool isBroadcast = !dim;
198 Location loc = xferOp.getLoc();
200 if (!xferOp.isDimInBounds(0) && !isBroadcast) {
203 bindDims(xferOp.getContext(), d0, d1);
204 Value base = xferOp.getIndices()[*dim];
205 Value memrefIdx =
207 cond = lb.createarith::CmpIOp(arith::CmpIPredicate::sgt, memrefDim,
208 memrefIdx);
209 }
210
211
212 if (auto maskCond = generateMaskCheck(b, xferOp, iv)) {
213 if (cond)
214 cond = lb.createarith::AndIOp(cond, maskCond);
215 else
216 cond = maskCond;
217 }
218
219
220 if (cond) {
221 auto check = lb.createscf::IfOp(
222 cond,
223
225 maybeYieldValue(b, loc, hasRetVal, inBoundsCase(b, loc));
226 },
227
229 if (outOfBoundsCase) {
230 maybeYieldValue(b, loc, hasRetVal, outOfBoundsCase(b, loc));
231 } else {
232 b.createscf::YieldOp(loc);
233 }
234 });
235
236 return hasRetVal ? check.getResult(0) : Value();
237 }
238
239
240 return inBoundsCase(b, loc);
241 }
242
243
244
245 template
246 static void generateInBoundsCheck(
247 OpBuilder &b, OpTy xferOp, Value iv, std::optional<int64_t> dim,
250 generateInBoundsCheck(
251 b, xferOp, iv, dim, TypeRange(),
252
254 inBoundsCase(b, loc);
256 },
257
259 if (outOfBoundsCase)
260 outOfBoundsCase(b, loc);
262 });
263 }
264
265
266 static ArrayAttr dropFirstElem(OpBuilder &b, ArrayAttr attr) {
267 if (!attr)
268 return attr;
270 }
271
272
273
274 template
275 static void maybeApplyPassLabel(OpBuilder &b, OpTy newXferOp,
276 unsigned targetRank) {
277 if (newXferOp.getVectorType().getRank() > targetRank)
278 newXferOp->setAttr(kPassLabel, b.getUnitAttr());
279 }
280
282
283
284 struct BufferAllocs {
285 Value dataBuffer;
286 Value maskBuffer;
287 };
288
289
293 assert(scope && "Expected op to be inside automatic allocation scope");
294 return scope;
295 }
296
297
298 template
299 static BufferAllocs allocBuffers(OpBuilder &b, OpTy xferOp) {
300 Location loc = xferOp.getLoc();
304 "AutomaticAllocationScope with >1 regions");
306
307 BufferAllocs result;
308 auto bufferType = MemRefType::get({}, xferOp.getVectorType());
309 result.dataBuffer = b.creatememref::AllocaOp(loc, bufferType);
310
311 if (xferOp.getMask()) {
313 auto maskBuffer = b.creatememref::AllocaOp(loc, maskType);
315 b.creatememref::StoreOp(loc, xferOp.getMask(), maskBuffer);
316 result.maskBuffer = b.creatememref::LoadOp(loc, maskBuffer, ValueRange());
317 }
318
319 return result;
320 }
321
322
323
324
325
326 static FailureOr unpackOneDim(MemRefType type) {
327 auto vectorType = dyn_cast(type.getElementType());
328
329
330 if (vectorType.getScalableDims().front())
331 return failure();
332 auto memrefShape = type.getShape();
334 newMemrefShape.append(memrefShape.begin(), memrefShape.end());
335 newMemrefShape.push_back(vectorType.getDimSize(0));
338 }
339
340
341
342 template
343 static Value getMaskBuffer(OpTy xferOp) {
344 assert(xferOp.getMask() && "Expected that transfer op has mask");
345 auto loadOp = xferOp.getMask().template getDefiningOpmemref::LoadOp();
346 assert(loadOp && "Expected transfer op mask produced by LoadOp");
347 return loadOp.getMemRef();
348 }
349
350
351 template
352 struct Strategy;
353
354
355 template <>
356 struct Strategy {
357
358
359 static memref::StoreOp getStoreOp(TransferReadOp xferOp) {
360 assert(xferOp->hasOneUse() && "Expected exactly one use of TransferReadOp");
361 auto storeOp = dyn_castmemref::StoreOp((*xferOp->use_begin()).getOwner());
362 assert(storeOp && "Expected TransferReadOp result used by StoreOp");
363 return storeOp;
364 }
365
366
367
368
369
370
371
372
374 return getStoreOp(xferOp).getMemRef();
375 }
376
377
378 static void getBufferIndices(TransferReadOp xferOp,
380 auto storeOp = getStoreOp(xferOp);
381 auto prevIndices = memref::StoreOpAdaptor(storeOp).getIndices();
382 indices.append(prevIndices.begin(), prevIndices.end());
383 }
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412 static TransferReadOp rewriteOp(OpBuilder &b,
414 TransferReadOp xferOp, Value buffer, Value iv,
417 getBufferIndices(xferOp, storeIndices);
418 storeIndices.push_back(iv);
419
422
423 Location loc = xferOp.getLoc();
424 auto bufferType = dyn_cast(buffer.getType());
425 auto vecType = dyn_cast(bufferType.getElementType());
426 auto inBoundsAttr = dropFirstElem(b, xferOp.getInBoundsAttr());
427 auto newXferOp = b.createvector::TransferReadOp(
428 loc, vecType, xferOp.getBase(), xferIndices,
430 xferOp.getPadding(), Value(), inBoundsAttr);
431
432 maybeApplyPassLabel(b, newXferOp, options.targetRank);
433
434 b.creatememref::StoreOp(loc, newXferOp.getVector(), buffer, storeIndices);
435 return newXferOp;
436 }
437
438
439
440 static Value handleOutOfBoundsDim(OpBuilder &b, TransferReadOp xferOp,
444 getBufferIndices(xferOp, storeIndices);
445 storeIndices.push_back(iv);
446
447 Location loc = xferOp.getLoc();
448 auto bufferType = dyn_cast(buffer.getType());
449 auto vecType = dyn_cast(bufferType.getElementType());
450 auto vec = b.createvector::SplatOp(loc, vecType, xferOp.getPadding());
451 b.creatememref::StoreOp(loc, vec, buffer, storeIndices);
452
454 }
455
456
457 static void cleanup(PatternRewriter &rewriter, TransferReadOp xferOp,
458 scf::ForOp ) {
459 rewriter.eraseOp(getStoreOp(xferOp));
460 rewriter.eraseOp(xferOp);
461 }
462
463
464 static Value initialLoopState(TransferReadOp xferOp) { return Value(); }
465 };
466
467
468 template <>
469 struct Strategy {
470
471
472
473
474
475
476
478 auto loadOp = xferOp.getVector().getDefiningOpmemref::LoadOp();
479 assert(loadOp && "Expected transfer op vector produced by LoadOp");
480 return loadOp.getMemRef();
481 }
482
483
484 static void getBufferIndices(TransferWriteOp xferOp,
486 auto loadOp = xferOp.getVector().getDefiningOpmemref::LoadOp();
487 auto prevIndices = memref::LoadOpAdaptor(loadOp).getIndices();
488 indices.append(prevIndices.begin(), prevIndices.end());
489 }
490
491
492
493
494
495
496
497
498
499
500 static TransferWriteOp rewriteOp(OpBuilder &b,
502 TransferWriteOp xferOp, Value buffer,
505 getBufferIndices(xferOp, loadIndices);
506 loadIndices.push_back(iv);
507
510
511 Location loc = xferOp.getLoc();
512 auto vec = b.creatememref::LoadOp(loc, buffer, loadIndices);
513 auto inBoundsAttr = dropFirstElem(b, xferOp.getInBoundsAttr());
514 auto source = loopState.empty() ? xferOp.getBase() : loopState[0];
515 Type type = isTensorOp(xferOp) ? xferOp.getShapedType() : Type();
516 auto newXferOp = b.createvector::TransferWriteOp(
517 loc, type, vec, source, xferIndices,
519 inBoundsAttr);
520
521 maybeApplyPassLabel(b, newXferOp, options.targetRank);
522
523 return newXferOp;
524 }
525
526
527 static Value handleOutOfBoundsDim(OpBuilder &b, TransferWriteOp xferOp,
530 return isTensorOp(xferOp) ? loopState[0] : Value();
531 }
532
533
534 static void cleanup(PatternRewriter &rewriter, TransferWriteOp xferOp,
535 scf::ForOp forOp) {
536 if (isTensorOp(xferOp)) {
537 assert(forOp->getNumResults() == 1 && "Expected one for loop result");
538 rewriter.replaceOp(xferOp, forOp->getResult(0));
539 } else {
540 rewriter.eraseOp(xferOp);
541 }
542 }
543
544
545 static Value initialLoopState(TransferWriteOp xferOp) {
546 return isTensorOp(xferOp) ? xferOp.getBase() : Value();
547 }
548 };
549
550 template
551 static LogicalResult checkPrepareXferOp(OpTy xferOp, PatternRewriter &rewriter,
553 if (xferOp->hasAttr(kPassLabel))
555 xferOp, "kPassLabel is present (vector-to-scf lowering in progress)");
556 if (xferOp.getVectorType().getRank() <= options.targetRank)
558 xferOp, "xferOp vector rank <= transformation target rank");
559 if (xferOp.getVectorType().getScalableDims().front())
561 xferOp, "Unpacking of the leading dimension into the memref is not yet "
562 "supported for scalable dims");
563 if (isTensorOp(xferOp) && .lowerTensors)
565 xferOp, "Unpacking for tensors has been disabled.");
566 if (xferOp.getVectorType().getElementType() !=
567 xferOp.getShapedType().getElementType())
569 xferOp, "Mismatching source and destination element types.");
570
571 return success();
572 }
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597 struct PrepareTransferReadConversion
598 : public VectorToSCFPattern {
599 using VectorToSCFPattern::VectorToSCFPattern;
600
601 LogicalResult matchAndRewrite(TransferReadOp xferOp,
603 if (checkPrepareXferOp(xferOp, rewriter, options).failed())
605 xferOp, "checkPrepareXferOp conditions not met!");
606
607 auto buffers = allocBuffers(rewriter, xferOp);
608 auto *newXfer = rewriter.clone(*xferOp.getOperation());
610 if (xferOp.getMask()) {
611 dyn_cast(newXfer).getMaskMutable().assign(
612 buffers.maskBuffer);
613 }
614
615 Location loc = xferOp.getLoc();
616 rewriter.creatememref::StoreOp(loc, newXfer->getResult(0),
617 buffers.dataBuffer);
618 rewriter.replaceOpWithNewOpmemref::LoadOp(xferOp, buffers.dataBuffer);
619
620 return success();
621 }
622 };
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647 struct PrepareTransferWriteConversion
648 : public VectorToSCFPattern {
649 using VectorToSCFPattern::VectorToSCFPattern;
650
651 LogicalResult matchAndRewrite(TransferWriteOp xferOp,
653 if (checkPrepareXferOp(xferOp, rewriter, options).failed())
655 xferOp, "checkPrepareXferOp conditions not met!");
656
657 Location loc = xferOp.getLoc();
658 auto buffers = allocBuffers(rewriter, xferOp);
659 rewriter.creatememref::StoreOp(loc, xferOp.getVector(),
660 buffers.dataBuffer);
661 auto loadedVec = rewriter.creatememref::LoadOp(loc, buffers.dataBuffer);
663 xferOp.getValueToStoreMutable().assign(loadedVec);
664 xferOp->setAttr(kPassLabel, rewriter.getUnitAttr());
665 });
666
667 if (xferOp.getMask()) {
669 xferOp.getMaskMutable().assign(buffers.maskBuffer);
670 });
671 }
672
673 return success();
674 }
675 };
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704 struct DecomposePrintOpConversion : public VectorToSCFPatternvector::PrintOp {
705 using VectorToSCFPatternvector::PrintOp::VectorToSCFPattern;
706 LogicalResult matchAndRewrite(vector::PrintOp printOp,
708 if (.getSource())
709 return failure();
710
711 VectorType vectorType = dyn_cast(printOp.getPrintType());
712 if (!vectorType)
713 return failure();
714
715
716
717
718
719
720
721 if (vectorType.getRank() > 1 && vectorType.isScalable())
722 return failure();
723
724 auto loc = printOp.getLoc();
725 auto value = printOp.getSource();
726
727 if (auto intTy = dyn_cast(vectorType.getElementType())) {
728
729
730
731 auto width = intTy.getWidth();
732 auto legalWidth = llvm::NextPowerOf2(std::max(8u, width) - 1);
734 intTy.getSignedness());
735
736 auto signlessSourceVectorType =
737 vectorType.cloneWith({}, getIntTypeWithSignlessSemantics(intTy));
738 auto signlessTargetVectorType =
739 vectorType.cloneWith({}, getIntTypeWithSignlessSemantics(legalIntTy));
740 auto targetVectorType = vectorType.cloneWith({}, legalIntTy);
741 value = rewriter.createvector::BitCastOp(loc, signlessSourceVectorType,
742 value);
743 if (value.getType() != signlessTargetVectorType) {
744 if (width == 1 || intTy.isUnsigned())
745 value = rewriter.createarith::ExtUIOp(loc, signlessTargetVectorType,
746 value);
747 else
748 value = rewriter.createarith::ExtSIOp(loc, signlessTargetVectorType,
749 value);
750 }
751 value = rewriter.createvector::BitCastOp(loc, targetVectorType, value);
752 vectorType = targetVectorType;
753 }
754
755 auto scalableDimensions = vectorType.getScalableDims();
756 auto shape = vectorType.getShape();
757 constexpr int64_t singletonShape[] = {1};
758 if (vectorType.getRank() == 0)
759 shape = singletonShape;
760
761 if (vectorType.getRank() != 1) {
762
763
764
765 auto flatLength = std::accumulate(shape.begin(), shape.end(), 1,
766 std::multiplies<int64_t>());
767 auto flatVectorType =
768 VectorType::get({flatLength}, vectorType.getElementType());
769 value = rewriter.createvector::ShapeCastOp(loc, flatVectorType, value);
770 }
771
772 vector::PrintOp firstClose;
774 for (unsigned d = 0; d < shape.size(); d++) {
775
776 Value lowerBound = rewriter.createarith::ConstantIndexOp(loc, 0);
777 Value upperBound = rewriter.createarith::ConstantIndexOp(loc, shape[d]);
778 Value step = rewriter.createarith::ConstantIndexOp(loc, 1);
779 if (!scalableDimensions.empty() && scalableDimensions[d]) {
780 auto vscale = rewriter.createvector::VectorScaleOp(
782 upperBound = rewriter.createarith::MulIOp(loc, upperBound, vscale);
783 }
784 auto lastIndex = rewriter.createarith::SubIOp(loc, upperBound, step);
785
786
787 rewriter.createvector::PrintOp(loc, vector::PrintPunctuation::Open);
788 auto loop =
789 rewriter.createscf::ForOp(loc, lowerBound, upperBound, step);
791 loc, vector::PrintPunctuation::Close);
792 if (!firstClose)
794
795 auto loopIdx = loop.getInductionVar();
796 loopIndices.push_back(loopIdx);
797
798
800 auto notLastIndex = rewriter.createarith::CmpIOp(
801 loc, arith::CmpIPredicate::ult, loopIdx, lastIndex);
802 rewriter.createscf::IfOp(loc, notLastIndex,
804 builder.createvector::PrintOp(
805 loc, vector::PrintPunctuation::Comma);
806 builder.createscf::YieldOp(loc);
807 });
808
810 }
811
812
813
815 auto currentStride = 1;
816 for (int d = shape.size() - 1; d >= 0; d--) {
817 auto stride = rewriter.createarith::ConstantIndexOp(loc, currentStride);
818 auto index = rewriter.createarith::MulIOp(loc, stride, loopIndices[d]);
819 if (flatIndex)
820 flatIndex = rewriter.createarith::AddIOp(loc, flatIndex, index);
821 else
822 flatIndex = index;
823 currentStride *= shape[d];
824 }
825
826
827 auto element =
828 rewriter.createvector::ExtractElementOp(loc, value, flatIndex);
829 rewriter.createvector::PrintOp(loc, element,
830 vector::PrintPunctuation::NoPunctuation);
831
833 rewriter.createvector::PrintOp(loc, printOp.getPunctuation());
835 return success();
836 }
837
838 static IntegerType getIntTypeWithSignlessSemantics(IntegerType intTy) {
839 return IntegerType::get(intTy.getContext(), intTy.getWidth(),
840 IntegerType::Signless);
841 };
842 };
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873 template
874 struct TransferOpConversion : public VectorToSCFPattern {
875 using VectorToSCFPattern::VectorToSCFPattern;
876
877 void initialize() {
878
879
880 this->setHasBoundedRewriteRecursion();
881 }
882
883 static void getMaskBufferLoadIndices(OpTy xferOp, Value castedMaskBuffer,
886 assert(xferOp.getMask() && "Expected transfer op to have mask");
887
888
889
890
891
892 Value maskBuffer = getMaskBuffer(xferOp);
894
895 if (auto loadOp = dyn_castmemref::LoadOp(user)) {
897 loadIndices.append(prevIndices.begin(), prevIndices.end());
898 break;
899 }
900 }
901
902
903
904 if (!xferOp.isBroadcastDim(0))
905 loadIndices.push_back(iv);
906 }
907
908 LogicalResult matchAndRewrite(OpTy xferOp,
910 if (!xferOp->hasAttr(kPassLabel))
912 xferOp, "kPassLabel is present (progressing lowering in progress)");
913
914
917 auto dataBufferType = dyn_cast(dataBuffer.getType());
918 FailureOr castedDataType = unpackOneDim(dataBufferType);
919 if (failed(castedDataType))
921 "Failed to unpack one vector dim.");
922
923 auto castedDataBuffer =
924 locB.createvector::TypeCastOp(*castedDataType, dataBuffer);
925
926
927 Value castedMaskBuffer;
928 if (xferOp.getMask()) {
929 Value maskBuffer = getMaskBuffer(xferOp);
930 if (xferOp.isBroadcastDim(0) || xferOp.getMaskType().getRank() == 1) {
931
932
933
934
935
936 castedMaskBuffer = maskBuffer;
937 } else {
938
939
940 auto maskBufferType = cast(maskBuffer.getType());
941 MemRefType castedMaskType = *unpackOneDim(maskBufferType);
942 castedMaskBuffer =
943 locB.createvector::TypeCastOp(castedMaskType, maskBuffer);
944 }
945 }
946
947
948 auto lb = locB.createarith::ConstantIndexOp(0);
949 auto ub = locB.createarith::ConstantIndexOp(
950 castedDataType->getDimSize(castedDataType->getRank() - 1));
951 auto step = locB.createarith::ConstantIndexOp(1);
952
953
954 auto loopState = Strategy::initialLoopState(xferOp);
955
956
957 auto result = locB.createscf::ForOp(
960 Type stateType = loopState.empty() ? Type() : loopState[0].getType();
961
962 auto result = generateInBoundsCheck(
963 b, xferOp, iv, unpackedDim(xferOp),
965
967
968 OpTy newXfer = Strategy::rewriteOp(
969 b, this->options, xferOp, castedDataBuffer, iv, loopState);
970
971
972
973
974
975 if (xferOp.getMask() && (xferOp.isBroadcastDim(0) ||
976 xferOp.getMaskType().getRank() > 1)) {
979
981 getMaskBufferLoadIndices(xferOp, castedMaskBuffer,
982 loadIndices, iv);
983 auto mask = b.creatememref::LoadOp(loc, castedMaskBuffer,
984 loadIndices);
986 newXfer.getMaskMutable().assign(mask);
987 });
988 }
989
990 return loopState.empty() ? Value() : newXfer->getResult(0);
991 },
992
994 return Strategy::handleOutOfBoundsDim(
995 b, xferOp, castedDataBuffer, iv, loopState);
996 });
997
998 maybeYieldValue(b, loc, !loopState.empty(), result);
999 });
1000
1001 Strategy::cleanup(rewriter, xferOp, result);
1002 return success();
1003 }
1004 };
1005
1006
1007
1008 template
1009 static FailureOr<SmallVector>
1010 getMaskDimSizes(Value mask, VscaleConstantBuilder &createVscaleMultiple) {
1011 if (!mask)
1013 if (auto createMaskOp = mask.getDefiningOpvector::CreateMaskOp()) {
1014 return llvm::map_to_vector(createMaskOp.getOperands(), [](Value dimSize) {
1015 return OpFoldResult(dimSize);
1016 });
1017 }
1018 if (auto constantMask = mask.getDefiningOpvector::ConstantMaskOp()) {
1019 int dimIdx = 0;
1020 VectorType maskType = constantMask.getVectorType();
1022 return llvm::map_to_vector(
1023 constantMask.getMaskDimSizes(), [&](int64_t dimSize) {
1024
1025 if (maskType.getScalableDims()[dimIdx++])
1026 return OpFoldResult(createVscaleMultiple(dimSize));
1027 return OpFoldResult(IntegerAttr::get(indexType, dimSize));
1028 });
1029 }
1030 return failure();
1031 }
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070 struct ScalableTransposeTransferWriteConversion
1071 : VectorToSCFPatternvector::TransferWriteOp {
1072 using VectorToSCFPattern::VectorToSCFPattern;
1073
1074 LogicalResult matchAndRewrite(TransferWriteOp writeOp,
1076 if (failed(checkLowerTensors(writeOp, rewriter)))
1077 return failure();
1078
1079 VectorType vectorType = writeOp.getVectorType();
1080
1081
1082
1083 ArrayRef scalableFlags = vectorType.getScalableDims();
1086 writeOp, "expected vector of the form vector<[N]xMxty>");
1087 }
1088
1089 auto permutationMap = writeOp.getPermutationMap();
1090 if (!permutationMap.isIdentity()) {
1092 writeOp, "non-identity permutations are unsupported (lower first)");
1093 }
1094
1095
1096
1097
1098 if (!writeOp.isDimInBounds(0)) {
1100 writeOp, "out-of-bounds dims are unsupported (use masking)");
1101 }
1102
1103 Value vector = writeOp.getVector();
1104 auto transposeOp = vector.getDefiningOpvector::TransposeOp();
1105 if (!transposeOp ||
1107 return rewriter.notifyMatchFailure(writeOp, "source not transpose");
1108 }
1109
1110 auto loc = writeOp.getLoc();
1111 auto createVscaleMultiple =
1113
1114 auto maskDims = getMaskDimSizes(writeOp.getMask(), createVscaleMultiple);
1115 if (failed(maskDims)) {
1117 "failed to resolve mask dims");
1118 }
1119
1120 int64_t fixedDimSize = vectorType.getDimSize(1);
1121 auto fixedDimOffsets = llvm::seq(fixedDimSize);
1122
1123
1124 auto transposeSource = transposeOp.getVector();
1126 llvm::map_to_vector(fixedDimOffsets, [&](int64_t idx) -> Value {
1127 return rewriter.createvector::ExtractOp(loc, transposeSource, idx);
1128 });
1129
1130
1131 auto lb = rewriter.createarith::ConstantIndexOp(loc, 0);
1132 auto ub =
1133 maskDims->empty()
1134 ? Value(createVscaleMultiple(vectorType.getDimSize(0)))
1136 auto step = rewriter.createarith::ConstantIndexOp(loc, 1);
1137
1138
1140 Value sliceMask = nullptr;
1141 if (!maskDims->empty()) {
1142 sliceMask = rewriter.createvector::CreateMaskOp(
1143 loc, sliceType.clone(rewriter.getI1Type()),
1145 }
1146
1147 Value initDest = isTensorOp(writeOp) ? writeOp.getBase() : Value{};
1149 auto result = rewriter.createscf::ForOp(
1150 loc, lb, ub, step, initLoopArgs,
1152
1155
1156
1158 llvm::map_to_vector(fixedDimOffsets, [&](int64_t idx) -> Value {
1159 return b.createvector::ExtractOp(
1160 loc, transposeSourceSlices[idx], iv);
1161 });
1162 auto sliceVec = b.createvector::FromElementsOp(loc, sliceType,
1163 transposeElements);
1164
1165
1167 loopIterArgs.empty() ? writeOp.getBase() : loopIterArgs.front();
1168 auto newWriteOp = b.createvector::TransferWriteOp(
1169 loc, sliceVec, dest, xferIndices,
1170 ArrayRef(writeOp.getInBoundsValues()).drop_front());
1171 if (sliceMask)
1172 newWriteOp.getMaskMutable().assign(sliceMask);
1173
1174
1175 b.createscf::YieldOp(loc, loopIterArgs.empty()
1177 : newWriteOp.getResult());
1178 });
1179
1180 if (isTensorOp(writeOp))
1181 rewriter.replaceOp(writeOp, result);
1182 else
1183 rewriter.eraseOp(writeOp);
1184
1185 return success();
1186 }
1187 };
1188
1189 }
1190
1192
1193
1194
1195 template
1196 static void maybeAssignMask(OpBuilder &b, OpTy xferOp, OpTy newXferOp,
1197 int64_t i) {
1198 if (!xferOp.getMask())
1199 return;
1200
1201 if (xferOp.isBroadcastDim(0)) {
1202
1203
1204 newXferOp.getMaskMutable().assign(xferOp.getMask());
1205 return;
1206 }
1207
1208 if (xferOp.getMaskType().getRank() > 1) {
1209
1212
1214 Location loc = xferOp.getLoc();
1215 auto newMask = b.createvector::ExtractOp(loc, xferOp.getMask(), indices);
1216 newXferOp.getMaskMutable().assign(newMask);
1217 }
1218
1219
1220
1221
1222 }
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252 struct UnrollTransferReadConversion
1253 : public VectorToSCFPattern {
1254 using VectorToSCFPattern::VectorToSCFPattern;
1255
1256 void initialize() {
1257
1258
1259 setHasBoundedRewriteRecursion();
1260 }
1261
1262
1263
1265 TransferReadOp xferOp) const {
1266 if (auto insertOp = getInsertOp(xferOp))
1267 return insertOp.getDest();
1268 Location loc = xferOp.getLoc();
1269 return rewriter.createvector::SplatOp(loc, xferOp.getVectorType(),
1270 xferOp.getPadding());
1271 }
1272
1273
1274
1275 vector::InsertOp getInsertOp(TransferReadOp xferOp) const {
1276 if (xferOp->hasOneUse()) {
1278 if (auto insertOp = dyn_castvector::InsertOp(xferOpUser))
1279 return insertOp;
1280 }
1281
1282 return vector::InsertOp();
1283 }
1284
1285
1286
1287 void getInsertionIndices(TransferReadOp xferOp,
1289 if (auto insertOp = getInsertOp(xferOp)) {
1290 auto pos = insertOp.getMixedPosition();
1291 indices.append(pos.begin(), pos.end());
1292 }
1293 }
1294
1295
1296
1297 LogicalResult matchAndRewrite(TransferReadOp xferOp,
1299 if (xferOp.getVectorType().getRank() <= options.targetRank)
1301 xferOp, "vector rank is less or equal to target rank");
1302 if (failed(checkLowerTensors(xferOp, rewriter)))
1303 return failure();
1304 if (xferOp.getVectorType().getElementType() !=
1305 xferOp.getShapedType().getElementType())
1307 xferOp, "not yet supported: element type mismatch");
1308 auto xferVecType = xferOp.getVectorType();
1309 if (xferVecType.getScalableDims()[0]) {
1311 xferOp, "scalable dimensions cannot be unrolled at compile time");
1312 }
1313
1314 auto insertOp = getInsertOp(xferOp);
1315 auto vec = buildResultVector(rewriter, xferOp);
1316 auto vecType = dyn_cast(vec.getType());
1317
1319
1320 int64_t dimSize = xferVecType.getShape()[0];
1321
1322
1323 Location loc = xferOp.getLoc();
1324 for (int64_t i = 0; i < dimSize; ++i) {
1325 Value iv = rewriter.createarith::ConstantIndexOp(loc, i);
1326
1327 vec = generateInBoundsCheck(
1328 rewriter, xferOp, iv, unpackedDim(xferOp), TypeRange(vecType),
1329
1331
1334
1335
1337 getInsertionIndices(xferOp, insertionIndices);
1338 insertionIndices.push_back(rewriter.getIndexAttr(i));
1339
1340 auto inBoundsAttr = dropFirstElem(b, xferOp.getInBoundsAttr());
1341 auto newXferOp = b.createvector::TransferReadOp(
1342 loc, newXferVecType, xferOp.getBase(), xferIndices,
1344 xferOp.getPadding(), Value(), inBoundsAttr);
1345 maybeAssignMask(b, xferOp, newXferOp, i);
1346 return b.createvector::InsertOp(loc, newXferOp, vec,
1347 insertionIndices);
1348 },
1349
1351
1352 return vec;
1353 });
1354 }
1355
1356 if (insertOp) {
1357
1358 rewriter.replaceOp(insertOp, vec);
1359 rewriter.eraseOp(xferOp);
1360 } else {
1361 rewriter.replaceOp(xferOp, vec);
1362 }
1363
1364 return success();
1365 }
1366 };
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394 struct UnrollTransferWriteConversion
1395 : public VectorToSCFPattern {
1396 using VectorToSCFPattern::VectorToSCFPattern;
1397
1398 void initialize() {
1399
1400
1401 setHasBoundedRewriteRecursion();
1402 }
1403
1404
1405 Value getDataVector(TransferWriteOp xferOp) const {
1406 if (auto extractOp = getExtractOp(xferOp))
1407 return extractOp.getVector();
1408 return xferOp.getVector();
1409 }
1410
1411
1412 vector::ExtractOp getExtractOp(TransferWriteOp xferOp) const {
1413 if (auto *op = xferOp.getVector().getDefiningOp())
1414 return dyn_castvector::ExtractOp(op);
1415 return vector::ExtractOp();
1416 }
1417
1418
1419
1420 void getExtractionIndices(TransferWriteOp xferOp,
1422 if (auto extractOp = getExtractOp(xferOp)) {
1423 auto pos = extractOp.getMixedPosition();
1424 indices.append(pos.begin(), pos.end());
1425 }
1426 }
1427
1428
1429
1430 LogicalResult matchAndRewrite(TransferWriteOp xferOp,
1432 VectorType inputVectorTy = xferOp.getVectorType();
1433
1434 if (inputVectorTy.getRank() <= options.targetRank)
1435 return failure();
1436
1437 if (failed(checkLowerTensors(xferOp, rewriter)))
1438 return failure();
1439
1440 if (inputVectorTy.getElementType() !=
1441 xferOp.getShapedType().getElementType())
1442 return failure();
1443
1444 auto vec = getDataVector(xferOp);
1445 if (inputVectorTy.getScalableDims()[0]) {
1446
1447 return failure();
1448 }
1449
1450 int64_t dimSize = inputVectorTy.getShape()[0];
1451 Value source = xferOp.getBase();
1452 auto sourceType = isTensorOp(xferOp) ? xferOp.getShapedType() : Type();
1453
1454
1455 Location loc = xferOp.getLoc();
1456 for (int64_t i = 0; i < dimSize; ++i) {
1457 Value iv = rewriter.createarith::ConstantIndexOp(loc, i);
1458
1459 auto updatedSource = generateInBoundsCheck(
1460 rewriter, xferOp, iv, unpackedDim(xferOp),
1462
1464
1467
1468
1470 getExtractionIndices(xferOp, extractionIndices);
1472
1473 auto extracted =
1474 b.createvector::ExtractOp(loc, vec, extractionIndices);
1475 auto inBoundsAttr = dropFirstElem(b, xferOp.getInBoundsAttr());
1477 if (inputVectorTy.getRank() == 1) {
1478
1479
1480
1481 xferVec = b.createvector::BroadcastOp(
1482 loc, VectorType::get({}, extracted.getType()), extracted);
1483 } else {
1484 xferVec = extracted;
1485 }
1486 auto newXferOp = b.createvector::TransferWriteOp(
1487 loc, sourceType, xferVec, source, xferIndices,
1489 inBoundsAttr);
1490
1491 maybeAssignMask(b, xferOp, newXferOp, i);
1492
1493 return isTensorOp(xferOp) ? newXferOp->getResult(0) : Value();
1494 },
1495
1497 return isTensorOp(xferOp) ? source : Value();
1498 });
1499
1500 if (isTensorOp(xferOp))
1501 source = updatedSource;
1502 }
1503
1504 if (isTensorOp(xferOp))
1505 rewriter.replaceOp(xferOp, source);
1506 else
1507 rewriter.eraseOp(xferOp);
1508
1509 return success();
1510 }
1511 };
1512
1513 }
1514
1516
1517
1518
1519
1520
1521 template
1522 static std::optional<int64_t>
1523 get1dMemrefIndices(OpBuilder &b, OpTy xferOp, Value iv,
1525 auto indices = xferOp.getIndices();
1526 auto map = xferOp.getPermutationMap();
1527 assert(xferOp.getTransferRank() > 0 && "unexpected 0-d transfer");
1528
1529 memrefIndices.append(indices.begin(), indices.end());
1530 assert(map.getNumResults() == 1 &&
1531 "Expected 1 permutation map result for 1D transfer");
1532 if (auto expr = dyn_cast(map.getResult(0))) {
1533 Location loc = xferOp.getLoc();
1534 auto dim = expr.getPosition();
1536 bindDims(xferOp.getContext(), d0, d1);
1537 Value offset = memrefIndices[dim];
1538 memrefIndices[dim] =
1540 return dim;
1541 }
1542
1543 assert(xferOp.isBroadcastDim(0) &&
1544 "Expected AffineDimExpr or AffineConstantExpr");
1545 return std::nullopt;
1546 }
1547
1548
1549
1550 template
1551 struct Strategy1d;
1552
1553
1554 template <>
1555 struct Strategy1d {
1557 TransferReadOp xferOp, Value iv,
1560 auto dim = get1dMemrefIndices(b, xferOp, iv, indices);
1561 auto vec = loopState[0];
1562
1563
1564
1565 auto nextVec = generateInBoundsCheck(
1566 b, xferOp, iv, dim, TypeRange(xferOp.getVectorType()),
1567
1569 Value val = b.creatememref::LoadOp(loc, xferOp.getBase(), indices);
1570 return b.createvector::InsertElementOp(loc, val, vec, iv);
1571 },
1572
1574 b.createscf::YieldOp(loc, nextVec);
1575 }
1576
1577 static Value initialLoopState(OpBuilder &b, TransferReadOp xferOp) {
1578
1579 Location loc = xferOp.getLoc();
1580 return b.createvector::SplatOp(loc, xferOp.getVectorType(),
1581 xferOp.getPadding());
1582 }
1583 };
1584
1585
1586 template <>
1587 struct Strategy1d {
1589 TransferWriteOp xferOp, Value iv,
1592 auto dim = get1dMemrefIndices(b, xferOp, iv, indices);
1593
1594
1595 generateInBoundsCheck(
1596 b, xferOp, iv, dim,
1598 auto val =
1599 b.createvector::ExtractElementOp(loc, xferOp.getVector(), iv);
1600 b.creatememref::StoreOp(loc, val, xferOp.getBase(), indices);
1601 });
1602 b.createscf::YieldOp(loc);
1603 }
1604
1605 static Value initialLoopState(OpBuilder &b, TransferWriteOp xferOp) {
1607 }
1608 };
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641 template
1642 struct TransferOp1dConversion : public VectorToSCFPattern {
1643 using VectorToSCFPattern::VectorToSCFPattern;
1644
1645 LogicalResult matchAndRewrite(OpTy xferOp,
1647
1648 if (xferOp.getTransferRank() == 0)
1649 return failure();
1650 auto map = xferOp.getPermutationMap();
1651 auto memRefType = dyn_cast(xferOp.getShapedType());
1652
1653 if (!memRefType)
1654 return failure();
1655 if (xferOp.getVectorType().getRank() != 1)
1656 return failure();
1657 if (map.isMinorIdentity() && memRefType.isLastDimUnitStride())
1658 return failure();
1659
1660
1661 Location loc = xferOp.getLoc();
1662 auto vecType = xferOp.getVectorType();
1663 auto lb = rewriter.createarith::ConstantIndexOp(loc, 0);
1665 rewriter.createarith::ConstantIndexOp(loc, vecType.getDimSize(0));
1666 if (vecType.isScalable()) {
1668 rewriter.createvector::VectorScaleOp(loc, rewriter.getIndexType());
1669 ub = rewriter.createarith::MulIOp(loc, ub, vscale);
1670 }
1671 auto step = rewriter.createarith::ConstantIndexOp(loc, 1);
1672 auto loopState = Strategy1d::initialLoopState(rewriter, xferOp);
1673
1674
1678 Strategy1d::generateForLoopBody(b, loc, xferOp, iv, loopState);
1679 });
1680
1681 return success();
1682 }
1683 };
1684
1685 }
1686 }
1687
1691 patterns.add<lowering_n_d_unrolled::UnrollTransferReadConversion,
1692 lowering_n_d_unrolled::UnrollTransferWriteConversion>(
1694 } else {
1695 patterns.add<lowering_n_d::PrepareTransferReadConversion,
1696 lowering_n_d::PrepareTransferWriteConversion,
1697 lowering_n_d::TransferOpConversion,
1698 lowering_n_d::TransferOpConversion>(
1700 }
1701 if (options.lowerScalable) {
1702 patterns.add<lowering_n_d::ScalableTransposeTransferWriteConversion>(
1704 }
1705 if (options.targetRank == 1) {
1706 patterns.add<lowering_1_d::TransferOp1dConversion,
1707 lowering_1_d::TransferOp1dConversion>(
1709 }
1710 patterns.add<lowering_n_d::DecomposePrintOpConversion>(patterns.getContext(),
1712 }
1713
1714 namespace {
1715
1716 struct ConvertVectorToSCFPass
1717 : public impl::ConvertVectorToSCFBase {
1718 ConvertVectorToSCFPass() = default;
1720 this->fullUnroll = options.unroll;
1721 this->targetRank = options.targetRank;
1722 this->lowerTensors = options.lowerTensors;
1723 this->lowerScalable = options.lowerScalable;
1724 }
1725
1726 void runOnOperation() override {
1728 options.unroll = fullUnroll;
1729 options.targetRank = targetRank;
1730 options.lowerTensors = lowerTensors;
1731 options.lowerScalable = lowerScalable;
1732
1733
1736 lowerTransferPatterns);
1738 std::move(lowerTransferPatterns));
1739
1743 }
1744 };
1745
1746 }
1747
1748 std::unique_ptr
1750 return std::make_unique(options);
1751 }
MLIR_CRUNNERUTILS_EXPORT void printClose()
static MLIRContext * getContext(OpFoldResult val)
static llvm::ManagedStatic< PassManagerOptions > options
static Value max(ImplicitLocOpBuilder &builder, Value value, Value bound)
static void printOp(llvm::raw_ostream &os, Operation *op, OpPrintingFlags &flags)
static void getXferIndices(RewriterBase &rewriter, TransferOpType xferOp, AffineMap offsetMap, ArrayRef< Value > dimValues, SmallVector< Value, 4 > &indices)
For a vector TransferOpType xferOp, an empty indices vector, and an AffineMap representing offsets to...
static Operation * getAutomaticAllocationScope(Operation *op)
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: () -> ().
IntegerAttr getIndexAttr(int64_t value)
IntegerAttr getI64IntegerAttr(int64_t value)
MLIRContext * getContext() const
ImplicitLocOpBuilder maintains a 'current location', allowing use of the create<> method without spec...
OpTy create(Args &&...args)
Create an operation of specific op type at the current insertion point and location.
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.
RAII guard to reset the insertion point of the builder when destroyed.
This class helps build Operations.
Operation * clone(Operation &op, IRMapping &mapper)
Creates a deep copy of the specified operation, remapping any operands that use values outside of the...
void setInsertionPointToStart(Block *block)
Sets the insertion point to the start of the specified block.
void setInsertionPoint(Block *block, Block::iterator insertPoint)
Set the insertion point to the specified location.
Operation * create(const OperationState &state)
Creates an operation given the fields represented as an OperationState.
void setInsertionPointAfter(Operation *op)
Sets the insertion point to the node after the specified operation, which will cause subsequent inser...
A trait of region holding operations that define a new scope for automatic allocations,...
This class implements the operand iterators for the Operation class.
Operation is the basic unit of execution within MLIR.
unsigned getNumRegions()
Returns the number of regions held by this operation.
Region & getRegion(unsigned index)
Returns the region held by this operation at position 'index'.
void setAttr(StringAttr name, Attribute value)
If the an attribute exists with the specified name, change it to the new value.
Operation * getParentWithTrait()
Returns the closest surrounding parent operation with trait Trait.
user_range getUsers()
Returns a range of all users.
A special type of RewriterBase that coordinates the application of a rewrite pattern on the current I...
std::enable_if_t<!std::is_convertible< CallbackT, Twine >::value, LogicalResult > notifyMatchFailure(Location loc, CallbackT &&reasonCallback)
Used to notify the listener that the IR failed to be rewritten because of a match failure,...
virtual void 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.
void modifyOpInPlace(Operation *root, CallableT &&callable)
This method is a utility wrapper around an in-place modification of an operation.
OpTy replaceOpWithNewOp(Operation *op, Args &&...args)
Replace the results of the given (original) op with a new op that is created without verification (re...
This class provides an abstraction over the various different ranges of value types.
Instances of the Type class are uniqued, have an immutable identifier and an optional mutable compone...
This class provides an abstraction over the different types of ranges over Values.
This class represents an instance of an SSA value in the MLIR system, representing a computable value...
MLIRContext * getContext() const
Utility to get the associated MLIRContext that this value is defined in.
Type getType() const
Return the type of this value.
user_range getUsers() const
Operation * getDefiningOp() const
If this value is the result of an operation, return the operation that defines it.
This is a builder type that keeps local references to arguments.
Builder & dropDim(unsigned pos)
Erase a dim from shape @pos.
AffineApplyOp makeComposedAffineApply(OpBuilder &b, Location loc, AffineMap map, ArrayRef< OpFoldResult > operands)
Returns a composed AffineApplyOp by composing map and operands with other AffineApplyOps supplying th...
FailureOr< Value > getBuffer(RewriterBase &rewriter, Value value, const BufferizationOptions &options, const BufferizationState &state)
Lookup the buffer for the given value.
void populateVectorTransferPermutationMapLoweringPatterns(RewritePatternSet &patterns, PatternBenefit benefit=1)
Collect a set of transfer read/write lowering patterns that simplify the permutation map (e....
Value createOrFoldDimOp(OpBuilder &b, Location loc, Value source, int64_t dim)
Helper function that creates a memref::DimOp or tensor::DimOp depending on the type of source.
SmallVector< Value > getAsValues(OpBuilder &builder, Location loc, ArrayRef< OpFoldResult > foldResults)
Convert foldResults into Values.
auto makeVscaleConstantBuilder(PatternRewriter &rewriter, Location loc)
Returns a functor (int64_t -> Value) which returns a constant vscale multiple.
Include the generated interface declarations.
Type getType(OpFoldResult ofr)
Returns the int type of the integer in ofr.
void bindDims(MLIRContext *ctx, AffineExprTy &...exprs)
Bind a list of AffineExpr references to DimExpr at positions: [0 .
LogicalResult applyPatternsGreedily(Region ®ion, const FrozenRewritePatternSet &patterns, GreedyRewriteConfig config=GreedyRewriteConfig(), bool *changed=nullptr)
Rewrite ops in the given region, which must be isolated from above, by repeatedly applying the highes...
const FrozenRewritePatternSet & patterns
void populateVectorToSCFConversionPatterns(RewritePatternSet &patterns, const VectorTransferToSCFOptions &options=VectorTransferToSCFOptions())
Collect a set of patterns to convert from the Vector dialect to SCF + func.
auto get(MLIRContext *context, Ts &&...params)
Helper method that injects context only if needed, this helps unify some of the attribute constructio...
std::unique_ptr< Pass > createConvertVectorToSCFPass(const VectorTransferToSCFOptions &options=VectorTransferToSCFOptions())
Create a pass to convert a subset of vector ops to SCF.
OpRewritePattern is a wrapper around RewritePattern that allows for matching and rewriting against an...
When lowering an N-d vector transfer op to an (N-1)-d vector transfer op, a temporary buffer is creat...