44#include "llvm/ADT/ArrayRef.h"
45#include "llvm/ADT/Repeated.h"
46#include "llvm/ADT/STLExtras.h"
47#include "llvm/ADT/SmallVector.h"
48#include "llvm/ADT/SmallVectorExtras.h"
49#include "llvm/ADT/StringSet.h"
50#include "llvm/ADT/TypeSwitch.h"
51#include "llvm/Support/Casting.h"
57#include "mlir/Dialect/Vector/IR/VectorDialect.cpp.inc"
59#include "mlir/Dialect/Vector/IR/VectorEnums.cpp.inc"
76 if (
auto c = mask.getDefiningOp<arith::ConstantOp>()) {
80 if (
auto denseElts = llvm::dyn_cast<DenseIntElementsAttr>(c.getValue())) {
82 for (
bool b : denseElts.getValues<
bool>())
85 else if (!
b && val <= 0)
94 }
else if (
auto m = mask.getDefiningOp<ConstantMaskOp>()) {
99 auto shape = m.getType().getShape();
101 bool allFalse =
true;
102 for (
auto [maskIdx, dimSize] : llvm::zip_equal(masks,
shape)) {
103 if (maskIdx < dimSize)
112 }
else if (
auto m = mask.getDefiningOp<CreateMaskOp>()) {
116 auto maskOperands = m.getOperands();
117 for (
Value operand : maskOperands) {
118 if (
auto constantOp = operand.getDefiningOp<arith::ConstantOp>()) {
120 llvm::cast<IntegerAttr>(constantOp.getValue()).getInt();
133 vector::YieldOp::create(builder, loc);
139 switch (combiningKind) {
140 case CombiningKind::ADD:
141 case CombiningKind::MUL:
143 case CombiningKind::MINUI:
144 case CombiningKind::MINSI:
145 case CombiningKind::MAXUI:
146 case CombiningKind::MAXSI:
147 case CombiningKind::AND:
148 case CombiningKind::OR:
149 case CombiningKind::XOR:
151 case CombiningKind::MINNUMF:
152 case CombiningKind::MAXNUMF:
153 case CombiningKind::MINIMUMF:
154 case CombiningKind::MAXIMUMF:
155 case CombiningKind::MINIMUMNUMF:
156 case CombiningKind::MAXIMUMNUMF:
157 return llvm::isa<FloatType>(elementType);
187 VectorType vectorType) {
188 unsigned elementVectorRank = 0;
189 VectorType elementVectorType =
190 llvm::dyn_cast<VectorType>(shapedType.getElementType());
191 if (elementVectorType)
192 elementVectorRank += elementVectorType.getRank();
193 return vectorType.getRank() - elementVectorRank;
197 VectorType vectorType) {
200 if (shapedType.getRank() == 0 &&
206 shapedType.getRank(),
208 shapedType.getContext());
215 vector::TransferReadOp read) {
216 auto readMask = read.getMask();
217 auto writeMask = write.getMask();
223 bool couldBeSameSplat = readMask && (!writeMask || writeMask == readMask);
224 if (!couldBeSameSplat)
241 vector::TransferReadOp read) {
248 if (defWrite.isMasked() || read.isMasked())
250 return !defWrite.hasOutOfBoundsDim() &&
251 defWrite.getIndices() == read.getIndices() &&
252 defWrite.getVectorType() == read.getVectorType() &&
253 defWrite.getPermutationMap() == read.getPermutationMap() &&
254 ((!defWrite.getMask() && !read.getMask()) ||
259 vector::TransferWriteOp priorWrite) {
267 if (write.isMasked())
269 return priorWrite.getIndices() == write.getIndices() &&
270 priorWrite.getMask() == write.getMask() &&
271 priorWrite.getVectorType() == write.getVectorType() &&
272 priorWrite.getPermutationMap() == write.getPermutationMap();
276 VectorTransferOpInterface transferA, VectorTransferOpInterface transferB,
277 bool testDynamicValueUsingBounds) {
279 if (transferA.getVectorType() != transferB.getVectorType())
281 unsigned rankOffset = transferA.getLeadingShapedRank();
282 for (
unsigned i = 0, e = transferA.getIndices().size(); i < e; i++) {
283 Value indexA = transferA.getIndices()[i];
284 Value indexB = transferB.getIndices()[i];
288 if (i < rankOffset) {
291 if (cstIndexA.has_value() && cstIndexB.has_value()) {
292 if (*cstIndexA != *cstIndexB)
296 if (testDynamicValueUsingBounds) {
299 FailureOr<uint64_t> delta =
301 if (succeeded(delta) && *delta != 0)
304 FailureOr<bool> testEqual =
306 if (succeeded(testEqual) && !testEqual.value())
312 int64_t vectorDim = transferA.getVectorType().getDimSize(i - rankOffset);
313 if (cstIndexA.has_value() && cstIndexB.has_value()) {
314 int64_t distance = std::abs(*cstIndexA - *cstIndexB);
315 if (distance >= vectorDim)
319 if (testDynamicValueUsingBounds) {
322 FailureOr<int64_t> delta =
324 if (succeeded(delta) && std::abs(*delta) >= vectorDim)
327 FailureOr<int64_t> computeDelta =
329 if (succeeded(computeDelta)) {
330 if (std::abs(computeDelta.value()) >= vectorDim)
340 VectorTransferOpInterface transferB,
341 bool testDynamicValueUsingBounds) {
342 if (transferA.getBase() != transferB.getBase())
345 testDynamicValueUsingBounds);
355 for (
auto [posInDim, dimSize, offsetInDim] :
356 llvm::reverse(llvm::zip_equal(position,
shape, offsets))) {
358 if (posInDim < dimSize + offsetInDim)
362 posInDim = offsetInDim;
372 llvm::transform(values, std::back_inserter(ints), [](
Value value) {
374 assert(constOp &&
"Unexpected non-constant index");
375 return constOp.value();
385 foldResults, std::back_inserter(ints), [](
OpFoldResult foldResult) {
386 assert(isa<Attribute>(foldResult) &&
"Unexpected non-constant index");
387 return cast<IntegerAttr>(cast<Attribute>(foldResult)).getInt();
397 llvm::transform(foldResults, std::back_inserter(values),
399 if (
auto attr = dyn_cast<Attribute>(foldResult))
401 builder, loc, cast<IntegerAttr>(attr).getInt())
404 return cast<Value>(foldResult);
415 auto lhs =
mul.getLhs();
416 auto rhs =
mul.getRhs();
417 if (lhs.getDefiningOp<vector::VectorScaleOp>())
419 if (rhs.getDefiningOp<vector::VectorScaleOp>())
429 if (
auto intAttr = dyn_cast<IntegerAttr>(attr)) {
430 if (
auto intType = dyn_cast<IntegerType>(expectedType)) {
431 if (intAttr.getType() != expectedType)
432 return IntegerAttr::get(expectedType, intAttr.getInt());
438 if (
auto floatAttr = dyn_cast<FloatAttr>(attr)) {
439 auto intType = dyn_cast<IntegerType>(expectedType);
443 APFloat floatVal = floatAttr.getValue();
444 APInt intVal = floatVal.bitcastToAPInt();
445 return IntegerAttr::get(expectedType, intVal);
454 Type srcType, VectorType dstVectorType,
455 std::pair<VectorDim, VectorDim> *mismatchingDims) {
457 if (isa<VectorElementTypeInterface>(srcType) && dstVectorType &&
461 VectorType srcVectorType = llvm::dyn_cast<VectorType>(srcType);
465 int64_t srcRank = srcVectorType.getRank();
466 int64_t dstRank = dstVectorType.getRank();
467 if (srcRank > dstRank)
471 int64_t lead = dstRank - srcRank;
472 for (
int64_t dimIdx = 0; dimIdx < srcRank; ++dimIdx) {
475 bool foundMismatchingDims =
false;
478 int64_t srcDim = srcVectorType.getDimSize(dimIdx);
479 int64_t dstDim = dstVectorType.getDimSize(lead + dimIdx);
480 if (srcDim != 1 && srcDim != dstDim)
481 foundMismatchingDims =
true;
484 bool srcDimScalableFlag = srcVectorType.getScalableDims()[dimIdx];
485 bool dstDimScalableFlag = dstVectorType.getScalableDims()[lead + dimIdx];
486 if ((srcDim == 1 && srcDimScalableFlag && dstDim != 1) ||
489 (srcDimScalableFlag != dstDimScalableFlag &&
490 (srcDim != 1 || srcDimScalableFlag)))
491 foundMismatchingDims =
true;
493 if (foundMismatchingDims) {
494 if (mismatchingDims !=
nullptr) {
495 mismatchingDims->first.dim = srcDim;
496 mismatchingDims->first.isScalable = srcDimScalableFlag;
498 mismatchingDims->second.dim = dstDim;
499 mismatchingDims->second.isScalable = dstDimScalableFlag;
541struct VectorInlinerInterface :
public DialectInlinerInterface {
542 using DialectInlinerInterface::DialectInlinerInterface;
551void VectorDialect::initialize() {
553#define GET_ATTRDEF_LIST
554#include "mlir/Dialect/Vector/IR/VectorAttributes.cpp.inc"
559#include "mlir/Dialect/Vector/IR/VectorOps.cpp.inc"
562 addInterfaces<VectorInlinerInterface>();
564 declarePromisedInterfaces<memref::IndexedAccessOpInterface, LoadOp, StoreOp,
565 MaskedLoadOp, MaskedStoreOp, ExpandLoadOp,
567 declarePromisedInterfaces<bufferization::BufferizableOpInterface,
568 TransferReadOp, TransferWriteOp, GatherOp, MaskOp,
570 declarePromisedInterfaces<SubsetOpInterface, TransferReadOp,
572 declarePromisedInterface<SubsetExtractionOpInterface, TransferReadOp>();
573 declarePromisedInterface<SubsetInsertionOpInterface, TransferWriteOp>();
574 declarePromisedInterface<ConvertToLLVMPatternInterface, VectorDialect>();
585 return arith::ConstantOp::materialize(builder, value, type, loc);
601void vector::MultiDimReductionOp::build(
OpBuilder &builder,
604 CombiningKind kind) {
606 for (
const auto &en : llvm::enumerate(reductionMask))
608 reductionDims.push_back(en.index());
609 build(builder,
result, kind, source,
acc, reductionDims);
612OpFoldResult MultiDimReductionOp::fold(FoldAdaptor adaptor) {
614 if (getReductionDims().empty())
619std::optional<SmallVector<int64_t, 4>>
620MultiDimReductionOp::getShapeForUnroll() {
621 return llvm::to_vector<4>(getSourceVectorType().
getShape());
624LogicalResult MultiDimReductionOp::verify() {
626 int64_t sourceRank = getSourceVectorType().getRank();
628 for (
int64_t dim : getReductionDims()) {
629 if (dim < 0 || dim >= sourceRank)
630 return emitOpError(
"reduction dimension out of range: ") << dim;
632 return emitOpError(
"duplicate reduction dimension: ") << dim;
633 isReduced[dim] =
true;
638 Type inferredReturnType;
639 auto sourceScalableDims = getSourceVectorType().getScalableDims();
640 for (
auto [dimIdx, dimSize] :
641 llvm::enumerate(getSourceVectorType().
getShape()))
642 if (!isReduced[dimIdx]) {
643 targetShape.push_back(dimSize);
644 scalableDims.push_back(sourceScalableDims[dimIdx]);
647 if (targetShape.empty())
648 inferredReturnType = getSourceVectorType().getElementType();
650 inferredReturnType = VectorType::get(
651 targetShape, getSourceVectorType().
getElementType(), scalableDims);
652 if (
getType() != inferredReturnType)
653 return emitOpError() <<
"destination type " <<
getType()
654 <<
" is incompatible with source type "
655 << getSourceVectorType();
661Type MultiDimReductionOp::getExpectedMaskType() {
662 auto vecType = getSourceVectorType();
663 return VectorType::get(vecType.getShape(),
664 IntegerType::get(vecType.getContext(), 1),
665 vecType.getScalableDims());
674struct ElideUnitDimsInMultiDimReduction
678 LogicalResult matchAndRewrite(MultiDimReductionOp reductionOp,
679 PatternRewriter &rewriter)
const override {
680 ArrayRef<int64_t> shape = reductionOp.getSourceVectorType().getShape();
681 for (
const auto &dim :
enumerate(shape)) {
682 if (reductionOp.isReducedDim(dim.index()) && dim.value() != 1)
687 OpBuilder::InsertionGuard guard(rewriter);
690 if (reductionOp.isMasked()) {
692 rootOp = reductionOp.getMaskingOp();
693 mask = reductionOp.getMaskingOp().getMask();
695 rootOp = reductionOp;
698 Location loc = reductionOp.getLoc();
699 Value acc = reductionOp.getAcc();
701 if (
auto dstVecType = dyn_cast<VectorType>(reductionOp.getDestType())) {
703 VectorType newMaskType =
704 VectorType::get(dstVecType.getShape(), rewriter.
getI1Type(),
705 dstVecType.getScalableDims());
706 mask = vector::ShapeCastOp::create(rewriter, loc, newMaskType, mask);
708 cast = vector::ShapeCastOp::create(
709 rewriter, loc, reductionOp.getDestType(), reductionOp.getSource());
714 mask = vector::ExtractOp::create(rewriter, loc, mask);
715 cast = vector::ExtractOp::create(rewriter, loc, reductionOp.getSource());
720 cast,
nullptr, mask);
727void MultiDimReductionOp::getCanonicalizationPatterns(
729 results.
add<ElideUnitDimsInMultiDimReduction>(context);
738 arith::FastMathFlags fastMathFlags) {
744 arith::FastMathFlags fastMathFlags) {
746 llvm::cast<VectorType>(
vector.getType()).getElementType(), kind,
vector,
750LogicalResult ReductionOp::verify() {
752 int64_t rank = getSourceVectorType().getRank();
754 return emitOpError(
"unsupported reduction rank: ") << rank;
757 Type eltType = getDest().getType();
759 return emitOpError(
"unsupported reduction type '")
760 << eltType <<
"' for kind '" << stringifyCombiningKind(getKind())
769Type ReductionOp::getExpectedMaskType() {
770 auto vecType = getSourceVectorType();
771 return VectorType::get(vecType.getShape(),
772 IntegerType::get(vecType.getContext(), 1),
773 vecType.getScalableDims());
780 case arith::AtomicRMWKind::addf:
781 case arith::AtomicRMWKind::addi:
782 return vector::ReductionOp::create(builder,
vector.getLoc(),
783 CombiningKind::ADD,
vector);
784 case arith::AtomicRMWKind::mulf:
785 case arith::AtomicRMWKind::muli:
786 return vector::ReductionOp::create(builder,
vector.getLoc(),
787 CombiningKind::MUL,
vector);
788 case arith::AtomicRMWKind::minimumf:
789 return vector::ReductionOp::create(builder,
vector.getLoc(),
790 CombiningKind::MINIMUMF,
vector);
791 case arith::AtomicRMWKind::mins:
792 return vector::ReductionOp::create(builder,
vector.getLoc(),
793 CombiningKind::MINSI,
vector);
794 case arith::AtomicRMWKind::minu:
795 return vector::ReductionOp::create(builder,
vector.getLoc(),
796 CombiningKind::MINUI,
vector);
797 case arith::AtomicRMWKind::maximumf:
798 return vector::ReductionOp::create(builder,
vector.getLoc(),
799 CombiningKind::MAXIMUMF,
vector);
800 case arith::AtomicRMWKind::maxs:
801 return vector::ReductionOp::create(builder,
vector.getLoc(),
802 CombiningKind::MAXSI,
vector);
803 case arith::AtomicRMWKind::maxu:
804 return vector::ReductionOp::create(builder,
vector.getLoc(),
805 CombiningKind::MAXUI,
vector);
806 case arith::AtomicRMWKind::andi:
807 return vector::ReductionOp::create(builder,
vector.getLoc(),
808 CombiningKind::AND,
vector);
809 case arith::AtomicRMWKind::ori:
810 return vector::ReductionOp::create(builder,
vector.getLoc(),
811 CombiningKind::OR,
vector);
812 case arith::AtomicRMWKind::minnumf:
813 return vector::ReductionOp::create(builder,
vector.getLoc(),
814 CombiningKind::MINNUMF,
vector);
815 case arith::AtomicRMWKind::maxnumf:
816 return vector::ReductionOp::create(builder,
vector.getLoc(),
817 CombiningKind::MAXNUMF,
vector);
818 case arith::AtomicRMWKind::xori:
819 return vector::ReductionOp::create(builder,
vector.getLoc(),
820 CombiningKind::XOR,
vector);
828std::optional<SmallVector<int64_t, 4>> ReductionOp::getShapeForUnroll() {
829 return llvm::to_vector<4>(getSourceVectorType().
getShape());
836 LogicalResult matchAndRewrite(ReductionOp reductionOp,
841 cast<vector::MaskableOpInterface>(reductionOp.getOperation());
844 if (maskableOp.isMasked()) {
846 rootOp = maskableOp.getMaskingOp();
847 mask = maskableOp.getMaskingOp().getMask();
849 rootOp = reductionOp;
852 auto vectorType = reductionOp.getSourceVectorType();
853 if (vectorType.getRank() != 0 && vectorType.getDimSize(0) != 1)
856 Location loc = reductionOp.getLoc();
858 mask = ExtractOp::create(rewriter, loc, mask);
859 Value
result = ExtractOp::create(rewriter, loc, reductionOp.getVector());
861 if (Value acc = reductionOp.getAcc())
864 reductionOp.getFastmathAttr(), mask);
874 results.
add<ElideSingleElementReduction>(context);
888 getIndexingMapsAttrName(
result.name),
892 getIteratorTypesAttrName(
result.name),
895 return IteratorTypeAttr::get(builder.getContext(), t);
903 build(builder,
result, lhs, rhs,
acc, indexingMaps, iteratorTypes,
904 ContractionOp::getDefaultKind());
910 ArrayAttr iteratorTypes, CombiningKind kind,
911 arith::FastMathFlags fastMathFlags) {
914 result.addAttribute(getIndexingMapsAttrName(
result.name), indexingMaps);
915 result.addAttribute(getIteratorTypesAttrName(
result.name), iteratorTypes);
917 CombiningKindAttr::get(builder.
getContext(), kind));
918 if (fastMathFlags != arith::FastMathFlags::none)
920 getFastmathAttrName(
result.name),
921 arith::FastMathFlagsAttr::get(builder.
getContext(), fastMathFlags));
932 DictionaryAttr dictAttr;
946 result.attributes.append(dictAttr.getValue().begin(),
947 dictAttr.getValue().end());
953 auto iteratorTypes = dyn_cast_or_null<ArrayAttr>(
954 result.attributes.get(getIteratorTypesAttrName(
result.name)));
955 if (!iteratorTypes) {
957 <<
"expected " << getIteratorTypesAttrName(
result.name)
958 <<
" array attribute";
963 for (StringRef s : iteratorTypes.getAsValueRange<StringAttr>()) {
964 auto maybeIteratorType = symbolizeIteratorType(s);
965 if (!maybeIteratorType.has_value())
966 return parser.
emitError(loc) <<
"unexpected iterator_type (" << s <<
")";
968 iteratorTypeAttrs.push_back(
969 IteratorTypeAttr::get(parser.
getContext(), maybeIteratorType.value()));
971 result.attributes.set(getIteratorTypesAttrName(
result.name),
974 if (!
result.attributes.get(getKindAttrName(
result.name))) {
976 getKindAttrName(
result.name),
977 CombiningKindAttr::get(
result.getContext(),
978 ContractionOp::getDefaultKind()));
980 if (masksInfo.empty())
982 if (masksInfo.size() != 2)
984 "expected zero or exactly 2 vector mask operands");
985 auto lhsType = llvm::cast<VectorType>(types[0]);
986 auto rhsType = llvm::cast<VectorType>(types[1]);
988 std::array<VectorType, 2> maskTypes = {
998 auto attrNames = getTraitAttrNames();
1000 traitAttrsSet.insert_range(attrNames);
1001 NamedAttrList allAttrs(getOperation()->getRawDictionaryAttrs());
1002 getOperation()->getName().walkInherentAttrs(
1004 [&](StringRef name,
Attribute &attr) { allAttrs.append(name, attr); });
1006 for (
auto attr : allAttrs) {
1007 if (attr.getName() == getIteratorTypesAttrName()) {
1008 auto iteratorTypes =
1009 llvm::cast<ArrayAttr>(attr.getValue())
1010 .getAsValueRange<IteratorTypeAttr, IteratorType>();
1016 llvm::map_to_vector(iteratorTypes, [&](IteratorType t) ->
Attribute {
1017 return StringAttr::get(
getContext(), stringifyIteratorType(t));
1020 attrs.emplace_back(getIteratorTypesAttrName(),
1021 ArrayAttr::get(
getContext(), iteratorTypeNames));
1022 }
else if (traitAttrsSet.count(attr.getName().strref()) > 0) {
1024 if (attr.getName() == getFastmathAttrName() &&
1025 llvm::cast<arith::FastMathFlagsAttr>(attr.getValue()).getValue() ==
1026 arith::FastMathFlags::none)
1028 attrs.push_back(attr);
1032 auto dictAttr = DictionaryAttr::get(
getContext(), attrs);
1033 p <<
" " << dictAttr <<
" " << getLhs() <<
", ";
1034 p << getRhs() <<
", " << getAcc();
1037 p <<
" : " << getLhs().getType() <<
", " << getRhs().getType() <<
" into "
1042 const std::vector<std::pair<int64_t, int64_t>> &map) {
1043 for (
auto &dimPair : map) {
1044 if (dimPair.first < 0 || dimPair.first >= lhsType.getRank() ||
1045 dimPair.second < 0 || dimPair.second >= rhsType.getRank() ||
1046 lhsType.getDimSize(dimPair.first) != rhsType.getDimSize(dimPair.second))
1053 ContractionOp op, VectorType lhsType, VectorType rhsType,
Type accType,
1055 const std::vector<std::pair<int64_t, int64_t>> &contractingDimMap,
1056 const std::vector<std::pair<int64_t, int64_t>> &batchDimMap) {
1059 for (
auto &dimPair : contractingDimMap) {
1060 lhsContractingDimSet.insert(dimPair.first);
1061 rhsContractingDimSet.insert(dimPair.second);
1064 llvm::make_second_range(batchDimMap));
1068 for (
int64_t i = 0, e = lhsType.getRank(); i < e; ++i) {
1069 if (lhsContractingDimSet.count(i) > 0)
1071 expectedResultDims.push_back(lhsType.getDimSize(i));
1075 for (
int64_t i = 0, e = rhsType.getRank(); i < e; ++i) {
1076 if (rhsContractingDimSet.count(i) > 0 || rhsBatchDimSet.count(i) > 0)
1078 expectedResultDims.push_back(rhsType.getDimSize(i));
1082 if (expectedResultDims.empty()) {
1084 if (llvm::isa<VectorType>(resType) || llvm::isa<VectorType>(accType))
1085 return op.emitOpError(
"invalid accumulator/result vector shape");
1088 auto resVectorType = llvm::dyn_cast<VectorType>(resType);
1089 auto accVectorType = llvm::dyn_cast<VectorType>(accType);
1090 if (!resVectorType || !accVectorType)
1091 return op.emitOpError(
"invalid accumulator/result vector shape");
1097 AffineMap lhsMap = op.getIndexingMapsArray()[0];
1098 AffineMap rhsMap = op.getIndexingMapsArray()[1];
1100 return op.emitOpError(
1101 "expected all dimensions to be either a LHS or a RHS dimension");
1104 {std::make_pair(lhsType, lhsMap), std::make_pair(rhsType, rhsMap)}) {
1105 VectorType v = pair.first;
1106 auto map = pair.second;
1107 for (
unsigned idx = 0, e = v.getRank(); idx < e; ++idx) {
1108 unsigned pos = map.getDimPosition(idx);
1113 if (!llvm::all_of(extents, [](
AffineExpr e) {
return e; }))
1114 return op.emitOpError(
"expected all dimensions to get an extent as "
1115 "either a LHS or a RHS dimension");
1117 AffineMap resMap = op.getIndexingMapsArray()[2];
1122 assert(llvm::all_of(expectedMap.
getResults(),
1123 llvm::IsaPred<AffineConstantExpr>) &&
1124 "expected constant extent along all dimensions.");
1126 auto expectedShape =
1128 return cast<AffineConstantExpr>(e).getValue();
1131 VectorType::get(expectedShape, resVectorType.getElementType(),
1132 resVectorType.getScalableDims());
1133 if (resVectorType != expected || accVectorType != expected)
1134 return op.emitOpError(
1135 "invalid accumulator/result vector shape, expected: ")
1141LogicalResult ContractionOp::verify() {
1142 VectorType lhsType = getLhsType();
1143 VectorType rhsType = getRhsType();
1144 Type accType = getAccType();
1145 Type resType = getResultType();
1147 if (llvm::isa<IntegerType>(lhsType.getElementType())) {
1148 if (!lhsType.getElementType().isSignlessInteger())
1149 return emitOpError(
"only supports signless integer types");
1153 if (getIndexingMapsArray().size() != 3)
1154 return emitOpError(
"expected an indexing map for each vector operand");
1159 unsigned numIterators = getIteratorTypes().getValue().size();
1160 for (
const auto &it : llvm::enumerate(getIndexingMapsArray())) {
1161 auto index = it.index();
1162 auto map = it.value();
1163 if (map.getNumSymbols() != 0)
1164 return emitOpError(
"expected indexing map ")
1165 <<
index <<
" to have no symbols";
1166 auto vectorType = llvm::dyn_cast<VectorType>(getOperand(
index).
getType());
1167 unsigned rank = vectorType ? vectorType.getShape().size() : 0;
1170 if (map.getNumDims() != numIterators)
1171 return emitOpError(
"expected indexing map ")
1172 <<
index <<
" to have " << numIterators <<
" number of inputs";
1173 if (map.getNumResults() != rank)
1174 return emitOpError(
"expected indexing map ")
1175 <<
index <<
" to have " << rank <<
" number of outputs";
1176 if (!map.isProjectedPermutation())
1177 return emitOpError(
"expected indexing map ")
1178 <<
index <<
" to be a projected permutation of its inputs";
1181 auto contractingDimMap = getContractingDimMap();
1182 auto batchDimMap = getBatchDimMap();
1185 if (contractingDimMap.empty())
1186 return emitOpError(
"expected at least one contracting dimension pair");
1189 if (!
verifyDimMap(lhsType, rhsType, contractingDimMap))
1190 return emitOpError(
"invalid contracting dimension map");
1194 return emitOpError(
"invalid batch dimension map");
1198 contractingDimMap, batchDimMap)))
1201 if (!getKindAttr()) {
1202 return emitOpError(
"expected 'kind' attribute of type CombiningKind (e.g. "
1203 "'vector.kind<add>')");
1207 auto vectorType = llvm::dyn_cast<VectorType>(resType);
1208 auto elementType = vectorType ? vectorType.getElementType() : resType;
1210 return emitOpError(
"unsupported contraction type");
1213 return cast<IndexingMapOpInterface>(this->getOperation()).verifyImpl();
1220Type ContractionOp::getExpectedMaskType() {
1221 auto indexingMaps = this->getIndexingMapsArray();
1224 VectorType lhsType = this->getLhsType();
1225 VectorType rhsType = this->getRhsType();
1227 unsigned numVecDims = lhsIdxMap.
getNumDims();
1233 for (
auto [dimIdx, dimSize] : llvm::enumerate(lhsType.getShape())) {
1236 lhsType.getScalableDims()[dimIdx];
1238 for (
auto [dimIdx, dimSize] : llvm::enumerate(rhsType.getShape())) {
1241 rhsType.getScalableDims()[dimIdx];
1244 assert(ShapedType::isStaticShape(maskShape) &&
1245 "Mask shape couldn't be computed");
1247 return VectorType::get(maskShape,
1248 IntegerType::get(lhsType.getContext(), 1),
1249 maskShapeScalableDims);
1254 getIteratorTypesAttrName(), getKindAttrName(),
1255 getFastmathAttrName()};
1265static std::vector<std::pair<int64_t, int64_t>>
1267 IteratorType targetIteratorType,
MLIRContext *context) {
1268 std::vector<std::pair<int64_t, int64_t>> dimMap;
1269 for (
const auto &it : llvm::enumerate(iteratorTypes)) {
1270 auto iteratorType = llvm::cast<IteratorTypeAttr>(it.value()).getValue();
1271 if (iteratorType != targetIteratorType)
1277 if (lhsDim >= 0 && rhsDim >= 0)
1278 dimMap.emplace_back(lhsDim, rhsDim);
1283void ContractionOp::getIterationBounds(
1285 auto lhsShape = getLhsType().getShape();
1286 auto resVectorType = llvm::dyn_cast<VectorType>(getResultType());
1288 for (
const auto &it : llvm::enumerate(getIteratorTypes())) {
1291 auto iteratorType = llvm::cast<IteratorTypeAttr>(it.value()).getValue();
1292 if (iteratorType == IteratorType::reduction) {
1295 assert(lhsDimIndex >= 0);
1296 iterationBounds.push_back(lhsShape[lhsDimIndex]);
1301 assert(resDimIndex >= 0);
1302 assert(resVectorType !=
nullptr);
1303 iterationBounds.push_back(resVectorType.getShape()[resDimIndex]);
1307void ContractionOp::getIterationIndexMap(
1309 unsigned numMaps = getIndexingMapsArray().size();
1310 iterationIndexMap.resize(numMaps);
1311 for (
const auto &it : llvm::enumerate(getIndexingMapsArray())) {
1312 auto index = it.index();
1313 auto map = it.value();
1314 for (
unsigned i = 0, e = map.getNumResults(); i < e; ++i) {
1315 auto dim = cast<AffineDimExpr>(map.getResult(i));
1316 iterationIndexMap[
index][dim.getPosition()] = i;
1321std::vector<std::pair<int64_t, int64_t>> ContractionOp::getContractingDimMap() {
1323 return getDimMap(indexingMaps, getIteratorTypes(), IteratorType::reduction,
1327std::vector<std::pair<int64_t, int64_t>> ContractionOp::getBatchDimMap() {
1329 return getDimMap(indexingMaps, getIteratorTypes(), IteratorType::parallel,
1333std::optional<SmallVector<int64_t, 4>> ContractionOp::getShapeForUnroll() {
1335 getIterationBounds(
shape);
1357template <
typename AddOpType>
1363 auto canonicalize = [&](
Value maybeContraction,
1364 Value otherOperand) -> vector::ContractionOp {
1365 vector::ContractionOp contractionOp =
1366 dyn_cast_or_null<vector::ContractionOp>(
1369 return vector::ContractionOp();
1370 if (
auto maybeZero = dyn_cast_or_null<arith::ConstantOp>(
1371 contractionOp.getAcc().getDefiningOp())) {
1372 if (maybeZero.getValue() ==
1373 rewriter.
getZeroAttr(contractionOp.getAcc().getType())) {
1375 bvm.
map(contractionOp.getAcc(), otherOperand);
1376 auto newContraction =
1377 cast<vector::ContractionOp>(rewriter.
clone(*contractionOp, bvm));
1378 rewriter.
replaceOp(addOp, newContraction.getResult());
1379 return newContraction;
1382 return vector::ContractionOp();
1385 Value a = addOp->getOperand(0),
b = addOp->getOperand(1);
1386 vector::ContractionOp
contract = canonicalize(a,
b);
1411 setResultRanges(getResult(), argRanges.front());
1416 auto vectorTy = cast<VectorType>(source.
getType());
1441 build(builder,
result, source, dynamicPos,
1446ExtractOp::inferReturnTypes(
MLIRContext *, std::optional<Location>,
1447 ExtractOp::Adaptor adaptor,
1449 auto vectorType = llvm::cast<VectorType>(adaptor.getSource().getType());
1450 if (
static_cast<int64_t>(adaptor.getStaticPosition().size()) ==
1451 vectorType.getRank()) {
1452 inferredReturnTypes.push_back(vectorType.getElementType());
1454 auto n = std::min<size_t>(adaptor.getStaticPosition().size(),
1455 vectorType.getRank());
1456 inferredReturnTypes.push_back(VectorType::get(
1457 vectorType.getShape().drop_front(n), vectorType.getElementType(),
1458 vectorType.getScalableDims().drop_front(n)));
1463LogicalResult vector::ExtractOp::verify() {
1464 if (
auto resTy = dyn_cast<VectorType>(getResult().
getType()))
1465 if (resTy.getRank() == 0)
1467 "expected a scalar instead of a 0-d vector as the result type");
1470 auto dynamicMarkersCount =
1471 llvm::count_if(getStaticPosition(), ShapedType::isDynamic);
1472 if (
static_cast<size_t>(dynamicMarkersCount) != getDynamicPosition().size())
1474 "mismatch between dynamic and static positions (kDynamic marker but no "
1475 "corresponding dynamic position) -- this can only happen due to an "
1476 "incorrect fold/rewrite");
1477 auto position = getMixedPosition();
1478 if (position.size() >
static_cast<unsigned>(getSourceVectorType().getRank()))
1480 "expected position attribute of rank no greater than vector rank");
1481 for (
auto [idx, pos] : llvm::enumerate(position)) {
1482 if (
auto attr = dyn_cast<Attribute>(pos)) {
1483 int64_t constIdx = cast<IntegerAttr>(attr).getInt();
1485 constIdx, kPoisonIndex, getSourceVectorType().getDimSize(idx))) {
1486 return emitOpError(
"expected position attribute #")
1488 <<
" to be a non-negative integer smaller than the "
1489 "corresponding vector dimension or poison (-1)";
1496template <
typename IntType>
1498 return llvm::map_to_vector<4>(
1499 arrayAttr.getAsRange<IntegerAttr>(),
1500 [](IntegerAttr attr) { return static_cast<IntType>(attr.getInt()); });
1506 if (!extractOp.getSource().getDefiningOp<ExtractOp>())
1510 if (extractOp.hasDynamicPosition())
1514 ExtractOp currentOp = extractOp;
1516 globalPosition.append(extrPos.rbegin(), extrPos.rend());
1517 while (ExtractOp nextOp = currentOp.getSource().getDefiningOp<ExtractOp>()) {
1520 if (currentOp.hasDynamicPosition())
1523 globalPosition.append(extrPos.rbegin(), extrPos.rend());
1525 extractOp.setOperand(0, currentOp.getSource());
1528 std::reverse(globalPosition.begin(), globalPosition.end());
1529 extractOp.setStaticPosition(globalPosition);
1541class ExtractFromInsertTransposeChainState {
1543 ExtractFromInsertTransposeChainState(ExtractOp e);
1552 template <
typename ContainerA,
typename ContainerB>
1553 bool isContainedWithin(
const ContainerA &a,
const ContainerB &
b) {
1554 return a.size() <=
b.size() &&
1555 std::equal(a.begin(), a.begin() + a.size(),
b.begin());
1562 template <
typename ContainerA,
typename ContainerB>
1563 bool intersectsWhereNonNegative(
const ContainerA &a,
const ContainerB &
b) {
1564 for (
auto [elemA, elemB] : llvm::zip(a,
b)) {
1565 if (elemA < 0 || elemB < 0)
1576 return (sentinels == ArrayRef(extractPosition).drop_front(extractedRank));
1580 void updateStateForNextIteration(Value v) {
1587 LogicalResult handleTransposeOp();
1590 LogicalResult handleInsertOpWithMatchingPos(Value &res);
1605 LogicalResult handleInsertOpWithPrefixPos(Value &res);
1610 Value tryToFoldExtractOpInPlace(Value source);
1612 ExtractOp extractOp;
1614 int64_t extractedRank;
1616 InsertOp nextInsertOp;
1617 TransposeOp nextTransposeOp;
1627 SmallVector<int64_t> sentinels;
1628 SmallVector<int64_t> extractPosition;
1632ExtractFromInsertTransposeChainState::ExtractFromInsertTransposeChainState(
1634 : extractOp(e), vectorRank(extractOp.getSourceVectorType().getRank()),
1635 extractedRank(extractOp.getNumIndices()) {
1636 assert(vectorRank >= extractedRank &&
"Extracted position overflow");
1637 sentinels.reserve(vectorRank - extractedRank);
1638 for (
int64_t i = 0, e = vectorRank - extractedRank; i < e; ++i)
1639 sentinels.push_back(-(i + 1));
1641 extractOp.getStaticPosition().end());
1647LogicalResult ExtractFromInsertTransposeChainState::handleTransposeOp() {
1649 if (extractOp.hasDynamicPosition())
1652 if (!nextTransposeOp)
1655 nextTransposeOp.getPermutation(), extractOp.getContext()));
1662ExtractFromInsertTransposeChainState::handleInsertOpWithMatchingPos(
1665 if (extractOp.hasDynamicPosition() || nextInsertOp.hasDynamicPosition())
1668 ArrayRef<int64_t> insertedPos = nextInsertOp.getStaticPosition();
1669 if (insertedPos != llvm::ArrayRef(
extractPosition).take_front(extractedRank))
1672 res = nextInsertOp.getValueToStore();
1681ExtractFromInsertTransposeChainState::handleInsertOpWithPrefixPos(Value &res) {
1683 if (extractOp.hasDynamicPosition() || nextInsertOp.hasDynamicPosition())
1686 ArrayRef<int64_t> insertedPos = nextInsertOp.getStaticPosition();
1696 res = nextInsertOp.getValueToStore();
1704Value ExtractFromInsertTransposeChainState::tryToFoldExtractOpInPlace(
1707 if (extractOp.hasDynamicPosition())
1711 bool nothingToFold = (source == extractOp.getSource());
1712 if (nothingToFold || !canFold())
1716 OpBuilder
b(extractOp.getContext());
1717 extractOp.setStaticPosition(
1719 extractOp.getSourceMutable().assign(source);
1720 return extractOp.getResult();
1724Value ExtractFromInsertTransposeChainState::fold() {
1726 if (extractOp.hasDynamicPosition())
1729 Value valueToExtractFrom = extractOp.getSource();
1730 updateStateForNextIteration(valueToExtractFrom);
1731 while (nextInsertOp || nextTransposeOp) {
1734 if (succeeded(handleTransposeOp())) {
1735 valueToExtractFrom = nextTransposeOp.getVector();
1736 updateStateForNextIteration(valueToExtractFrom);
1742 if (succeeded(handleInsertOpWithMatchingPos(
result)))
1747 if (succeeded(handleInsertOpWithPrefixPos(
result)))
1748 return tryToFoldExtractOpInPlace(
result);
1752 ArrayRef<int64_t> insertedPos = nextInsertOp.getStaticPosition();
1758 valueToExtractFrom = nextInsertOp.getDest();
1759 updateStateForNextIteration(valueToExtractFrom);
1762 return tryToFoldExtractOpInPlace(valueToExtractFrom);
1767 auto hasZeroDimVectorType = [](
Type type) ->
bool {
1768 auto vecType = dyn_cast<VectorType>(type);
1769 return vecType && vecType.getRank() == 0;
1779 if (isa<BroadcastOp>(op))
1782 auto shapeCast = dyn_cast<ShapeCastOp>(op);
1790 VectorType srcType = shapeCast.getSourceVectorType();
1792 uint64_t srcRank = srcType.getRank();
1794 return dstShape.size() >= srcRank && dstShape.take_back(srcRank) == srcShape;
1820 Operation *defOp = extractOp.getSource().getDefiningOp();
1827 if (extractOp.getType() == input.
getType())
1833 auto inputType = llvm::dyn_cast<VectorType>(input.
getType());
1834 auto extractType = llvm::dyn_cast<VectorType>(extractOp.getType());
1835 unsigned inputRank = inputType ? inputType.getRank() : 0;
1836 unsigned broadcastRank = extractOp.getSourceVectorType().getRank();
1837 unsigned extractRank = extractType ? extractType.getRank() : 0;
1840 if (extractRank > inputRank)
1844 assert(inputType &&
"input must be a vector type because of previous checks");
1853 extractType.getShape() != inputShape.take_back(extractRank))
1858 unsigned deltaOverall = inputRank - extractRank;
1859 unsigned deltaBroadcast = broadcastRank - inputRank;
1863 for (
auto [i, size] : llvm::enumerate(inputShape.take_front(deltaOverall))) {
1864 newPositions[i] = size == 1 ? zero : oldPositions[i + deltaBroadcast];
1867 extractOp->setOperands(
1868 llvm::to_vector(llvm::concat<Value>(
ValueRange(input), dynPos)));
1869 extractOp.setStaticPosition(staticPos);
1870 return extractOp.getResult();
1886 if (extractOp.hasDynamicPosition())
1889 auto shuffleOp = extractOp.getSource().getDefiningOp<ShuffleOp>();
1894 if (shuffleOp.getResultVectorType().getRank() != 1)
1897 int64_t inputVecSize = shuffleOp.getV1().getType().getShape()[0];
1898 auto shuffleMask = shuffleOp.getMask();
1899 int64_t extractIdx = extractOp.getStaticPosition()[0];
1900 int64_t shuffleIdx = shuffleMask[extractIdx];
1903 if (shuffleIdx < inputVecSize) {
1904 extractOp.setOperand(0, shuffleOp.getV1());
1905 extractOp.setStaticPosition({shuffleIdx});
1907 extractOp.setOperand(0, shuffleOp.getV2());
1908 extractOp.setStaticPosition({shuffleIdx - inputVecSize});
1911 return extractOp.getResult();
1917 if (extractOp.hasDynamicPosition())
1920 auto shapeCastOp = extractOp.getSource().getDefiningOp<vector::ShapeCastOp>();
1925 auto getDimReverse = [](VectorType type,
int64_t n) {
1926 return type.getShape().take_back(n + 1).front();
1929 llvm::isa<VectorType>(extractOp.getType())
1930 ? llvm::cast<VectorType>(extractOp.getType()).getRank()
1932 if (destinationRank > shapeCastOp.getSourceVectorType().getRank())
1934 if (destinationRank > 0) {
1935 auto destinationType =
1936 llvm::cast<VectorType>(extractOp.getResult().getType());
1937 for (
int64_t i = 0; i < destinationRank; i++) {
1941 if (getDimReverse(shapeCastOp.getSourceVectorType(), i) !=
1942 getDimReverse(destinationType, i))
1949 std::reverse(extractedPos.begin(), extractedPos.end());
1952 for (
int64_t i = 0, e = extractedPos.size(); i < e; i++) {
1953 strides.push_back(stride);
1955 getDimReverse(extractOp.getSourceVectorType(), i + destinationRank);
1963 shapeCastOp.getSourceVectorType().getRank() - destinationRank;
1965 for (
int64_t i = 0; i < numDimension; i++) {
1966 newStrides.push_back(stride);
1968 getDimReverse(shapeCastOp.getSourceVectorType(), i + destinationRank);
1970 std::reverse(newStrides.begin(), newStrides.end());
1974 extractOp.setStaticPosition(newPosition);
1975 extractOp.setOperand(0, shapeCastOp.getSource());
1976 return extractOp.getResult();
1982 if (extractOp.hasDynamicPosition())
1985 auto extractStridedSliceOp =
1986 extractOp.getSource().getDefiningOp<vector::ExtractStridedSliceOp>();
1987 if (!extractStridedSliceOp)
1996 if (extractStridedSliceOp.hasNonUnitStrides())
2002 while (!sliceOffsets.empty()) {
2003 size_t lastOffset = sliceOffsets.size() - 1;
2004 if (sliceOffsets.back() != 0 ||
2005 extractStridedSliceOp.getType().getDimSize(lastOffset) !=
2006 extractStridedSliceOp.getSourceVectorType().getDimSize(lastOffset))
2008 sliceOffsets.pop_back();
2010 unsigned destinationRank = 0;
2011 if (
auto vecType = llvm::dyn_cast<VectorType>(extractOp.getType()))
2012 destinationRank = vecType.getRank();
2015 if (destinationRank > extractStridedSliceOp.getSourceVectorType().getRank() -
2016 sliceOffsets.size())
2020 assert(extractedPos.size() >= sliceOffsets.size());
2021 for (
size_t i = 0, e = sliceOffsets.size(); i < e; i++)
2022 extractedPos[i] = extractedPos[i] + sliceOffsets[i];
2023 extractOp.getSourceMutable().assign(extractStridedSliceOp.getSource());
2027 extractOp.setStaticPosition(extractedPos);
2028 return extractOp.getResult();
2034 if (extractOp.hasDynamicPosition())
2038 llvm::isa<VectorType>(extractOp.getType())
2039 ? llvm::cast<VectorType>(extractOp.getType()).getRank()
2041 auto insertOp = extractOp.getSource().getDefiningOp<InsertStridedSliceOp>();
2051 int64_t insertRankDiff = insertOp.getDestVectorType().getRank() -
2052 insertOp.getSourceVectorType().getRank();
2053 if (destinationRank > insertOp.getSourceVectorType().getRank())
2058 if (llvm::any_of(insertOp.getStrides(), [](
Attribute attr) {
2059 return llvm::cast<IntegerAttr>(attr).getInt() != 1;
2062 bool disjoint =
false;
2064 for (
unsigned dim = 0, e = extractOffsets.size(); dim < e; ++dim) {
2065 int64_t start = insertOffsets[dim];
2067 (dim < insertRankDiff)
2069 : insertOp.getSourceVectorType().getDimSize(dim - insertRankDiff);
2071 int64_t offset = extractOffsets[dim];
2073 if (start <= offset && offset < end) {
2074 if (dim >= insertRankDiff)
2075 offsetDiffs.push_back(offset - start);
2086 insertOp.getSourceVectorType().getRank() - destinationRank;
2087 for (
int64_t i = 0; i < destinationRank; i++) {
2088 if (insertOp.getSourceVectorType().getDimSize(i + srcRankDiff) !=
2089 insertOp.getDestVectorType().getDimSize(i + srcRankDiff +
2093 extractOp.getSourceMutable().assign(insertOp.getValueToStore());
2096 extractOp.setStaticPosition(offsetDiffs);
2097 return extractOp.getResult();
2101 insertOp = insertOp.getDest().getDefiningOp<InsertStridedSliceOp>();
2114 if (extractOp.hasDynamicPosition())
2118 auto fromElementsOp = extractOp.getSource().
getDefiningOp<FromElementsOp>();
2119 if (!fromElementsOp)
2123 auto vecType = llvm::cast<VectorType>(fromElementsOp.getType());
2124 if (vecType.isScalable())
2128 int64_t rank = vecType.getRank();
2130 if (extractOp.getType() != vecType.getElementType())
2133 "unexpected number of indices");
2138 for (
int i = rank - 1; i >= 0; --i) {
2139 flatIndex +=
indices[i] * stride;
2140 stride *= vecType.getDimSize(i);
2142 return fromElementsOp.getElements()[flatIndex];
2147template <
typename OpType,
typename AdaptorType>
2150 std::vector<int64_t> staticPosition = op.getStaticPosition().vec();
2151 OperandRange dynamicPosition = op.getDynamicPosition();
2154 if constexpr (std::is_same_v<OpType, ExtractOp>)
2155 vectorShape = op.getSourceVectorType().getShape();
2160 if (!dynamicPosition.size())
2167 bool opChange =
false;
2168 for (
unsigned i = 0, e = staticPosition.size(); i < e; ++i) {
2169 if (ShapedType::isStatic(staticPosition[i]))
2173 if (
auto attr = mlir::dyn_cast_if_present<IntegerAttr>(positionAttr)) {
2174 int64_t value = attr.getInt();
2178 staticPosition[i] = attr.getInt();
2183 operands.push_back(position);
2187 op.setStaticPosition(staticPosition);
2188 op.getOperation()->setOperands(operands);
2190 return op.getResult();
2200 if (!is_contained(staticPos, poisonVal))
2203 return ub::PoisonAttr::get(context);
2217 auto denseAttr = dyn_cast_if_present<DenseElementsAttr>(srcAttr);
2222 if (denseAttr.isSplat()) {
2224 if (
auto vecDstType = dyn_cast<VectorType>(extractOp.getType()))
2229 auto vecTy = cast<VectorType>(extractOp.getSourceVectorType());
2230 if (vecTy.isScalable())
2233 if (extractOp.hasDynamicPosition()) {
2248 copy(extractOp.getStaticPosition(), completePositions.begin());
2251 auto denseValuesBegin = denseAttr.value_begin<TypedAttr>() + startPos;
2254 if (
auto resVecTy = dyn_cast<VectorType>(extractOp.getType())) {
2256 denseValuesBegin, denseValuesBegin + resVecTy.getNumElements());
2259 newAttr = *denseValuesBegin;
2265OpFoldResult ExtractOp::fold(FoldAdaptor adaptor) {
2269 if (getNumIndices() == 0 && getSource().
getType() == getResult().
getType())
2276 SmallVector<Value> operands = {getSource()};
2280 getContext(), adaptor.getStaticPosition(), kPoisonIndex))
2286 if (
auto res = ExtractFromInsertTransposeChainState(*this).fold())
2301 return inplaceFolded;
2307class ExtractOpFromBroadcast final :
public OpRewritePattern<ExtractOp> {
2311 LogicalResult matchAndRewrite(ExtractOp extractOp,
2312 PatternRewriter &rewriter)
const override {
2315 VectorType outType = dyn_cast<VectorType>(extractOp.getType());
2321 BroadcastableToResult::Success)
2330class ExtractOpFromCreateMask final :
public OpRewritePattern<ExtractOp> {
2334 LogicalResult matchAndRewrite(ExtractOp extractOp,
2335 PatternRewriter &rewriter)
const override {
2337 extractOp.getSource().getDefiningOp<vector::CreateMaskOp>();
2341 VectorType extractedMaskType =
2342 llvm::dyn_cast<VectorType>(extractOp.getResult().getType());
2344 if (!extractedMaskType)
2347 auto maskOperands = createMaskOp.getOperands();
2348 ArrayRef<int64_t> extractOpPos = extractOp.getStaticPosition();
2349 VectorType maskType = createMaskOp.getVectorType();
2351 bool containsUnknownDims =
false;
2354 for (
size_t dimIdx = 0; !allFalse && dimIdx < extractOpPos.size();
2356 int64_t pos = extractOpPos[dimIdx];
2357 Value operand = maskOperands[dimIdx];
2358 auto constantOp = operand.
getDefiningOp<arith::ConstantOp>();
2361 containsUnknownDims =
true;
2365 int64_t createMaskBound =
2366 llvm::cast<IntegerAttr>(constantOp.getValue()).getInt();
2368 if (pos != ShapedType::kDynamic) {
2371 allFalse |= pos >= createMaskBound;
2372 }
else if (createMaskBound < maskType.getDimSize(dimIdx)) {
2376 containsUnknownDims =
true;
2383 }
else if (!containsUnknownDims) {
2385 extractOp, extractedMaskType,
2386 maskOperands.drop_front(extractOpPos.size()));
2395class ExtractOpFromConstantMask final :
public OpRewritePattern<ExtractOp> {
2399 LogicalResult matchAndRewrite(ExtractOp extractOp,
2400 PatternRewriter &rewriter)
const override {
2401 auto constantMaskOp =
2402 extractOp.getSource().getDefiningOp<vector::ConstantMaskOp>();
2403 if (!constantMaskOp)
2406 Type resultType = extractOp.getResult().getType();
2407 auto extractedMaskType = dyn_cast<VectorType>(resultType);
2409 ArrayRef<int64_t> extractOpPos = extractOp.getStaticPosition();
2410 ArrayRef<int64_t> maskDimSizes = constantMaskOp.getMaskDimSizes();
2412 VectorType maskType = constantMaskOp.getVectorType();
2415 for (
size_t dimIdx = 0; dimIdx < extractOpPos.size(); dimIdx++) {
2416 int64_t pos = extractOpPos[dimIdx];
2417 if (pos == ShapedType::kDynamic) {
2420 if (maskDimSizes[dimIdx] == maskType.getDimSize(dimIdx))
2429 if (pos >= maskDimSizes[dimIdx]) {
2430 if (extractedMaskType) {
2442 if (extractedMaskType) {
2446 extractOp, extractedMaskType,
2447 maskDimSizes.drop_front(extractOpPos.size()));
2460LogicalResult foldExtractFromShapeCastToShapeCast(ExtractOp extractOp,
2461 PatternRewriter &rewriter) {
2462 auto castOp = extractOp.getSource().getDefiningOp<ShapeCastOp>();
2466 VectorType sourceType = castOp.getSourceVectorType();
2467 auto targetType = dyn_cast<VectorType>(extractOp.getResult().getType());
2471 if (sourceType.getNumElements() != targetType.getNumElements())
2475 castOp.getSource());
2485LogicalResult foldExtractFromFromElements(ExtractOp extractOp,
2486 PatternRewriter &rewriter) {
2488 if (extractOp.hasDynamicPosition())
2492 auto resultType = dyn_cast<VectorType>(extractOp.getType());
2497 auto fromElementsOp = extractOp.getSource().getDefiningOp<FromElementsOp>();
2498 if (!fromElementsOp)
2500 VectorType inputType = fromElementsOp.getType();
2503 if (resultType.isScalable() || inputType.isScalable())
2508 SmallVector<int64_t> firstElementPos =
2509 llvm::to_vector(extractOp.getStaticPosition());
2510 firstElementPos.append(resultType.getRank(), 0);
2513 for (int64_t i = inputType.getRank() - 1; i >= 0; --i) {
2514 flatIndex += firstElementPos[i] * stride;
2515 stride *= inputType.getDimSize(i);
2520 extractOp, resultType,
2521 fromElementsOp.getElements().
slice(flatIndex,
2522 resultType.getNumElements()));
2534struct ExtractToShapeCast final : OpRewritePattern<vector::ExtractOp> {
2536 LogicalResult matchAndRewrite(vector::ExtractOp extractOp,
2537 PatternRewriter &rewriter)
const override {
2538 VectorType sourceType = extractOp.getSourceVectorType();
2539 VectorType outType = dyn_cast<VectorType>(extractOp.getType());
2543 if (sourceType.getNumElements() != outType.getNumElements())
2545 extractOp,
"extract to vector with fewer elements");
2549 if (llvm::any_of(extractOp.getMixedPosition(),
2550 [](OpFoldResult v) { return !isConstantIntValue(v, 0); }))
2552 "leaving for extract poison folder");
2555 extractOp.getSource());
2576struct FoldExtractFromInsertUnitDim final
2577 : OpRewritePattern<vector::ExtractOp> {
2580 LogicalResult matchAndRewrite(vector::ExtractOp extractOp,
2581 PatternRewriter &rewriter)
const override {
2582 if (extractOp.hasDynamicPosition())
2585 auto insertOp = extractOp.getSource().getDefiningOp<vector::InsertOp>();
2586 if (!insertOp || insertOp.hasDynamicPosition())
2589 ArrayRef<int64_t> extractPos = extractOp.getStaticPosition();
2590 ArrayRef<int64_t> insertPos = insertOp.getStaticPosition();
2593 if (extractPos.size() >= insertPos.size() ||
2594 extractPos != insertPos.take_front(extractPos.size()))
2600 auto srcVecType = extractOp.getSourceVectorType();
2601 for (int64_t i = extractPos.size(), e = srcVecType.getRank(); i < e; ++i)
2602 if (srcVecType.getDimSize(i) != 1)
2605 Value
inserted = insertOp.getValueToStore();
2606 Type extractedType = extractOp.getResult().getType();
2607 if (isa<VectorType>(
inserted.getType())) {
2614 extractOp, extractOp.getResult().
getType(),
2615 insertOp.getValueToStore());
2623void ExtractOp::getCanonicalizationPatterns(RewritePatternSet &results,
2624 MLIRContext *context) {
2625 results.
add<ExtractOpFromBroadcast, ExtractOpFromCreateMask,
2626 ExtractOpFromConstantMask, ExtractToShapeCast,
2627 FoldExtractFromInsertUnitDim>(context);
2628 results.
add(foldExtractFromShapeCastToShapeCast);
2629 results.
add(foldExtractFromFromElements);
2634 for (
auto attr : arrayAttr)
2635 results.push_back(llvm::cast<IntegerAttr>(attr).getInt());
2642std::optional<SmallVector<int64_t, 4>> FMAOp::getShapeForUnroll() {
2653 if (operands.empty())
2656 return llvm::all_of(operands, [&](
Value operand) {
2658 return currentDef == defOp;
2676 auto fromElementsOp =
2677 toElementsOp.getSource().getDefiningOp<FromElementsOp>();
2678 if (!fromElementsOp)
2681 llvm::append_range(results, fromElementsOp.getElements());
2698 auto bcastOp = toElementsOp.getSource().getDefiningOp<BroadcastOp>();
2702 if (isa<VectorType>(bcastOp.getSource().getType()))
2705 auto resultVecType = cast<VectorType>(toElementsOp.getSource().getType());
2707 Value scalar = bcastOp.getSource();
2708 results.assign(resultVecType.getNumElements(), scalar);
2712LogicalResult ToElementsOp::fold(FoldAdaptor adaptor,
2713 SmallVectorImpl<OpFoldResult> &results) {
2718 if (
auto shapeCast = getSource().getDefiningOp<ShapeCastOp>()) {
2719 setOperand(shapeCast.getSource());
2727ToElementsOp::inferReturnTypes(MLIRContext *ctx, std::optional<Location> loc,
2728 ToElementsOp::Adaptor adaptor,
2729 SmallVectorImpl<Type> &inferredReturnTypes) {
2730 auto vecType = cast<VectorType>(adaptor.getSource().getType());
2731 Type elType = vecType.getElementType();
2732 inferredReturnTypes.append(vecType.getNumElements(), elType);
2754 auto bcastOp = toElementsOp.getSource().getDefiningOp<BroadcastOp>();
2759 auto srcType = dyn_cast<VectorType>(bcastOp.getSource().getType());
2763 auto dstType = cast<VectorType>(toElementsOp.getSource().getType());
2768 int64_t dstRank = dstShape.size();
2769 int64_t srcRank = srcShape.size();
2772 auto srcElems = vector::ToElementsOp::create(
2773 rewriter, toElementsOp.getLoc(), bcastOp.getSource());
2775 int64_t dstCount = llvm::product_of(dstShape);
2778 replacements.reserve(dstCount);
2803 for (
int64_t lin = 0; lin < dstCount; ++lin) {
2806 for (
int64_t k = 0; k < srcRank; ++k)
2807 srcIdx[k] = (srcShape[k] == 1) ? 0 : dstIdx[dstRank - srcRank + k];
2810 replacements.push_back(srcElems.getResult(srcLin));
2813 rewriter.
replaceOp(toElementsOp, replacements);
2818void ToElementsOp::getCanonicalizationPatterns(RewritePatternSet &results,
2819 MLIRContext *context) {
2820 results.
add<ToElementsOfBroadcast>(context);
2840 OperandRange fromElemsOperands = fromElementsOp.getElements();
2841 if (fromElemsOperands.empty())
2844 auto toElementsOp = fromElemsOperands[0].getDefiningOp<ToElementsOp>();
2852 Value toElementsInput = toElementsOp.getSource();
2853 if (fromElementsOp.getType() == toElementsInput.
getType() &&
2854 llvm::equal(fromElemsOperands, toElementsOp.getResults())) {
2855 return toElementsInput;
2875 if (llvm::any_of(elements, [](
Attribute attr) {
2881 auto destVecType = fromElementsOp.getDest().getType();
2882 auto destEltType = destVecType.getElementType();
2883 if (!destEltType.isIntOrIndexOrFloat() && !isa<ComplexType>(destEltType))
2888 auto convertedElements = llvm::map_to_vector(elements, [&](
Attribute attr) {
2895OpFoldResult FromElementsOp::fold(FoldAdaptor adaptor) {
2912 if (!llvm::all_equal(fromElementsOp.getElements()))
2915 fromElementsOp, fromElementsOp.getType(),
2916 fromElementsOp.getElements().front());
2944 LogicalResult matchAndRewrite(FromElementsOp fromElements,
2948 if (fromElements.getType().getNumElements() == 1)
2959 for (
auto [insertIndex, element] :
2960 llvm::enumerate(fromElements.getElements())) {
2963 auto extractOp = element.getDefiningOp<vector::ExtractOp>();
2966 "element not from vector.extract");
2971 if (insertIndex == 0) {
2972 source = extractOp.getSource();
2973 }
else if (extractOp.getSource() != source) {
2975 "element from different vector");
2979 int64_t rank = position.size();
2980 assert(rank == source.getType().getRank() &&
2981 "scalar extract must have full rank position");
2992 if (insertIndex == 0) {
2993 const int64_t numElms = fromElements.getType().getNumElements();
2996 while (
index > 0 && position[
index - 1] == 0 &&
2997 numSuffixElms < numElms) {
2998 numSuffixElms *= source.getType().getDimSize(
index - 1);
3001 if (numSuffixElms != numElms) {
3003 fromElements,
"elements do not form a suffix of source");
3005 expectedPosition = llvm::to_vector(position);
3006 combinedPosition = position.drop_back(rank -
index);
3010 else if (expectedPosition != position) {
3012 fromElements,
"elements not in ascending order (static order)");
3014 increment(expectedPosition, source.getType().getShape());
3017 auto extracted = rewriter.
createOrFold<vector::ExtractOp>(
3018 fromElements.getLoc(), source, combinedPosition);
3021 fromElements, fromElements.getType(), extracted);
3029 for (
int dim : llvm::reverse(llvm::seq<int>(0,
indices.size()))) {
3048void BroadcastOp::inferResultRanges(ArrayRef<ConstantIntRanges> argRanges,
3050 setResultRanges(getResult(), argRanges.front());
3053std::optional<SmallVector<int64_t, 4>> BroadcastOp::getShapeForUnroll() {
3054 return llvm::to_vector<4>(getResultVectorType().
getShape());
3059static llvm::SetVector<int64_t>
3062 int64_t rankDiff = dstShape.size() - srcShape.size();
3065 for (
auto [s1, s2] :
3066 llvm::zip_equal(srcShape, dstShape.drop_front(rankDiff))) {
3068 assert(s1 == 1 &&
"expected \"dim-1\" broadcasting");
3076llvm::SetVector<int64_t> BroadcastOp::computeBroadcastedUnitDims() {
3078 auto srcVectorType = llvm::dyn_cast<VectorType>(getSourceType());
3081 return ::computeBroadcastedUnitDims(srcVectorType.getShape(),
3097Value BroadcastOp::createOrFoldBroadcastOp(
3098 OpBuilder &
b, Value value, ArrayRef<int64_t> dstShape,
3099 const llvm::SetVector<int64_t> &broadcastedDims) {
3100 assert(!dstShape.empty() &&
"unexpected empty dst shape");
3103 SmallVector<int64_t> checkShape;
3104 for (
int i = 0, e = dstShape.size(); i < e; ++i) {
3105 if (broadcastedDims.contains(i))
3107 checkShape.push_back(dstShape[i]);
3109 assert(broadcastedDims.size() == dstShape.size() - checkShape.size() &&
3110 "ill-formed broadcastedDims contains values not confined to "
3113 Location loc = value.
getLoc();
3115 VectorType srcVectorType = llvm::dyn_cast<VectorType>(value.
getType());
3116 VectorType dstVectorType = VectorType::get(dstShape, elementType);
3119 if (!srcVectorType) {
3120 assert(checkShape.empty() &&
3121 "ill-formed createOrFoldBroadcastOp arguments");
3122 return b.createOrFold<vector::BroadcastOp>(loc, dstVectorType, value);
3125 assert(srcVectorType.getShape().equals(checkShape) &&
3126 "ill-formed createOrFoldBroadcastOp arguments");
3136 SmallVector<int64_t> broadcastShape, permutation(dstShape.size(), -1);
3137 broadcastShape.reserve(dstShape.size());
3153 int64_t nextSrcShapeDim = broadcastedDims.size();
3154 for (int64_t i = 0, e = dstShape.size(); i < e; ++i) {
3155 if (broadcastedDims.contains(i)) {
3160 broadcastShape.push_back(dstShape[i]);
3161 permutation[i] = broadcastShape.size() - 1;
3167 permutation[i] = nextSrcShapeDim++;
3171 llvm::append_range(broadcastShape, srcVectorType.getShape());
3176 "unexpected \"dim-1\" broadcast");
3178 VectorType broadcastType = VectorType::get(broadcastShape, elementType);
3180 vector::BroadcastableToResult::Success &&
3181 "must be broadcastable");
3182 Value res =
b.createOrFold<vector::BroadcastOp>(loc, broadcastType, value);
3185 for (int64_t i = 0, e = permutation.size(); i < e; ++i)
3186 if (permutation[i] != i)
3187 return b.createOrFold<vector::TransposeOp>(loc, res, permutation);
3192LogicalResult BroadcastOp::verify() {
3193 std::pair<VectorDim, VectorDim> mismatchingDims;
3195 getSourceType(), getResultVectorType(), &mismatchingDims);
3196 if (res == BroadcastableToResult::Success)
3198 if (res == BroadcastableToResult::SourceRankHigher)
3199 return emitOpError(
"source rank higher than destination rank");
3200 if (res == BroadcastableToResult::DimensionMismatch) {
3201 return emitOpError(
"dimension mismatch (")
3202 << (mismatchingDims.first.isScalable ?
"[" :
"")
3203 << mismatchingDims.first.dim
3204 << (mismatchingDims.first.isScalable ?
"]" :
"") <<
" vs. "
3205 << (mismatchingDims.second.isScalable ?
"[" :
"")
3206 << mismatchingDims.second.dim
3207 << (mismatchingDims.second.isScalable ?
"]" :
"") <<
")";
3209 if (res == BroadcastableToResult::SourceTypeNotAVector)
3210 return emitOpError(
"source type is not a vector");
3211 llvm_unreachable(
"unexpected vector.broadcast op error");
3218 auto srcShapeCast = broadcastOp.getSource().getDefiningOp<ShapeCastOp>();
3222 VectorType srcType = srcShapeCast.getSourceVectorType();
3223 VectorType destType = broadcastOp.getResultVectorType();
3231 srcShapeCast.getResultVectorType().getShape();
3234 unsigned numTrailingDims = std::min(srcShape.size(), shapecastShape.size());
3235 if (!llvm::equal(srcShape.take_back(numTrailingDims),
3236 shapecastShape.take_back(numTrailingDims)))
3239 assert(all_of(srcShape.drop_back(numTrailingDims),
3240 [](
int64_t E) { return E == 1; }) &&
3241 all_of(shapecastShape.drop_back(numTrailingDims),
3242 [](
int64_t E) { return E == 1; }) &&
3243 "ill-formed shape_cast");
3245 broadcastOp.getSourceMutable().assign(srcShapeCast.getSource());
3249OpFoldResult BroadcastOp::fold(FoldAdaptor adaptor) {
3250 if (getSourceType() == getResultVectorType())
3255 if (!adaptor.getSource())
3257 auto vectorType = getResultVectorType();
3258 if (
auto attr = llvm::dyn_cast<IntegerAttr>(adaptor.getSource())) {
3259 if (vectorType.getElementType() != attr.getType())
3263 if (
auto attr = llvm::dyn_cast<FloatAttr>(adaptor.getSource())) {
3264 if (vectorType.getElementType() != attr.getType())
3268 if (
auto attr = llvm::dyn_cast<SplatElementsAttr>(adaptor.getSource()))
3278struct BroadcastFolder :
public OpRewritePattern<BroadcastOp> {
3281 LogicalResult matchAndRewrite(BroadcastOp broadcastOp,
3282 PatternRewriter &rewriter)
const override {
3283 auto srcBroadcast = broadcastOp.getSource().getDefiningOp<BroadcastOp>();
3287 broadcastOp.getResultVectorType(),
3288 srcBroadcast.getSource());
3301struct BroadcastToShapeCast final
3302 :
public OpRewritePattern<vector::BroadcastOp> {
3304 LogicalResult matchAndRewrite(vector::BroadcastOp
broadcast,
3305 PatternRewriter &rewriter)
const override {
3307 auto sourceType = dyn_cast<VectorType>(
broadcast.getSourceType());
3310 broadcast,
"source is a scalar, shape_cast doesn't support scalar");
3314 if (sourceType.getNumElements() != outType.getNumElements()) {
3316 broadcast,
"broadcast to a greater number of elements");
3326void BroadcastOp::getCanonicalizationPatterns(RewritePatternSet &results,
3327 MLIRContext *context) {
3328 results.
add<BroadcastFolder, BroadcastToShapeCast>(context);
3335LogicalResult ShuffleOp::verify() {
3336 VectorType resultType = getResultVectorType();
3337 VectorType v1Type = getV1VectorType();
3338 VectorType v2Type = getV2VectorType();
3340 int64_t resRank = resultType.getRank();
3341 int64_t v1Rank = v1Type.getRank();
3342 int64_t v2Rank = v2Type.getRank();
3343 bool wellFormed0DCase = v1Rank == 0 && v2Rank == 0 && resRank == 1;
3344 bool wellFormedNDCase = v1Rank == resRank && v2Rank == resRank;
3345 if (!wellFormed0DCase && !wellFormedNDCase)
3346 return emitOpError(
"rank mismatch");
3349 for (int64_t r = 1; r < v1Rank; ++r) {
3350 int64_t resDim = resultType.getDimSize(r);
3351 int64_t v1Dim = v1Type.getDimSize(r);
3352 int64_t v2Dim = v2Type.getDimSize(r);
3353 if (resDim != v1Dim || v1Dim != v2Dim)
3354 return emitOpError(
"dimension mismatch");
3357 ArrayRef<int64_t> mask = getMask();
3358 int64_t maskLength = mask.size();
3359 if (maskLength <= 0)
3360 return emitOpError(
"invalid mask length");
3361 if (maskLength != resultType.getDimSize(0))
3362 return emitOpError(
"mask length mismatch");
3364 int64_t indexSize = (v1Type.getRank() == 0 ? 1 : v1Type.getDimSize(0)) +
3365 (v2Type.getRank() == 0 ? 1 : v2Type.getDimSize(0));
3366 for (
auto [idx, maskPos] : llvm::enumerate(mask)) {
3368 return emitOpError(
"mask index #") << (idx + 1) <<
" out of range";
3374ShuffleOp::inferReturnTypes(MLIRContext *, std::optional<Location> loc,
3375 ShuffleOp::Adaptor adaptor,
3376 SmallVectorImpl<Type> &inferredReturnTypes) {
3377 auto v1Type = llvm::dyn_cast<VectorType>(adaptor.getV1().getType());
3381 auto v1Rank = v1Type.getRank();
3384 SmallVector<int64_t, 4> shape;
3385 shape.reserve(v1Rank);
3386 shape.push_back(std::max<size_t>(1, adaptor.getMask().size()));
3389 llvm::append_range(shape, v1Type.getShape().drop_front());
3390 inferredReturnTypes.push_back(
3391 VectorType::get(shape, v1Type.getElementType()));
3395template <
typename T>
3398 return idxArr.size() == width && llvm::all_of(idxArr, [&expected](T value) {
3399 return value == expected++;
3406 auto v1Type = op.getV1VectorType();
3407 auto v2Type = op.getV2VectorType();
3408 auto mask = op.getMask();
3421 if (!isV1Poison && !isV2Poison)
3424 int64_t v1Size = op.getV1VectorType().getDimSize(0);
3425 bool changed =
false;
3427 for (
int64_t &idx : newMask) {
3428 if (idx == ShuffleOp::kPoisonIndex)
3430 if ((isV1Poison && idx < v1Size) || (isV2Poison && idx >= v1Size)) {
3431 idx = ShuffleOp::kPoisonIndex;
3439 op.setMask(newMask);
3440 return op.getResult();
3449 return ub::PoisonAttr::get(context);
3456 auto v1Type = op.getV1VectorType();
3457 if (v1Type.getRank() != 1)
3469 auto v2DenseAttr = dyn_cast<DenseElementsAttr>(v2Attr);
3472 v2Elements = to_vector(v2DenseAttr.getValues<
Attribute>());
3473 poisonElement = v2Elements[0];
3476 auto v1DenseAttr = dyn_cast<DenseElementsAttr>(v1Attr);
3479 v1Elements = to_vector(v1DenseAttr.getValues<
Attribute>());
3480 poisonElement = v1Elements[0];
3485 int64_t v1Size = v1Type.getDimSize(0);
3486 for (
int64_t maskIdx : mask) {
3489 if (maskIdx == ShuffleOp::kPoisonIndex) {
3490 indexedElm = poisonElement;
3492 if (maskIdx < v1Size)
3493 indexedElm = isV1Poison ? poisonElement : v1Elements[maskIdx];
3495 indexedElm = isV2Poison ? poisonElement : v2Elements[maskIdx - v1Size];
3498 results.push_back(indexedElm);
3504OpFoldResult vector::ShuffleOp::fold(FoldAdaptor adaptor) {
3505 auto v1Type = getV1VectorType();
3507 assert(!v1Type.isScalable() && !getV2VectorType().isScalable() &&
3508 "Vector shuffle does not support scalable vectors");
3512 if (v1Type.getRank() == 0)
3520 Attribute v1Attr = adaptor.getV1(), v2Attr = adaptor.getV2();
3521 if (!v1Attr || !v2Attr)
3536struct Canonicalize0DShuffleOp :
public OpRewritePattern<ShuffleOp> {
3539 LogicalResult matchAndRewrite(ShuffleOp shuffleOp,
3540 PatternRewriter &rewriter)
const override {
3541 VectorType v1VectorType = shuffleOp.getV1VectorType();
3542 ArrayRef<int64_t> mask = shuffleOp.getMask();
3543 if (v1VectorType.getRank() > 0)
3545 if (mask.size() != 1)
3547 VectorType resType = VectorType::Builder(v1VectorType).setShape({1});
3565static Value getScalarSplatSource(Value value) {
3571 auto broadcast = dyn_cast<vector::BroadcastOp>(defOp);
3578 if (isa<VectorType>(
broadcast.getSourceType()))
3586class ShuffleSplat final :
public OpRewritePattern<ShuffleOp> {
3590 LogicalResult matchAndRewrite(ShuffleOp op,
3591 PatternRewriter &rewriter)
const override {
3592 Value splat = getScalarSplatSource(op.getV1());
3593 if (!splat || getScalarSplatSource(op.getV2()) != splat)
3603class ShuffleInterleave :
public OpRewritePattern<ShuffleOp> {
3607 LogicalResult matchAndRewrite(ShuffleOp op,
3608 PatternRewriter &rewriter)
const override {
3609 VectorType resultType = op.getResultVectorType();
3610 if (resultType.isScalable())
3612 op,
"ShuffleOp can't represent a scalable interleave");
3614 if (resultType.getRank() != 1)
3616 op,
"ShuffleOp can't represent an n-D interleave");
3618 VectorType sourceType = op.getV1VectorType();
3619 if (sourceType != op.getV2VectorType() ||
3620 sourceType.getNumElements() * 2 != resultType.getNumElements()) {
3622 op,
"ShuffleOp types don't match an interleave");
3625 ArrayRef<int64_t> shuffleMask = op.getMask();
3626 int64_t resultVectorSize = resultType.getNumElements();
3627 for (
int i = 0, e = resultVectorSize / 2; i < e; ++i) {
3628 int64_t maskValueA = shuffleMask[i * 2];
3629 int64_t maskValueB = shuffleMask[(i * 2) + 1];
3630 if (maskValueA != i || maskValueB != (resultVectorSize / 2) + i)
3632 "ShuffleOp mask not interleaving");
3648class FoldUnusedShuffleOperand final :
public OpRewritePattern<ShuffleOp> {
3652 LogicalResult matchAndRewrite(ShuffleOp op,
3653 PatternRewriter &rewriter)
const override {
3655 if (llvm::all_of(op.getMask(), [](int64_t mask) {
3656 return mask == ShuffleOp::kPoisonIndex;
3663 auto replaceOperandWithPoison = [&](OpOperand &operand) {
3666 Value poison = ub::PoisonOp::create(rewriter, op.getLoc(),
3675 int64_t leadingV1Size = op.getV1VectorType().getRank() > 0
3676 ? op.getV1VectorType().getDimSize(0)
3678 bool isV1Used = llvm::any_of(op.getMask(), [&](int64_t mask) {
3679 return mask != ShuffleOp::kPoisonIndex && mask < leadingV1Size;
3681 if (!isV1Used && succeeded(replaceOperandWithPoison(op.getV1Mutable())))
3685 bool isV2Used = llvm::any_of(op.getMask(), [&](int64_t mask) {
3686 return mask != ShuffleOp::kPoisonIndex && mask >= leadingV1Size;
3688 if (!isV2Used && succeeded(replaceOperandWithPoison(op.getV2Mutable())))
3696void ShuffleOp::getCanonicalizationPatterns(RewritePatternSet &results,
3697 MLIRContext *context) {
3698 results.
add<ShuffleSplat, ShuffleInterleave, Canonicalize0DShuffleOp,
3699 FoldUnusedShuffleOperand>(context);
3706void vector::InsertOp::inferResultRanges(ArrayRef<ConstantIntRanges> argRanges,
3708 setResultRanges(getResult(), argRanges[0].rangeUnion(argRanges[1]));
3711void vector::InsertOp::build(OpBuilder &builder, OperationState &
result,
3712 Value source, Value dest) {
3713 auto vectorTy = cast<VectorType>(dest.
getType());
3714 build(builder,
result, source, dest,
3715 SmallVector<int64_t>(vectorTy.getRank(), 0));
3718void vector::InsertOp::build(OpBuilder &builder, OperationState &
result,
3719 Value source, Value dest, int64_t position) {
3720 build(builder,
result, source, dest, ArrayRef<int64_t>{position});
3723void vector::InsertOp::build(OpBuilder &builder, OperationState &
result,
3724 Value source, Value dest, OpFoldResult position) {
3725 build(builder,
result, source, dest, ArrayRef<OpFoldResult>{position});
3728void vector::InsertOp::build(OpBuilder &builder, OperationState &
result,
3729 Value source, Value dest,
3730 ArrayRef<int64_t> position) {
3731 SmallVector<OpFoldResult> posVals;
3732 posVals.reserve(position.size());
3733 llvm::transform(position, std::back_inserter(posVals),
3735 build(builder,
result, source, dest, posVals);
3738void vector::InsertOp::build(OpBuilder &builder, OperationState &
result,
3739 Value source, Value dest,
3740 ArrayRef<OpFoldResult> position) {
3741 SmallVector<int64_t> staticPos;
3742 SmallVector<Value> dynamicPos;
3744 build(builder,
result, source, dest, dynamicPos,
3748LogicalResult InsertOp::verify() {
3749 if (
auto srcTy = dyn_cast<VectorType>(getValueToStoreType()))
3750 if (srcTy.getRank() == 0)
3752 "expected a scalar instead of a 0-d vector as the source operand");
3754 SmallVector<OpFoldResult> position = getMixedPosition();
3755 auto destVectorType = getDestVectorType();
3756 if (position.size() >
static_cast<unsigned>(destVectorType.getRank()))
3758 "expected position attribute of rank no greater than dest vector rank");
3759 auto srcVectorType = llvm::dyn_cast<VectorType>(getValueToStoreType());
3760 if (srcVectorType &&
3761 (
static_cast<unsigned>(srcVectorType.getRank()) + position.size() !=
3762 static_cast<unsigned>(destVectorType.getRank())))
3763 return emitOpError(
"expected position attribute rank + source rank to "
3764 "match dest vector rank");
3765 if (!srcVectorType &&
3766 (position.size() !=
static_cast<unsigned>(destVectorType.getRank())))
3768 "expected position attribute rank to match the dest vector rank");
3769 for (
auto [idx, pos] : llvm::enumerate(position)) {
3770 if (
auto attr = dyn_cast<Attribute>(pos)) {
3771 int64_t constIdx = cast<IntegerAttr>(attr).getInt();
3773 destVectorType.getDimSize(idx))) {
3774 return emitOpError(
"expected position attribute #")
3776 <<
" to be a non-negative integer smaller than the "
3778 "dest vector dimension";
3791 assert(positions.size() <= completePositions.size() &&
3792 "positions size must be less than or equal to destTy rank");
3793 copy(positions, completePositions.begin());
3801class InsertToBroadcast final :
public OpRewritePattern<InsertOp> {
3805 LogicalResult matchAndRewrite(InsertOp insertOp,
3806 PatternRewriter &rewriter)
const override {
3808 llvm::dyn_cast<VectorType>(insertOp.getValueToStoreType());
3809 if (!srcVecType || insertOp.getDestVectorType().getNumElements() !=
3810 srcVecType.getNumElements())
3813 insertOp, insertOp.getDestVectorType(), insertOp.getValueToStore());
3819class InsertSplatToSplat final :
public OpRewritePattern<InsertOp> {
3823 LogicalResult matchAndRewrite(InsertOp op,
3824 PatternRewriter &rewriter)
const override {
3826 Value splat = getScalarSplatSource(op.getValueToStore());
3827 if (!splat || getScalarSplatSource(op.getDest()) != splat)
3855class InsertChainFullyInitialized final :
public OpRewritePattern<InsertOp> {
3858 LogicalResult matchAndRewrite(InsertOp op,
3859 PatternRewriter &rewriter)
const override {
3861 VectorType destTy = op.getDestVectorType();
3862 if (destTy.isScalable())
3865 for (Operation *user : op.getResult().getUsers())
3866 if (
auto insertOp = dyn_cast<InsertOp>(user))
3867 if (insertOp.getDest() == op.getResult())
3870 InsertOp currentOp = op;
3871 SmallVector<InsertOp> chainInsertOps;
3874 if (currentOp.hasDynamicPosition())
3877 chainInsertOps.push_back(currentOp);
3878 currentOp = currentOp.getDest().getDefiningOp<InsertOp>();
3881 if (currentOp && !currentOp->hasOneUse())
3885 int64_t vectorSize = destTy.getNumElements();
3886 int64_t initializedCount = 0;
3887 SmallVector<bool> initializedDestIdxs(vectorSize,
false);
3888 SmallVector<int64_t> pendingInsertPos;
3889 SmallVector<int64_t> pendingInsertSize;
3890 SmallVector<Value> pendingInsertValues;
3892 for (
auto insertOp : chainInsertOps) {
3894 if (is_contained(insertOp.getStaticPosition(), InsertOp::kPoisonIndex))
3898 int64_t insertBeginPosition =
3903 int64_t insertSize = 1;
3904 if (
auto srcVectorType =
3905 llvm::dyn_cast<VectorType>(insertOp.getValueToStoreType()))
3906 insertSize = srcVectorType.getNumElements();
3908 assert(insertBeginPosition + insertSize <= vectorSize &&
3909 "insert would overflow the vector");
3911 for (
auto index : llvm::seq<int64_t>(insertBeginPosition,
3912 insertBeginPosition + insertSize)) {
3913 if (initializedDestIdxs[index])
3915 initializedDestIdxs[index] =
true;
3921 pendingInsertPos.push_back(insertBeginPosition);
3922 pendingInsertSize.push_back(insertSize);
3923 pendingInsertValues.push_back(insertOp.getValueToStore());
3925 if (initializedCount == vectorSize)
3930 if (initializedCount != vectorSize)
3933 SmallVector<Value> elements(vectorSize);
3934 for (
auto [insertBeginPosition, insertSize, valueToStore] :
3935 llvm::reverse(llvm::zip(pendingInsertPos, pendingInsertSize,
3936 pendingInsertValues))) {
3937 auto srcVectorType = llvm::dyn_cast<VectorType>(valueToStore.getType());
3939 if (!srcVectorType) {
3940 elements[insertBeginPosition] = valueToStore;
3944 Repeated<Type> elementToInsertTypes(insertSize,
3945 srcVectorType.getElementType());
3947 auto elementsToInsert = vector::ToElementsOp::create(
3948 rewriter, op.getLoc(), elementToInsertTypes, valueToStore);
3949 for (int64_t linearIdx = 0; linearIdx < insertSize; linearIdx++) {
3950 elements[insertBeginPosition + linearIdx] =
3951 elementsToInsert.getResult(linearIdx);
3965 int64_t maxVectorSizeFoldThreshold) {
3966 if (insertOp.hasDynamicPosition())
3969 auto denseDst = llvm::dyn_cast_if_present<DenseElementsAttr>(dstAttr);
3977 VectorType destTy = insertOp.getDestVectorType();
3978 if (destTy.isScalable())
3982 if (destTy.getNumElements() > maxVectorSizeFoldThreshold &&
3983 !insertOp->hasOneUse())
3988 if (is_contained(insertOp.getStaticPosition(), InsertOp::kPoisonIndex))
3995 Type destEltType = destTy.getElementType();
3999 if (
auto denseSource = llvm::dyn_cast<DenseElementsAttr>(srcAttr)) {
4000 for (
auto value : denseSource.getValues<
Attribute>())
4006 auto allValues = llvm::to_vector(denseDst.getValues<
Attribute>());
4007 copy(insertedValues, allValues.begin() + insertBeginPosition);
4016 auto destInsert = insertOp.getDest().
getDefiningOp<InsertOp>();
4020 if (insertOp.getMixedPosition() != destInsert.getMixedPosition())
4023 insertOp.
setOperand(1, destInsert.getDest());
4024 return insertOp.getResult();
4027void InsertOp::getCanonicalizationPatterns(RewritePatternSet &results,
4028 MLIRContext *context) {
4029 results.
add<InsertToBroadcast, BroadcastFolder, InsertSplatToSplat,
4030 InsertChainFullyInitialized>(context);
4033OpFoldResult InsertOp::fold(FoldAdaptor adaptor) {
4036 constexpr int64_t vectorSizeFoldThreshold = 256;
4040 if (getNumIndices() == 0 && getValueToStoreType() ==
getType())
4041 return getValueToStore();
4045 SmallVector<Value> operands = {getValueToStore(), getDest()};
4051 getContext(), adaptor.getStaticPosition(), kPoisonIndex))
4054 *
this, adaptor.getValueToStore(), adaptor.getDest(),
4055 vectorSizeFoldThreshold)) {
4059 return inplaceFolded;
4066void InsertStridedSliceOp::build(OpBuilder &builder, OperationState &
result,
4067 Value source, Value dest,
4068 ArrayRef<int64_t> offsets,
4069 ArrayRef<int64_t> strides) {
4070 result.addOperands({source, dest});
4074 result.addAttribute(InsertStridedSliceOp::getOffsetsAttrName(
result.name),
4076 result.addAttribute(InsertStridedSliceOp::getStridesAttrName(
result.name),
4081template <
typename OpType>
4085 StringRef attrName) {
4086 if (arrayAttr.size() >
shape.size())
4087 return op.emitOpError(
"expected ")
4088 << attrName <<
" attribute of rank no greater than vector rank";
4095template <
typename OpType>
4099 bool halfOpen =
true) {
4100 for (
auto attr : arrayAttr) {
4101 auto val = llvm::cast<IntegerAttr>(attr).getInt();
4105 if (val < min || val >= upper)
4106 return op.emitOpError(
"expected ") << attrName <<
" to be confined to ["
4107 <<
min <<
", " << upper <<
")";
4115template <
typename OpType>
4120 for (
auto [
index, attrDimPair] :
4121 llvm::enumerate(llvm::zip_first(arrayAttr,
shape))) {
4122 int64_t val = llvm::cast<IntegerAttr>(std::get<0>(attrDimPair)).getInt();
4126 if (val < min || val >=
max)
4127 return op.emitOpError(
"expected ")
4128 << attrName <<
" dimension " <<
index <<
" to be confined to ["
4129 <<
min <<
", " <<
max <<
")";
4139template <
typename OpType>
4144 assert(arrayAttr1.size() <=
shape.size());
4145 assert(arrayAttr2.size() <=
shape.size());
4146 for (
auto [
index, it] :
4147 llvm::enumerate(llvm::zip(arrayAttr1, arrayAttr2,
shape))) {
4148 auto val1 = llvm::cast<IntegerAttr>(std::get<0>(it)).getInt();
4149 auto val2 = llvm::cast<IntegerAttr>(std::get<1>(it)).getInt();
4153 if (val1 + val2 < 0 || val1 + val2 >=
max)
4154 return op.emitOpError(
"expected sum(")
4155 << attrName1 <<
", " << attrName2 <<
") dimension " <<
index
4156 <<
" to be confined to [" <<
min <<
", " <<
max <<
")";
4164 return IntegerAttr::get(IntegerType::get(context, 64), APInt(64, v));
4166 return ArrayAttr::get(context, llvm::to_vector<8>(attrs));
4169LogicalResult InsertStridedSliceOp::verify() {
4170 auto sourceVectorType = getSourceVectorType();
4171 auto destVectorType = getDestVectorType();
4172 auto offsets = getOffsetsAttr();
4173 auto strides = getStridesAttr();
4174 if (offsets.size() !=
static_cast<unsigned>(destVectorType.getRank()))
4176 "expected offsets of same size as destination vector rank");
4177 if (strides.size() !=
static_cast<unsigned>(sourceVectorType.getRank()))
4178 return emitOpError(
"expected strides of same size as source vector rank");
4179 if (sourceVectorType.getRank() > destVectorType.getRank())
4181 "expected source rank to be no greater than destination rank");
4183 auto sourceShape = sourceVectorType.getShape();
4184 auto destShape = destVectorType.getShape();
4185 SmallVector<int64_t, 4> sourceShapeAsDestShape(
4186 destShape.size() - sourceShape.size(), 0);
4187 sourceShapeAsDestShape.append(sourceShape.begin(), sourceShape.end());
4188 auto offName = InsertStridedSliceOp::getOffsetsAttrName();
4189 auto stridesName = InsertStridedSliceOp::getStridesAttrName();
4198 offName,
"source vector shape",
4202 unsigned rankDiff = destShape.size() - sourceShape.size();
4203 for (
unsigned idx = 0; idx < sourceShape.size(); ++idx) {
4204 if (sourceVectorType.getScalableDims()[idx] !=
4205 destVectorType.getScalableDims()[idx + rankDiff]) {
4206 return emitOpError(
"mismatching scalable flags (at source vector idx=")
4209 if (sourceVectorType.getScalableDims()[idx]) {
4210 auto sourceSize = sourceShape[idx];
4211 auto destSize = destShape[idx + rankDiff];
4212 if (sourceSize != destSize) {
4213 return emitOpError(
"expected size at idx=")
4215 << (
" to match the corresponding base size from the input "
4217 << sourceSize << (
" vs ") << destSize << (
")");
4227class FoldInsertStridedSliceSplat final
4228 :
public OpRewritePattern<InsertStridedSliceOp> {
4232 LogicalResult matchAndRewrite(InsertStridedSliceOp insertStridedSliceOp,
4233 PatternRewriter &rewriter)
const override {
4235 auto dst = insertStridedSliceOp.getDest();
4236 auto splat = getScalarSplatSource(insertStridedSliceOp.getValueToStore());
4237 if (!splat || getScalarSplatSource(dst) != splat)
4240 rewriter.
replaceOp(insertStridedSliceOp, dst);
4247class FoldInsertStridedSliceOfExtract final
4248 :
public OpRewritePattern<InsertStridedSliceOp> {
4252 LogicalResult matchAndRewrite(InsertStridedSliceOp insertStridedSliceOp,
4253 PatternRewriter &rewriter)
const override {
4254 auto extractStridedSliceOp =
4255 insertStridedSliceOp.getValueToStore()
4256 .getDefiningOp<vector::ExtractStridedSliceOp>();
4258 if (!extractStridedSliceOp)
4261 if (extractStridedSliceOp.getOperand() != insertStridedSliceOp.getDest())
4265 if (extractStridedSliceOp.getStrides() !=
4266 insertStridedSliceOp.getStrides() ||
4267 extractStridedSliceOp.getOffsets() != insertStridedSliceOp.getOffsets())
4270 rewriter.
replaceOp(insertStridedSliceOp, insertStridedSliceOp.getDest());
4277class InsertStridedSliceConstantFolder final
4278 :
public OpRewritePattern<InsertStridedSliceOp> {
4284 static constexpr int64_t vectorSizeFoldThreshold = 256;
4286 LogicalResult matchAndRewrite(InsertStridedSliceOp op,
4287 PatternRewriter &rewriter)
const override {
4291 Attribute vectorDestCst;
4295 VectorType destTy = destVector.getType();
4296 if (destTy.isScalable())
4300 if (destTy.getNumElements() > vectorSizeFoldThreshold &&
4301 !destVector.hasOneUse())
4305 Attribute sourceCst;
4315 if (op.hasNonUnitStrides())
4318 VectorType sliceVecTy = sourceValue.getType();
4319 ArrayRef<int64_t> sliceShape = sliceVecTy.getShape();
4320 int64_t rankDifference = destTy.getRank() - sliceVecTy.getRank();
4321 SmallVector<int64_t, 4> offsets =
getI64SubArray(op.getOffsets());
4322 SmallVector<int64_t, 4> destStrides =
computeStrides(destTy.getShape());
4330 auto denseDest = llvm::cast<DenseElementsAttr>(vectorDestCst);
4331 auto denseSlice = llvm::cast<DenseElementsAttr>(sourceCst);
4332 auto sliceValuesIt = denseSlice.value_begin<Attribute>();
4333 auto newValues = llvm::to_vector(denseDest.getValues<Attribute>());
4334 SmallVector<int64_t> currDestPosition(offsets.begin(), offsets.end());
4335 MutableArrayRef<int64_t> currSlicePosition(
4336 currDestPosition.begin() + rankDifference, currDestPosition.end());
4337 ArrayRef<int64_t> sliceOffsets(offsets.begin() + rankDifference,
4340 int64_t linearizedPosition =
linearize(currDestPosition, destStrides);
4341 assert(linearizedPosition < destTy.getNumElements() &&
"Invalid index");
4342 assert(sliceValuesIt != denseSlice.value_end<Attribute>() &&
4343 "Invalid slice element");
4344 newValues[linearizedPosition] = *sliceValuesIt;
4357void vector::InsertStridedSliceOp::getCanonicalizationPatterns(
4358 RewritePatternSet &results, MLIRContext *context) {
4359 results.
add<FoldInsertStridedSliceSplat, FoldInsertStridedSliceOfExtract,
4360 InsertStridedSliceConstantFolder>(context);
4363OpFoldResult InsertStridedSliceOp::fold(FoldAdaptor adaptor) {
4364 if (getSourceVectorType() == getDestVectorType())
4365 return getValueToStore();
4374void OuterProductOp::build(OpBuilder &builder, OperationState &
result,
4375 Value
lhs, Value
rhs, Value acc) {
4380void OuterProductOp::print(OpAsmPrinter &p) {
4381 p <<
" " << getLhs() <<
", " << getRhs();
4383 p <<
", " << getAcc();
4384 SmallVector<NamedAttribute> attrs((*this)->getDiscardableAttrs());
4385 attrs.emplace_back(getKindAttrName(), getKindAttr());
4389 p <<
" : " << getLhs().getType() <<
", " << getRhs().getType();
4392ParseResult OuterProductOp::parse(OpAsmParser &parser, OperationState &
result) {
4393 SmallVector<OpAsmParser::UnresolvedOperand, 3> operandsInfo;
4400 if (operandsInfo.size() < 2)
4402 "expected at least 2 operands");
4403 VectorType vLHS = llvm::dyn_cast<VectorType>(tLHS);
4404 VectorType vRHS = llvm::dyn_cast<VectorType>(tRHS);
4407 "expected vector type for operand #1");
4411 if (vLHS.getRank() == 0)
4413 "expected 1-d vector for operand #1");
4414 if (vRHS && vRHS.getRank() == 0)
4416 "expected 1-d vector for operand #2");
4420 SmallVector<bool> scalableDimsRes{vLHS.getScalableDims()[0],
4421 vRHS.getScalableDims()[0]};
4422 resType = VectorType::get({vLHS.getDimSize(0), vRHS.getDimSize(0)},
4423 vLHS.getElementType(), scalableDimsRes);
4426 SmallVector<bool> scalableDimsRes{vLHS.getScalableDims()[0]};
4427 resType = VectorType::get({vLHS.getDimSize(0)}, vLHS.getElementType(),
4431 if (!
result.attributes.get(OuterProductOp::getKindAttrName(
result.name))) {
4432 result.attributes.append(
4433 OuterProductOp::getKindAttrName(
result.name),
4434 CombiningKindAttr::get(
result.getContext(),
4435 OuterProductOp::getDefaultKind()));
4441 (operandsInfo.size() > 2 &&
4446LogicalResult OuterProductOp::verify() {
4447 Type tRHS = getOperandTypeRHS();
4448 VectorType vLHS = getOperandVectorTypeLHS(),
4449 vRHS = llvm::dyn_cast<VectorType>(tRHS),
4450 vACC = getOperandVectorTypeACC(), vRES = getResultVectorType();
4452 if (vLHS.getRank() != 1)
4453 return emitOpError(
"expected 1-d vector for operand #1");
4457 if (vRHS.getRank() != 1)
4458 return emitOpError(
"expected 1-d vector for operand #2");
4459 if (vRES.getRank() != 2)
4460 return emitOpError(
"expected 2-d vector result");
4461 if (vLHS.getDimSize(0) != vRES.getDimSize(0))
4462 return emitOpError(
"expected #1 operand dim to match result dim #1");
4463 if (vRHS.getDimSize(0) != vRES.getDimSize(1))
4464 return emitOpError(
"expected #2 operand dim to match result dim #2");
4465 if (vLHS.isScalable() && !vRHS.isScalable()) {
4469 "expected either both or only #2 operand dim to be scalable");
4473 if (vRES.getRank() != 1)
4474 return emitOpError(
"expected 1-d vector result");
4475 if (vLHS.getDimSize(0) != vRES.getDimSize(0))
4476 return emitOpError(
"expected #1 operand dim to match result dim #1");
4479 if (vACC && vACC != vRES)
4480 return emitOpError(
"expected operand #3 of same type as result type");
4482 if (!getKindAttr()) {
4483 return emitOpError(
"expected 'kind' attribute of type CombiningKind (e.g. "
4484 "'vector.kind<add>')");
4489 return emitOpError(
"unsupported outerproduct type");
4498Type OuterProductOp::getExpectedMaskType() {
4499 auto vecType = this->getResultVectorType();
4500 return VectorType::get(vecType.getShape(),
4501 IntegerType::get(vecType.getContext(), 1),
4502 vecType.getScalableDims());
4516 assert(offsets.size() == sizes.size() && offsets.size() == strides.size());
4518 shape.reserve(vectorType.getRank());
4520 for (
unsigned e = offsets.size(); idx < e; ++idx)
4521 shape.push_back(llvm::cast<IntegerAttr>(sizes[idx]).getInt());
4522 for (
unsigned e = vectorType.getShape().size(); idx < e; ++idx)
4523 shape.push_back(vectorType.getShape()[idx]);
4525 return VectorType::get(
shape, vectorType.getElementType(),
4526 vectorType.getScalableDims());
4529void ExtractStridedSliceOp::build(OpBuilder &builder, OperationState &
result,
4530 Value source, ArrayRef<int64_t> offsets,
4531 ArrayRef<int64_t> sizes,
4532 ArrayRef<int64_t> strides) {
4533 result.addOperands(source);
4539 offsetsAttr, sizesAttr, stridesAttr));
4540 result.addAttribute(ExtractStridedSliceOp::getOffsetsAttrName(
result.name),
4542 result.addAttribute(ExtractStridedSliceOp::getSizesAttrName(
result.name),
4544 result.addAttribute(ExtractStridedSliceOp::getStridesAttrName(
result.name),
4548LogicalResult ExtractStridedSliceOp::verify() {
4549 auto type = getSourceVectorType();
4550 auto offsets = getOffsetsAttr();
4551 auto sizes = getSizesAttr();
4552 auto strides = getStridesAttr();
4553 if (offsets.size() != sizes.size() || offsets.size() != strides.size())
4555 "expected offsets, sizes and strides attributes of same size");
4557 auto shape = type.getShape();
4558 auto offName = getOffsetsAttrName();
4559 auto sizesName = getSizesAttrName();
4560 auto stridesName = getStridesAttrName();
4576 shape, offName, sizesName,
4581 offsets, sizes, strides);
4582 if (getResult().
getType() != resultType)
4583 return emitOpError(
"expected result type to be ") << resultType;
4585 for (
unsigned idx = 0; idx < sizes.size(); ++idx) {
4586 if (type.getScalableDims()[idx]) {
4587 auto inputDim = type.getShape()[idx];
4588 auto inputSize = llvm::cast<IntegerAttr>(sizes[idx]).getInt();
4589 if (inputDim != inputSize)
4590 return emitOpError(
"expected size at idx=")
4592 << (
" to match the corresponding base size from the input "
4594 << inputSize << (
" vs ") << inputDim << (
")");
4607 auto getElement = [](
ArrayAttr array,
int idx) {
4608 return llvm::cast<IntegerAttr>(array[idx]).getInt();
4610 ArrayAttr extractOffsets = op.getOffsets();
4613 auto insertOp = op.getSource().getDefiningOp<InsertStridedSliceOp>();
4615 if (op.getSourceVectorType().getRank() !=
4616 insertOp.getSourceVectorType().getRank())
4618 ArrayAttr insertOffsets = insertOp.getOffsets();
4619 ArrayAttr insertStrides = insertOp.getStrides();
4622 if (extractOffsets.size() > insertOffsets.size())
4624 bool patialoverlap =
false;
4625 bool disjoint =
false;
4627 for (
unsigned dim = 0, e = extractOffsets.size(); dim < e; ++dim) {
4628 if (getElement(
extractStrides, dim) != getElement(insertStrides, dim))
4630 int64_t start = getElement(insertOffsets, dim);
4631 int64_t end = start + insertOp.getSourceVectorType().getDimSize(dim);
4632 int64_t offset = getElement(extractOffsets, dim);
4633 int64_t size = getElement(extractSizes, dim);
4635 if (start <= offset && offset < end) {
4638 if (offset + size > end)
4639 patialoverlap =
true;
4640 offsetDiffs.push_back(offset - start);
4647 if (!disjoint && !patialoverlap) {
4648 op.setOperand(insertOp.getValueToStore());
4651 op.setOffsetsAttr(
b.getI64ArrayAttr(offsetDiffs));
4657 insertOp = insertOp.getDest().getDefiningOp<InsertStridedSliceOp>();
4672 auto dense = llvm::dyn_cast_if_present<DenseElementsAttr>(foldInput);
4677 if (op.hasNonUnitStrides())
4680 VectorType sourceVecTy = op.getSourceVectorType();
4684 VectorType sliceVecTy = op.getType();
4686 int64_t rank = sliceVecTy.getRank();
4698 const auto denseValuesBegin = dense.value_begin<
Attribute>();
4700 sliceValues.reserve(sliceVecTy.getNumElements());
4704 assert(linearizedPosition < sourceVecTy.getNumElements() &&
4706 sliceValues.push_back(*(denseValuesBegin + linearizedPosition));
4707 }
while (succeeded(
incSlicePosition(currSlicePosition, sliceShape, offsets)));
4709 assert(
static_cast<int64_t>(sliceValues.size()) ==
4710 sliceVecTy.getNumElements() &&
4711 "Invalid number of slice elements");
4715OpFoldResult ExtractStridedSliceOp::fold(FoldAdaptor adaptor) {
4716 if (getSourceVectorType() == getResult().
getType())
4723 llvm::dyn_cast_if_present<SplatElementsAttr>(adaptor.getSource()))
4730void ExtractStridedSliceOp::getOffsets(SmallVectorImpl<int64_t> &results) {
4752class StridedSliceFolder final
4753 :
public OpRewritePattern<ExtractStridedSliceOp> {
4755 using OpRewritePattern<ExtractStridedSliceOp>::OpRewritePattern;
4757 LogicalResult matchAndRewrite(ExtractStridedSliceOp secondOp,
4758 PatternRewriter &rewriter)
const override {
4759 auto firstOp = secondOp.getSource().getDefiningOp<ExtractStridedSliceOp>();
4763 if (secondOp.hasNonUnitStrides() || firstOp.hasNonUnitStrides())
4766 SmallVector<int64_t> firstOffsets =
getI64SubArray(firstOp.getOffsets());
4767 SmallVector<int64_t> firstSizes =
getI64SubArray(firstOp.getSizes());
4768 SmallVector<int64_t> secondOffsets =
getI64SubArray(secondOp.getOffsets());
4769 SmallVector<int64_t> secondSizes =
getI64SubArray(secondOp.getSizes());
4771 unsigned newRank = std::max(firstOffsets.size(), secondOffsets.size());
4772 SmallVector<int64_t> combinedOffsets(newRank, 0);
4773 SmallVector<int64_t> combinedSizes(newRank);
4774 ArrayRef<int64_t> firstSourceShape =
4775 firstOp.getSourceVectorType().getShape();
4776 for (
unsigned i = 0; i < newRank; ++i) {
4777 int64_t off1 = (i < firstOffsets.size()) ? firstOffsets[i] : 0;
4778 int64_t off2 = (i < secondOffsets.size()) ? secondOffsets[i] : 0;
4779 combinedOffsets[i] = off1 + off2;
4781 if (i < secondSizes.size()) {
4782 combinedSizes[i] = secondSizes[i];
4783 }
else if (i < firstSizes.size()) {
4784 combinedSizes[i] = firstSizes[i];
4786 combinedSizes[i] = firstSourceShape[i];
4790 SmallVector<int64_t> combinedStrides(newRank, 1);
4792 secondOp, firstOp.getSource(), combinedOffsets, combinedSizes,
4810class StridedSliceCreateMaskFolder final
4811 :
public OpRewritePattern<ExtractStridedSliceOp> {
4815 LogicalResult matchAndRewrite(ExtractStridedSliceOp extractStridedSliceOp,
4816 PatternRewriter &rewriter)
const override {
4817 Location loc = extractStridedSliceOp.getLoc();
4821 extractStridedSliceOp.getSource().getDefiningOp<CreateMaskOp>();
4825 if (extractStridedSliceOp.hasNonUnitStrides())
4828 SmallVector<Value> maskDimSizes(createMaskOp.getOperands());
4830 SmallVector<int64_t> sliceOffsets;
4833 SmallVector<int64_t> sliceSizes;
4837 SmallVector<Value> sliceMaskDimSizes;
4838 sliceMaskDimSizes.reserve(maskDimSizes.size());
4842 for (
auto [maskDimSize, sliceOffset, sliceSize] :
4843 llvm::zip(maskDimSizes, sliceOffsets, sliceSizes)) {
4847 IntegerAttr offsetAttr =
4849 Value offset = arith::ConstantOp::create(rewriter, loc, offsetAttr);
4850 Value sliceMaskDimSize =
4851 arith::SubIOp::create(rewriter, loc, maskDimSize, offset);
4852 sliceMaskDimSizes.push_back(sliceMaskDimSize);
4857 llvm::drop_begin(maskDimSizes, sliceMaskDimSizes.size()));
4861 extractStridedSliceOp, extractStridedSliceOp.getResult().
getType(),
4869class StridedSliceConstantMaskFolder final
4870 :
public OpRewritePattern<ExtractStridedSliceOp> {
4874 LogicalResult matchAndRewrite(ExtractStridedSliceOp extractStridedSliceOp,
4875 PatternRewriter &rewriter)
const override {
4878 auto *defOp = extractStridedSliceOp.getSource().getDefiningOp();
4879 auto constantMaskOp = dyn_cast_or_null<ConstantMaskOp>(defOp);
4880 if (!constantMaskOp)
4883 if (extractStridedSliceOp.hasNonUnitStrides())
4886 ArrayRef<int64_t> maskDimSizes = constantMaskOp.getMaskDimSizes();
4888 SmallVector<int64_t> sliceOffsets;
4891 SmallVector<int64_t> sliceSizes;
4895 SmallVector<int64_t> sliceMaskDimSizes;
4896 sliceMaskDimSizes.reserve(maskDimSizes.size());
4897 for (
auto [maskDimSize, sliceOffset, sliceSize] :
4898 llvm::zip(maskDimSizes, sliceOffsets, sliceSizes)) {
4899 int64_t sliceMaskDimSize = std::max(
4900 static_cast<int64_t
>(0),
4901 std::min(sliceOffset + sliceSize, maskDimSize) - sliceOffset);
4902 sliceMaskDimSizes.push_back(sliceMaskDimSize);
4905 if (sliceMaskDimSizes.size() < maskDimSizes.size())
4906 for (
size_t i = sliceMaskDimSizes.size(); i < maskDimSizes.size(); ++i)
4907 sliceMaskDimSizes.push_back(maskDimSizes[i]);
4910 if (llvm::is_contained(sliceMaskDimSizes, 0))
4911 sliceMaskDimSizes.assign(maskDimSizes.size(), 0);
4916 extractStridedSliceOp, extractStridedSliceOp.getResult().
getType(),
4924class StridedSliceBroadcast final
4925 :
public OpRewritePattern<ExtractStridedSliceOp> {
4929 LogicalResult matchAndRewrite(ExtractStridedSliceOp op,
4930 PatternRewriter &rewriter)
const override {
4936 unsigned srcRank = srcVecType ? srcVecType.getRank() : 0;
4937 auto dstVecType = llvm::cast<VectorType>(op.getType());
4938 unsigned dstRank = dstVecType.getRank();
4939 unsigned rankDiff = dstRank - srcRank;
4943 bool needsSlice =
false;
4944 for (
unsigned i = 0; i < srcRank; i++) {
4945 if (srcVecType.getDimSize(i) != 1 &&
4946 srcVecType.getDimSize(i) != dstVecType.getDimSize(i + rankDiff)) {
4953 SmallVector<int64_t> offsets =
4955 SmallVector<int64_t> sizes =
4957 for (
unsigned i = 0; i < srcRank; i++) {
4958 if (srcVecType.getDimSize(i) == 1) {
4966 source = ExtractStridedSliceOp::create(
4967 rewriter, op->getLoc(), source, offsets, sizes,
4976class StridedSliceSplat final :
public OpRewritePattern<ExtractStridedSliceOp> {
4980 LogicalResult matchAndRewrite(ExtractStridedSliceOp op,
4981 PatternRewriter &rewriter)
const override {
4983 Value splat = getScalarSplatSource(op.getSource());
5007class ContiguousExtractStridedSliceToExtract final
5008 :
public OpRewritePattern<ExtractStridedSliceOp> {
5012 LogicalResult matchAndRewrite(ExtractStridedSliceOp op,
5013 PatternRewriter &rewriter)
const override {
5014 if (op.hasNonUnitStrides())
5016 Value source = op.getOperand();
5017 auto sourceType = cast<VectorType>(source.
getType());
5018 if (sourceType.isScalable() || sourceType.getRank() == 0)
5027 for (numOffsets = sizes.size(); numOffsets > 0; --numOffsets) {
5028 if (sizes[numOffsets - 1] != sourceType.getDimSize(numOffsets - 1))
5035 if (numOffsets == 0)
5040 if (numOffsets == sourceType.getRank() &&
5041 static_cast<int>(sizes.size()) == sourceType.getRank())
5045 for (
int i = 0; i < numOffsets; ++i) {
5053 while (numOffsets <
static_cast<int>(sizes.size()) - 1 &&
5054 sizes[numOffsets] == 1) {
5059 auto extractOffsets = ArrayRef(offsets).take_front(numOffsets);
5060 Value extract = vector::ExtractOp::create(rewriter, op->getLoc(), source,
5069void ExtractStridedSliceOp::getCanonicalizationPatterns(
5070 RewritePatternSet &results, MLIRContext *context) {
5073 results.
add<StridedSliceFolder, StridedSliceCreateMaskFolder,
5074 StridedSliceConstantMaskFolder, StridedSliceBroadcast,
5075 StridedSliceSplat, ContiguousExtractStridedSliceToExtract>(
5085void TransferReadOp::build(OpBuilder &builder, OperationState &
result,
5086 VectorType vectorType, Value source,
5088 AffineMapAttr permutationMapAttr,
5091 Type elemType = llvm::cast<ShapedType>(source.
getType()).getElementType();
5093 padding = ub::PoisonOp::create(builder,
result.location, elemType);
5096 build(builder,
result, vectorType, source,
indices, permutationMapAttr,
5097 *padding, Value(), inBoundsAttr);
5105void TransferReadOp::build(OpBuilder &builder, OperationState &
result,
5106 VectorType vectorType, Value source,
5108 AffineMap permutationMap,
5109 std::optional<ArrayRef<bool>> inBounds) {
5110 if (!permutationMap)
5112 llvm::cast<ShapedType>(source.
getType()), vectorType);
5113 auto permutationMapAttr = AffineMapAttr::get(permutationMap);
5114 auto inBoundsAttr = (inBounds && !inBounds.value().empty())
5117 SmallVector<bool>(vectorType.getRank(),
false));
5119 build(builder,
result, vectorType, source,
indices, padding,
5120 permutationMapAttr, inBoundsAttr);
5126void TransferReadOp::build(OpBuilder &builder, OperationState &
result,
5127 VectorType vectorType, Value source,
5129 std::optional<ArrayRef<bool>> inBounds) {
5131 build(builder,
result, vectorType, source,
indices, padding,
5132 AffineMap(), inBounds);
5135template <
typename EmitFun>
5137 EmitFun emitOpError) {
5139 for (
auto expr : permutationMap.
getResults()) {
5140 auto dim = dyn_cast<AffineDimExpr>(expr);
5141 auto zero = dyn_cast<AffineConstantExpr>(expr);
5143 if (zero.getValue() != 0) {
5145 "requires a projected permutation_map (at most one dim or the zero "
5146 "constant can appear in each result)");
5151 return emitOpError(
"requires a projected permutation_map (at most one "
5152 "dim or the zero constant can appear in each result)");
5154 if (seen[dim.getPosition()]) {
5156 "requires a permutation_map that is a permutation (found one dim "
5157 "used more than once)");
5159 seen[dim.getPosition()] =
true;
5166 VectorType vectorType, VectorType maskType,
5167 VectorType inferredMaskType,
AffineMap permutationMap,
5169 if (op->hasDiscardableAttr(
"masked")) {
5170 return op->emitOpError(
"masked attribute has been removed. "
5171 "Use in_bounds instead.");
5174 if (!llvm::isa<MemRefType, RankedTensorType>(shapedType))
5175 return op->emitOpError(
5176 "requires source to be a memref or ranked tensor type");
5178 auto elementType = shapedType.getElementType();
5180 if (
auto vectorElementType = llvm::dyn_cast<VectorType>(elementType)) {
5182 unsigned sourceVecSize =
5184 vectorElementType.getShape().back();
5185 unsigned resultVecSize =
5187 vectorType.getShape().back();
5188 if (resultVecSize % sourceVecSize != 0)
5189 return op->emitOpError(
5190 "requires the bitwidth of the minor 1-D vector to be an integral "
5191 "multiple of the bitwidth of the minor 1-D vector of the source");
5193 unsigned sourceVecEltRank = vectorElementType.getRank();
5194 unsigned resultVecRank = vectorType.getRank();
5195 if (sourceVecEltRank > resultVecRank)
5196 return op->emitOpError(
5197 "requires source vector element and vector result ranks to match.");
5198 unsigned rankOffset = resultVecRank - sourceVecEltRank;
5201 return op->emitOpError(
"requires a permutation_map with result dims of "
5202 "the same rank as the vector type");
5205 return op->emitOpError(
"does not support masks with vector element type");
5208 unsigned minorSize =
5209 vectorType.getRank() == 0 ? 1 : vectorType.getShape().back();
5210 unsigned resultVecSize =
5213 return op->emitOpError(
5214 "requires the bitwidth of the minor 1-D vector to be an integral "
5215 "multiple of the bitwidth of the source element type");
5219 return op->emitOpError(
"requires a permutation_map with result dims of "
5220 "the same rank as the vector type");
5224 return op->emitOpError(
"requires permutation_map without symbols");
5226 if (permutationMap.
getNumInputs() != shapedType.getRank())
5227 return op->emitOpError(
"requires a permutation_map with input dims of the "
5228 "same rank as the source type");
5230 if (maskType && maskType != inferredMaskType)
5231 return op->emitOpError(
"inferred mask type (")
5232 << inferredMaskType <<
") and mask operand type (" << maskType
5236 return op->emitOpError(
"expects the in_bounds attr of same rank "
5237 "as permutation_map results: ")
5238 << AffineMapAttr::get(permutationMap)
5239 <<
" vs inBounds of size: " << inBounds.size();
5247 if (llvm::any_of(op.getInBoundsValues(), [](
bool b) { return b; }))
5248 attrs.emplace_back(op.getInBoundsAttrName(), op.getInBounds());
5249 if (!op.getPermutationMap().isMinorIdentity())
5250 attrs.emplace_back(op.getPermutationMapAttrName(),
5251 AffineMapAttr::get(op.getPermutationMap()));
5256void TransferReadOp::print(OpAsmPrinter &p) {
5259 p <<
", " << getMask();
5266 auto i1Type = IntegerType::get(permMap.
getContext(), 1);
5268 assert(invPermMap &&
"Inversed permutation map couldn't be computed");
5273 if (maskShape.empty())
5274 maskShape.push_back(1);
5279 return VectorType::get(maskShape, i1Type, scalableDims);
5296 if (hasMask.succeeded()) {
5303 if (types.size() != 2)
5304 return parser.
emitError(typesLoc,
"requires two types");
5306 auto shapedType = llvm::dyn_cast<ShapedType>(types[0]);
5307 if (!shapedType || !llvm::isa<MemRefType, RankedTensorType>(shapedType))
5308 return parser.
emitError(typesLoc,
"requires memref or ranked tensor type");
5309 VectorType vectorType = llvm::dyn_cast<VectorType>(types[1]);
5311 return parser.
emitError(typesLoc,
"requires vector type");
5312 auto permMapAttrName = TransferReadOp::getPermutationMapAttrName(
result.name);
5316 if (shapedType.getRank() <
5319 "expected a custom permutation_map when "
5320 "rank(source) != rank(destination)");
5322 result.attributes.set(permMapAttrName, AffineMapAttr::get(permMap));
5324 permMap = llvm::cast<AffineMapAttr>(permMapAttr).getValue();
5326 auto inBoundsAttrName = TransferReadOp::getInBoundsAttrName(
result.name);
5327 Attribute inBoundsAttr =
result.attributes.get(inBoundsAttrName);
5328 if (!inBoundsAttr) {
5329 result.addAttribute(inBoundsAttrName,
5338 if (hasMask.succeeded()) {
5339 if (llvm::dyn_cast<VectorType>(shapedType.getElementType()))
5341 maskInfo.
location,
"does not support masks with vector element type");
5344 "expected the same rank for the vector and the "
5345 "results of the permutation map");
5353 result.addAttribute(TransferReadOp::getOperandSegmentSizeAttr(),
5355 {1, static_cast<int32_t>(indexInfo.size()), 1,
5356 static_cast<int32_t>(hasMask.succeeded())}));
5360LogicalResult TransferReadOp::verify() {
5362 ShapedType shapedType = getShapedType();
5364 VectorType maskType = getMaskType();
5365 auto paddingType = getPadding().getType();
5366 auto permutationMap = getPermutationMap();
5367 VectorType inferredMaskType =
5370 auto sourceElementType = shapedType.getElementType();
5372 if (
static_cast<int64_t
>(
getIndices().size()) != shapedType.getRank())
5373 return emitOpError(
"requires ") << shapedType.getRank() <<
" indices";
5376 shapedType, vectorType, maskType,
5377 inferredMaskType, permutationMap, getInBounds())))
5380 if (
auto sourceVectorElementType =
5381 llvm::dyn_cast<VectorType>(sourceElementType)) {
5384 if (sourceVectorElementType != paddingType)
5386 "requires source element type and padding type to match.");
5390 if (!VectorType::isValidElementType(paddingType))
5391 return emitOpError(
"requires valid padding vector elemental type");
5394 if (paddingType != sourceElementType)
5396 "requires formal padding and source of the same elemental type");
5400 [&](Twine t) {
return emitOpError(t); });
5407Type TransferReadOp::getExpectedMaskType() {
5414VectorType TransferReadOp::getVectorType() {
5415 return cast<VectorType>(getVector().
getType());
5418template <
typename TransferOp>
5422 if (op.getShapedType().isDynamicDim(indicesIdx))
5426 if (op.getVectorType().getScalableDims()[resultIdx])
5430 if (!cstOp.has_value())
5433 int64_t sourceSize = op.getShapedType().getDimSize(indicesIdx);
5434 int64_t vectorSize = op.getVectorType().getDimSize(resultIdx);
5440 int64_t maxStart = sourceSize - vectorSize;
5444 return *cstOp >= 0 && *cstOp <= maxStart;
5447template <
typename TransferOp>
5451 if (op.getTransferRank() == 0)
5454 bool changed =
false;
5456 newInBounds.reserve(op.getTransferRank());
5461 for (
unsigned i = 0; i < op.getTransferRank(); ++i) {
5463 if (op.isDimInBounds(i)) {
5464 newInBounds.push_back(
true);
5469 bool inBounds =
false;
5470 auto dimExpr = dyn_cast<AffineDimExpr>(permutationMap.
getResult(i));
5473 dimExpr.getPosition());
5474 nonBcastDims.push_back(i);
5477 newInBounds.push_back(inBounds);
5479 changed |= inBounds;
5485 bool allNonBcastDimsInBounds = llvm::all_of(
5486 nonBcastDims, [&newInBounds](
unsigned idx) {
return newInBounds[idx]; });
5487 if (allNonBcastDimsInBounds) {
5489 changed |= !newInBounds[idx];
5490 newInBounds[idx] =
true;
5498 op.setInBoundsAttr(
b.getBoolArrayAttr(newInBounds));
5502template <
typename TransferOp>
5504 auto mask = op.getMask();
5511 op.getMaskMutable().clear();
5519template <
typename TransferOp>
5521 VectorType vecType = op.getVectorType();
5522 if (vecType.getRank() != 1 || vecType.getShape()[0] != 1 ||
5523 vecType.isScalable())
5530 int64_t srcRank = op.getShapedType().getRank();
5536 op.setPermutationMapAttr(AffineMapAttr::get(minorIdentity));
5550static Value foldRAW(TransferReadOp readOp) {
5551 if (!llvm::isa<RankedTensorType>(readOp.getShapedType()))
5553 auto defWrite = readOp.getBase().getDefiningOp<vector::TransferWriteOp>();
5556 return defWrite.getVector();
5558 cast<VectorTransferOpInterface>(defWrite.getOperation()),
5559 cast<VectorTransferOpInterface>(readOp.getOperation())))
5561 defWrite = defWrite.getBase().getDefiningOp<vector::TransferWriteOp>();
5566OpFoldResult TransferReadOp::fold(FoldAdaptor) {
5567 if (Value vec = foldRAW(*
this))
5580 return OpFoldResult();
5583std::optional<SmallVector<int64_t, 4>> TransferReadOp::getShapeForUnroll() {
5587void TransferReadOp::getEffects(
5588 SmallVectorImpl<SideEffects::EffectInstance<MemoryEffects::Effect>>
5590 if (llvm::isa<MemRefType>(getShapedType()))
5591 effects.emplace_back(MemoryEffects::Read::get(), &getBaseMutable(),
5592 SideEffects::DefaultResource::get());
5596 if (hasPureTensorSemantics())
5603static AffineMap inverseWithUnusedDims(AffineMap map) {
5605 "expected a projected permutation map");
5610 int64_t pos = cast<AffineDimExpr>(
result).getPosition();
5640struct TransferReadAfterWriteToBroadcast
5641 :
public OpRewritePattern<TransferReadOp> {
5644 LogicalResult matchAndRewrite(TransferReadOp readOp,
5645 PatternRewriter &rewriter)
const override {
5646 auto defWrite = readOp.getBase().getDefiningOp<vector::TransferWriteOp>();
5650 if (!readOp.hasPureTensorSemantics() || !defWrite.hasPureTensorSemantics())
5656 if (readOp.getMask() || defWrite.getMask() || readOp.isMasked())
5659 if (readOp.getIndices() != defWrite.getIndices())
5662 if (readOp.hasOutOfBoundsDim() || defWrite.hasOutOfBoundsDim())
5666 if (readOp.getTransferChunkAccessed() !=
5667 defWrite.getTransferChunkAccessed())
5674 AffineMap readMap = readOp.getPermutationMap();
5675 AffineMap writeMap = defWrite.getPermutationMap();
5676 AffineMap invWriteMap = inverseWithUnusedDims(writeMap);
5677 AffineMap composedMap = readMap.
compose(invWriteMap);
5691 int64_t numBroadcastedDims = broadcastedDims.size();
5692 auto invPerm = llvm::to_vector_of<int64_t>(broadcastedDims);
5694 for (
auto [idx, expr] : llvm::enumerate(composedMap.
getResults())) {
5695 if (
auto dim = dyn_cast<AffineDimExpr>(expr)) {
5696 int64_t effectiveDim = dim.getPosition() + numBroadcastedDims;
5697 invPerm[effectiveDim] = idx;
5702 VectorType readVecTy = readOp.getVectorType();
5704 auto broadcastedVecTy =
5706 readVecTy.getElementType(),
5709 Value vec = defWrite.getVector();
5710 Location loc = readOp.getLoc();
5711 vec = vector::BroadcastOp::create(rewriter, loc, broadcastedVecTy, vec);
5718void TransferReadOp::getCanonicalizationPatterns(RewritePatternSet &results,
5719 MLIRContext *context) {
5720 results.
add<TransferReadAfterWriteToBroadcast>(context);
5723FailureOr<std::optional<SmallVector<Value>>>
5724TransferReadOp::bubbleDownCasts(OpBuilder &builder) {
5725 if (!hasPureBufferSemantics())
5736void TransferWriteOp::build(OpBuilder &builder, OperationState &
result,
5738 AffineMapAttr permutationMapAttr,
5741 Type resultType = llvm::dyn_cast<RankedTensorType>(dest.
getType());
5742 build(builder,
result, resultType, vector, dest,
indices, permutationMapAttr,
5743 mask, inBoundsAttr);
5747void TransferWriteOp::build(OpBuilder &builder, OperationState &
result,
5749 AffineMapAttr permutationMapAttr,
5751 build(builder,
result, vector, dest,
indices, permutationMapAttr,
5752 Value(), inBoundsAttr);
5757void TransferWriteOp::build(OpBuilder &builder, OperationState &
result,
5759 AffineMap permutationMap,
5760 std::optional<ArrayRef<bool>> inBounds) {
5761 if (!permutationMap)
5764 llvm::cast<VectorType>(vector.
getType()));
5765 auto permutationMapAttr = AffineMapAttr::get(permutationMap);
5767 (inBounds && !inBounds.value().empty())
5770 llvm::cast<VectorType>(vector.
getType()).getRank(),
false));
5771 build(builder,
result, vector, dest,
indices, permutationMapAttr,
5772 Value(), inBoundsAttr);
5777void TransferWriteOp::build(OpBuilder &builder, OperationState &
result,
5779 std::optional<ArrayRef<bool>> inBounds) {
5784ParseResult TransferWriteOp::parse(OpAsmParser &parser,
5785 OperationState &
result) {
5788 OpAsmParser::UnresolvedOperand vectorInfo, sourceInfo;
5789 SmallVector<OpAsmParser::UnresolvedOperand, 8> indexInfo;
5790 SmallVector<Type, 2> types;
5791 OpAsmParser::UnresolvedOperand maskInfo;
5797 if (hasMask.succeeded() && parser.
parseOperand(maskInfo))
5802 if (types.size() != 2)
5803 return parser.
emitError(typesLoc,
"requires two types");
5805 VectorType vectorType = llvm::dyn_cast<VectorType>(types[0]);
5807 return parser.
emitError(typesLoc,
"requires vector type");
5808 ShapedType shapedType = llvm::dyn_cast<ShapedType>(types[1]);
5809 if (!shapedType || !llvm::isa<MemRefType, RankedTensorType>(shapedType))
5810 return parser.
emitError(typesLoc,
"requires memref or ranked tensor type");
5811 auto permMapAttrName =
5812 TransferWriteOp::getPermutationMapAttrName(
result.name);
5813 auto permMapAttr =
result.attributes.get(permMapAttrName);
5816 if (shapedType.getRank() <
5819 "expected a custom permutation_map when "
5820 "rank(source) != rank(destination)");
5822 result.attributes.set(permMapAttrName, AffineMapAttr::get(permMap));
5824 permMap = llvm::cast<AffineMapAttr>(permMapAttr).getValue();
5826 auto inBoundsAttrName = TransferWriteOp::getInBoundsAttrName(
result.name);
5827 Attribute inBoundsAttr =
result.attributes.get(inBoundsAttrName);
5828 if (!inBoundsAttr) {
5829 result.addAttribute(inBoundsAttrName,
5837 if (hasMask.succeeded()) {
5838 if (llvm::dyn_cast<VectorType>(shapedType.getElementType()))
5840 maskInfo.
location,
"does not support masks with vector element type");
5843 "expected the same rank for the vector and the "
5844 "results of the permutation map");
5850 result.addAttribute(TransferWriteOp::getOperandSegmentSizeAttr(),
5852 {1, 1, static_cast<int32_t>(indexInfo.size()),
5853 static_cast<int32_t>(hasMask.succeeded())}));
5854 return failure(llvm::isa<RankedTensorType>(shapedType) &&
5858void TransferWriteOp::print(OpAsmPrinter &p) {
5861 p <<
", " << getMask();
5866LogicalResult TransferWriteOp::verify() {
5868 ShapedType shapedType = getShapedType();
5870 VectorType maskType = getMaskType();
5871 auto permutationMap = getPermutationMap();
5872 VectorType inferredMaskType =
5876 if (llvm::size(
getIndices()) != shapedType.getRank())
5877 return emitOpError(
"requires ") << shapedType.getRank() <<
" indices";
5881 if (hasBroadcastDim())
5882 return emitOpError(
"should not have broadcast dimensions");
5885 shapedType, vectorType, maskType,
5886 inferredMaskType, permutationMap, getInBounds())))
5890 [&](Twine t) {
return emitOpError(t); });
5899Type TransferWriteOp::getExpectedMaskType() {
5906Value TransferWriteOp::getVector() {
return getOperand(0); }
5907VectorType TransferWriteOp::getVectorType() {
5908 return cast<VectorType>(getValueToStore().
getType());
5931static LogicalResult foldReadInitWrite(TransferWriteOp write,
5932 ArrayRef<Attribute>,
5933 SmallVectorImpl<OpFoldResult> &results) {
5935 if (write.getTransferRank() == 0)
5937 auto rankedTensorType =
5938 llvm::dyn_cast<RankedTensorType>(write.getBase().getType());
5940 if (!rankedTensorType)
5943 auto read = write.getVector().getDefiningOp<vector::TransferReadOp>();
5947 if (read.getTransferRank() == 0)
5950 if (!read.getPermutationMap().isMinorIdentity() ||
5951 !write.getPermutationMap().isMinorIdentity())
5954 if (read.getTransferRank() != write.getTransferRank())
5957 if (read.hasOutOfBoundsDim() || write.hasOutOfBoundsDim())
5960 if (read.getMask() || write.getMask())
5963 if (read.getBase().getType() != rankedTensorType)
5966 if (read.getVectorType() != write.getVectorType())
5969 if (read.getVectorType().getShape() != rankedTensorType.getShape())
5972 auto isNotConstantZero = [](Value v) {
5974 return !cstOp.has_value() || cstOp.value() != 0;
5976 if (llvm::any_of(read.getIndices(), isNotConstantZero) ||
5977 llvm::any_of(write.getIndices(), isNotConstantZero))
5980 results.push_back(read.getBase());
5984static bool checkSameValueWAR(vector::TransferReadOp read,
5985 vector::TransferWriteOp write) {
5986 return read.getBase() == write.getBase() &&
5987 read.getIndices() == write.getIndices() &&
5988 read.getPermutationMap() == write.getPermutationMap() &&
5989 read.getVectorType() == write.getVectorType() && !read.getMask() &&
6006static LogicalResult foldWAR(TransferWriteOp write,
6007 SmallVectorImpl<OpFoldResult> &results) {
6008 if (!llvm::isa<RankedTensorType>(write.getBase().getType()))
6010 auto read = write.getVector().getDefiningOp<vector::TransferReadOp>();
6014 if (!checkSameValueWAR(read, write))
6016 results.push_back(read.getBase());
6020LogicalResult TransferWriteOp::fold(FoldAdaptor adaptor,
6021 SmallVectorImpl<OpFoldResult> &results) {
6022 if (succeeded(foldReadInitWrite(*
this, adaptor.getOperands(), results)))
6024 if (succeeded(foldWAR(*
this, results)))
6038std::optional<SmallVector<int64_t, 4>> TransferWriteOp::getShapeForUnroll() {
6042void TransferWriteOp::getEffects(
6043 SmallVectorImpl<SideEffects::EffectInstance<MemoryEffects::Effect>>
6045 if (llvm::isa<MemRefType>(getShapedType()))
6046 effects.emplace_back(MemoryEffects::Write::get(), &getBaseMutable(),
6047 SideEffects::DefaultResource::get());
6051 if (hasPureTensorSemantics())
6081class FoldWaw final :
public OpRewritePattern<TransferWriteOp> {
6084 LogicalResult matchAndRewrite(TransferWriteOp writeOp,
6085 PatternRewriter &rewriter)
const override {
6086 if (!llvm::isa<RankedTensorType>(writeOp.getShapedType()))
6088 vector::TransferWriteOp writeToModify = writeOp;
6090 auto defWrite = writeOp.getBase().getDefiningOp<vector::TransferWriteOp>();
6094 writeToModify.getBaseMutable().assign(defWrite.getBase());
6099 cast<VectorTransferOpInterface>(defWrite.getOperation()),
6100 cast<VectorTransferOpInterface>(writeOp.getOperation())))
6104 if (!defWrite->hasOneUse())
6106 writeToModify = defWrite;
6107 defWrite = defWrite.getBase().getDefiningOp<vector::TransferWriteOp>();
6136struct SwapExtractSliceOfTransferWrite
6137 :
public OpRewritePattern<tensor::InsertSliceOp> {
6141 LogicalResult matchAndRewrite(tensor::InsertSliceOp insertOp,
6142 PatternRewriter &rewriter)
const override {
6143 if (!insertOp.hasUnitStride())
6146 insertOp.getSource().getDefiningOp<tensor::ExtractSliceOp>();
6147 if (!extractOp || !extractOp.hasUnitStride() || !extractOp->hasOneUse())
6149 auto transferOp = extractOp.getSource().getDefiningOp<TransferWriteOp>();
6150 if (!transferOp || !transferOp->hasOneUse())
6155 if (insertOp.getSourceType().getRank() != transferOp.getTransferRank()) {
6157 "use-def chain is rank-reducing");
6161 if (!extractOp.hasZeroOffset()) {
6163 "ExtractSliceOp has non-zero offset");
6167 if (!llvm::all_of(transferOp.getIndices(), [](Value value) {
6168 return getConstantIntValue(value) == static_cast<int64_t>(0);
6171 "TranferWriteOp has non-zero offset");
6175 if (insertOp.getMixedSizes().size() != extractOp.getMixedSizes().size()) {
6177 insertOp,
"InsertSliceOp and ExtractSliceOp ranks differ");
6180 for (
auto [insertSize, extractSize] :
6181 llvm::zip_equal(insertOp.getMixedSizes(), extractOp.getMixedSizes())) {
6184 insertOp,
"InsertSliceOp and ExtractSliceOp sizes differ");
6189 assert(transferOp.getVectorType().hasStaticShape() &&
6190 "expected vector to have a static shape");
6191 ArrayRef<int64_t>
vectorShape = transferOp.getVectorType().getShape();
6193 transferOp.getPermutationMap(), transferOp.getShapedType().getShape());
6194 if (transferOp.getMask() || !
vectorShape.equals(resultShape)) {
6196 insertOp,
"TransferWriteOp may not write the full tensor.");
6201 SmallVector<bool> newInBounds(
vectorShape.size(),
false);
6202 auto newExtractOp = tensor::ExtractSliceOp::create(
6203 rewriter, extractOp.getLoc(), insertOp.getSourceType(),
6204 insertOp.getDest(), insertOp.getMixedOffsets(),
6205 insertOp.getMixedSizes(), insertOp.getMixedStrides());
6206 auto newTransferWriteOp = TransferWriteOp::create(
6207 rewriter, transferOp.getLoc(), transferOp.getVector(),
6208 newExtractOp.getResult(), transferOp.getIndices(),
6209 transferOp.getPermutationMapAttr(),
6212 insertOp.getSourceMutable().assign(newTransferWriteOp.getResult());
6220void TransferWriteOp::getCanonicalizationPatterns(RewritePatternSet &results,
6221 MLIRContext *context) {
6222 results.
add<FoldWaw, SwapExtractSliceOfTransferWrite>(context);
6225FailureOr<std::optional<SmallVector<Value>>>
6226TransferWriteOp::bubbleDownCasts(OpBuilder &builder) {
6227 if (!hasPureBufferSemantics())
6241 result = dyn_cast<BoolAttr>(attr);
6244 "expected boolean attribute");
6248static void printBoolAttr(OpAsmPrinter &printer, Operation *, BoolAttr attr) {
6252static LogicalResult verifyLoadStoreMemRefLayout(Operation *op,
6254 MemRefType memRefTy) {
6257 if (!vecTy.isScalable() &&
6258 (vecTy.getRank() == 0 || vecTy.getNumElements() == 1))
6261 if (!memRefTy.isLastDimUnitStride())
6262 return op->
emitOpError(
"most minor memref dim must have unit stride");
6266LogicalResult vector::LoadOp::verify() {
6270 if (
failed(verifyLoadStoreMemRefLayout(*
this, resVecTy, memRefTy)))
6277 return emitOpError(
"memref strides must be non-negative");
6279 if (memRefTy.getRank() < resVecTy.getRank())
6281 "destination memref has lower rank than the result vector");
6284 Type memElemTy = memRefTy.getElementType();
6285 if (
auto memVecTy = llvm::dyn_cast<VectorType>(memElemTy)) {
6286 if (memVecTy != resVecTy)
6287 return emitOpError(
"base memref and result vector types should match");
6288 memElemTy = memVecTy.getElementType();
6291 if (resVecTy.getElementType() != memElemTy)
6292 return emitOpError(
"base and result element types should match");
6293 if (llvm::size(
getIndices()) != memRefTy.getRank())
6294 return emitOpError(
"requires ") << memRefTy.getRank() <<
" indices";
6298OpFoldResult LoadOp::fold(FoldAdaptor) {
6301 return OpFoldResult();
6304std::optional<SmallVector<int64_t, 4>> LoadOp::getShapeForUnroll() {
6308FailureOr<std::optional<SmallVector<Value>>>
6309LoadOp::bubbleDownCasts(OpBuilder &builder) {
6318LogicalResult vector::StoreOp::verify() {
6322 if (
failed(verifyLoadStoreMemRefLayout(*
this, valueVecTy, memRefTy)))
6329 return emitOpError(
"memref strides must be non-negative");
6331 if (memRefTy.getRank() < valueVecTy.getRank())
6332 return emitOpError(
"source memref has lower rank than the vector to store");
6335 Type memElemTy = memRefTy.getElementType();
6336 if (
auto memVecTy = llvm::dyn_cast<VectorType>(memElemTy)) {
6337 if (memVecTy != valueVecTy)
6339 "base memref and valueToStore vector types should match");
6340 memElemTy = memVecTy.getElementType();
6343 if (valueVecTy.getElementType() != memElemTy)
6344 return emitOpError(
"base and valueToStore element type should match");
6345 if (llvm::size(
getIndices()) != memRefTy.getRank())
6346 return emitOpError(
"requires ") << memRefTy.getRank() <<
" indices";
6350LogicalResult StoreOp::fold(FoldAdaptor adaptor,
6351 SmallVectorImpl<OpFoldResult> &results) {
6355std::optional<SmallVector<int64_t, 4>> StoreOp::getShapeForUnroll() {
6359FailureOr<std::optional<SmallVector<Value>>>
6360StoreOp::bubbleDownCasts(OpBuilder &builder) {
6369LogicalResult MaskedLoadOp::verify() {
6370 VectorType maskVType = getMaskVectorType();
6371 VectorType passVType = getPassThruVectorType();
6375 if (
failed(verifyLoadStoreMemRefLayout(*
this, resVType, memType)))
6382 return emitOpError(
"memref strides must be non-negative");
6387 if (llvm::size(
getIndices()) != memType.getRank())
6388 return emitOpError(
"requires ") << memType.getRank() <<
" indices";
6389 if (resVType.getShape() != maskVType.getShape())
6390 return emitOpError(
"expected result shape to match mask shape");
6391 if (resVType != passVType)
6392 return emitOpError(
"expected pass_thru of same type as result type");
6397class MaskedLoadFolder final :
public OpRewritePattern<MaskedLoadOp> {
6400 LogicalResult matchAndRewrite(MaskedLoadOp
load,
6401 PatternRewriter &rewriter)
const override {
6413 llvm_unreachable(
"Unexpected 1DMaskFormat on MaskedLoad");
6418void MaskedLoadOp::getCanonicalizationPatterns(RewritePatternSet &results,
6419 MLIRContext *context) {
6420 results.
add<MaskedLoadFolder>(context);
6423OpFoldResult MaskedLoadOp::fold(FoldAdaptor) {
6426 return OpFoldResult();
6429FailureOr<std::optional<SmallVector<Value>>>
6430MaskedLoadOp::bubbleDownCasts(OpBuilder &builder) {
6439LogicalResult MaskedStoreOp::verify() {
6440 VectorType maskVType = getMaskVectorType();
6444 if (
failed(verifyLoadStoreMemRefLayout(*
this, valueVType, memType)))
6451 return emitOpError(
"memref strides must be non-negative");
6456 if (llvm::size(
getIndices()) != memType.getRank())
6457 return emitOpError(
"requires ") << memType.getRank() <<
" indices";
6458 if (valueVType.getShape() != maskVType.getShape())
6459 return emitOpError(
"expected valueToStore shape to match mask shape");
6464class MaskedStoreFolder final :
public OpRewritePattern<MaskedStoreOp> {
6467 LogicalResult matchAndRewrite(MaskedStoreOp store,
6468 PatternRewriter &rewriter)
const override {
6472 store, store.getValueToStore(), store.getBase(), store.getIndices());
6480 llvm_unreachable(
"Unexpected 1DMaskFormat on MaskedStore");
6485void MaskedStoreOp::getCanonicalizationPatterns(RewritePatternSet &results,
6486 MLIRContext *context) {
6487 results.
add<MaskedStoreFolder>(context);
6490LogicalResult MaskedStoreOp::fold(FoldAdaptor adaptor,
6491 SmallVectorImpl<OpFoldResult> &results) {
6495FailureOr<std::optional<SmallVector<Value>>>
6496MaskedStoreOp::bubbleDownCasts(OpBuilder &builder) {
6505LogicalResult GatherOp::verify() {
6506 VectorType indVType = getIndexVectorType();
6507 VectorType maskVType = getMaskVectorType();
6509 ShapedType baseType = getBaseType();
6511 if (!llvm::isa<MemRefType, RankedTensorType>(baseType))
6512 return emitOpError(
"requires base to be a memref or ranked tensor type");
6517 if (
auto memRefType = dyn_cast<MemRefType>(baseType))
6519 return emitOpError(
"memref strides must be non-negative");
6524 if (llvm::size(getOffsets()) != baseType.getRank())
6525 return emitOpError(
"requires ") << baseType.getRank() <<
" indices";
6526 if (resVType.getShape() != indVType.getShape())
6527 return emitOpError(
"expected result dim to match indices dim");
6528 if (resVType.getShape() != maskVType.getShape())
6529 return emitOpError(
"expected result dim to match mask dim");
6530 if (resVType != getPassThruVectorType())
6531 return emitOpError(
"expected pass_thru of same type as result type");
6532 if (getAlignmentAttr() && !isa<MemRefType>(baseType)) {
6534 "alignment is only supported for memref bases, not tensor bases");
6543Type GatherOp::getExpectedMaskType() {
6544 auto vecType = this->getIndexVectorType();
6545 return VectorType::get(vecType.getShape(),
6546 IntegerType::get(vecType.getContext(), 1),
6547 vecType.getScalableDims());
6550std::optional<SmallVector<int64_t, 4>> GatherOp::getShapeForUnroll() {
6555static LogicalResult isZeroBasedContiguousSeq(Value indexVec) {
6556 auto vecType = dyn_cast<VectorType>(indexVec.
getType());
6557 if (!vecType || vecType.getRank() != 1 || vecType.isScalable())
6563 DenseIntElementsAttr elements;
6568 llvm::equal(elements, llvm::seq<int64_t>(0, vecType.getNumElements())));
6572class GatherFolder final :
public OpRewritePattern<GatherOp> {
6575 LogicalResult matchAndRewrite(GatherOp gather,
6576 PatternRewriter &rewriter)
const override {
6581 rewriter.
replaceOp(gather, gather.getPassThru());
6586 llvm_unreachable(
"Unexpected 1DMaskFormat on GatherFolder");
6592class FoldContiguousGather final :
public OpRewritePattern<GatherOp> {
6595 LogicalResult matchAndRewrite(GatherOp op,
6596 PatternRewriter &rewriter)
const override {
6597 if (!isa<MemRefType>(op.getBase().getType()))
6600 if (
failed(isZeroBasedContiguousSeq(op.getIndices())))
6604 op.getOffsets(), op.getMask(),
6611void GatherOp::getCanonicalizationPatterns(RewritePatternSet &results,
6612 MLIRContext *context) {
6613 results.
add<GatherFolder, FoldContiguousGather>(context);
6616FailureOr<std::optional<SmallVector<Value>>>
6617GatherOp::bubbleDownCasts(OpBuilder &builder) {
6626LogicalResult ScatterOp::verify() {
6627 VectorType indVType = getIndexVectorType();
6628 VectorType maskVType = getMaskVectorType();
6630 ShapedType baseType = getBaseType();
6632 if (!llvm::isa<MemRefType, RankedTensorType>(baseType))
6633 return emitOpError(
"requires base to be a memref or ranked tensor type");
6638 if (
auto memRefType = dyn_cast<MemRefType>(baseType))
6640 return emitOpError(
"memref strides must be non-negative");
6645 if (llvm::size(getOffsets()) != baseType.getRank())
6646 return emitOpError(
"requires ") << baseType.getRank() <<
" indices";
6647 if (valueVType.getShape() != indVType.getShape())
6648 return emitOpError(
"expected valueToStore dim to match indices dim");
6649 if (valueVType.getShape() != maskVType.getShape())
6650 return emitOpError(
"expected valueToStore dim to match mask dim");
6651 if (getAlignmentAttr() && !isa<MemRefType>(baseType)) {
6653 "alignment is only supported for memref bases, not tensor bases");
6658class ScatterFolder final :
public OpRewritePattern<ScatterOp> {
6661 LogicalResult matchAndRewrite(ScatterOp scatter,
6662 PatternRewriter &rewriter)
const override {
6663 ShapedType baseType = scatter.getBaseType();
6664 bool isMemRef = isa<MemRefType>(baseType);
6665 if (!isMemRef && !isa<RankedTensorType>(baseType))
6678 rewriter.
replaceOp(scatter, scatter.getBase());
6683 llvm_unreachable(
"Unexpected 1DMaskFormat on ScatterFolder");
6689class FoldContiguousScatter final :
public OpRewritePattern<ScatterOp> {
6692 LogicalResult matchAndRewrite(ScatterOp op,
6693 PatternRewriter &rewriter)
const override {
6696 if (!isa<MemRefType>(op.getBase().getType()))
6699 if (
failed(isZeroBasedContiguousSeq(op.getIndices())))
6703 op, op.getBase(), op.getOffsets(), op.getMask(), op.getValueToStore());
6709void ScatterOp::getCanonicalizationPatterns(RewritePatternSet &results,
6710 MLIRContext *context) {
6711 results.
add<ScatterFolder, FoldContiguousScatter>(context);
6714FailureOr<std::optional<SmallVector<Value>>>
6715ScatterOp::bubbleDownCasts(OpBuilder &builder) {
6724LogicalResult ExpandLoadOp::verify() {
6725 VectorType maskVType = getMaskVectorType();
6726 VectorType passVType = getPassThruVectorType();
6730 if (
failed(verifyLoadStoreMemRefLayout(*
this, resVType, memType)))
6737 return emitOpError(
"memref strides must be non-negative");
6742 if (llvm::size(
getIndices()) != memType.getRank())
6743 return emitOpError(
"requires ") << memType.getRank() <<
" indices";
6744 if (resVType.getShape() != maskVType.getShape())
6745 return emitOpError(
"expected result shape to match mask shape");
6746 if (resVType.getScalableDims() != maskVType.getScalableDims())
6748 "expected result scalable dims to match mask scalable dims");
6749 if (resVType != passVType)
6750 return emitOpError(
"expected pass_thru of same type as result type");
6755class ExpandLoadFolder final :
public OpRewritePattern<ExpandLoadOp> {
6758 LogicalResult matchAndRewrite(ExpandLoadOp expand,
6759 PatternRewriter &rewriter)
const override {
6763 expand, expand.getType(), expand.getBase(), expand.getIndices());
6766 rewriter.
replaceOp(expand, expand.getPassThru());
6771 llvm_unreachable(
"Unexpected 1DMaskFormat on ExpandLoadFolder");
6776void ExpandLoadOp::getCanonicalizationPatterns(RewritePatternSet &results,
6777 MLIRContext *context) {
6778 results.
add<ExpandLoadFolder>(context);
6781FailureOr<std::optional<SmallVector<Value>>>
6782ExpandLoadOp::bubbleDownCasts(OpBuilder &builder) {
6791LogicalResult CompressStoreOp::verify() {
6792 VectorType maskVType = getMaskVectorType();
6796 if (
failed(verifyLoadStoreMemRefLayout(*
this, valueVType, memType)))
6803 return emitOpError(
"memref strides must be non-negative");
6808 if (llvm::size(
getIndices()) != memType.getRank())
6809 return emitOpError(
"requires ") << memType.getRank() <<
" indices";
6810 if (valueVType.getShape() != maskVType.getShape())
6811 return emitOpError(
"expected valueToStore shape to match mask shape");
6812 if (valueVType.getScalableDims() != maskVType.getScalableDims())
6814 "expected valueToStore scalable dims to match mask scalable dims");
6819class CompressStoreFolder final :
public OpRewritePattern<CompressStoreOp> {
6822 LogicalResult matchAndRewrite(CompressStoreOp compress,
6823 PatternRewriter &rewriter)
const override {
6827 compress, compress.getValueToStore(), compress.getBase(),
6828 compress.getIndices());
6836 llvm_unreachable(
"Unexpected 1DMaskFormat on CompressStoreFolder");
6841void CompressStoreOp::getCanonicalizationPatterns(RewritePatternSet &results,
6842 MLIRContext *context) {
6843 results.
add<CompressStoreFolder>(context);
6846FailureOr<std::optional<SmallVector<Value>>>
6847CompressStoreOp::bubbleDownCasts(OpBuilder &builder) {
6856void ShapeCastOp::inferResultRanges(ArrayRef<ConstantIntRanges> argRanges,
6858 setResultRanges(getResult(), argRanges.front());
6861std::optional<SmallVector<int64_t, 4>> ShapeCastOp::getShapeForUnroll() {
6862 return llvm::to_vector<4>(getResultVectorType().
getShape());
6865LogicalResult ShapeCastOp::verify() {
6867 VectorType sourceType = getSourceVectorType();
6868 VectorType resultType = getResultVectorType();
6876 int64_t sourceNElms = sourceType.getNumElements();
6877 int64_t resultNElms = resultType.getNumElements();
6878 if (sourceNElms != resultNElms) {
6879 return emitOpError() <<
"has different number of elements at source ("
6880 << sourceNElms <<
") and result (" << resultNElms
6885 int64_t sourceNScalableDims = sourceType.getNumScalableDims();
6886 int64_t resultNScalableDims = resultType.getNumScalableDims();
6887 if (sourceNScalableDims != resultNScalableDims)
6888 return emitOpError() <<
"has different number of scalable dims at source ("
6889 << sourceNScalableDims <<
") and result ("
6890 << resultNScalableDims <<
")";
6901bool ShapeCastOp::isBroadcastLike() {
6902 auto srcType = getSourceVectorType();
6903 auto resType = getResultVectorType();
6906 std::pair<VectorDim, VectorDim> mismatchingDims;
6908 BroadcastableToResult::Success)
6915 size_t rankDiff = resType.getRank() - srcType.getRank();
6920 if (!llvm::all_of(resType.getShape().take_front(rankDiff),
6921 [](int64_t dim) { return dim == 1; }))
6925 return resType.getShape().take_back(srcType.getRank()) == srcType.getShape();
6932static bool isOrderPreserving(TransposeOp transpose) {
6933 ArrayRef<int64_t> permutation = transpose.getPermutation();
6934 VectorType sourceType = transpose.getSourceVectorType();
6935 ArrayRef<int64_t> inShape = sourceType.getShape();
6936 ArrayRef<bool> inDimIsScalable = sourceType.getScalableDims();
6937 auto isNonScalableUnitDim = [&](int64_t dim) {
6938 return inShape[dim] == 1 && !inDimIsScalable[dim];
6940 int64_t current = 0;
6941 for (
auto p : permutation) {
6942 if (!isNonScalableUnitDim(p)) {
6952OpFoldResult ShapeCastOp::fold(FoldAdaptor adaptor) {
6954 VectorType resultType =
getType();
6957 if (getSource().
getType() == resultType)
6961 if (
auto precedingShapeCast = getSource().getDefiningOp<ShapeCastOp>()) {
6962 setOperand(precedingShapeCast.getSource());
6967 if (
auto transpose = getSource().getDefiningOp<TransposeOp>()) {
6968 if (isOrderPreserving(transpose)) {
6969 setOperand(transpose.getVector());
6977 if (
auto bcastOp = getSource().getDefiningOp<BroadcastOp>()) {
6978 if (bcastOp.getSourceType() == resultType)
6979 return bcastOp.getSource();
6983 if (
auto denseAttr =
6984 dyn_cast_if_present<DenseElementsAttr>(adaptor.getSource()))
6985 return denseAttr.reshape(
getType());
7001static VectorType trimTrailingUnitDims(VectorType oldType) {
7002 ArrayRef<int64_t> oldShape = oldType.getShape();
7003 ArrayRef<int64_t> newShape = oldShape;
7005 ArrayRef<bool> oldScalableDims = oldType.getScalableDims();
7006 ArrayRef<bool> newScalableDims = oldScalableDims;
7008 while (!newShape.empty() && newShape.back() == 1 && !newScalableDims.back()) {
7009 newShape = newShape.drop_back(1);
7010 newScalableDims = newScalableDims.drop_back(1);
7015 if (newShape.empty()) {
7016 newShape = oldShape.take_back();
7017 newScalableDims = oldScalableDims.take_back();
7020 return VectorType::get(newShape, oldType.getElementType(), newScalableDims);
7029 ArrayRef<int64_t> oldShape = oldType.getShape();
7030 ArrayRef<int64_t> newShape = oldShape;
7032 ArrayRef<bool> oldScalableDims = oldType.getScalableDims();
7033 ArrayRef<bool> newScalableDims = oldScalableDims;
7035 while (!newShape.empty() && newShape.front() == 1 &&
7036 !newScalableDims.front()) {
7037 newShape = newShape.drop_front(1);
7038 newScalableDims = newScalableDims.drop_front(1);
7043 if (newShape.empty()) {
7044 newShape = oldShape.take_back();
7045 newScalableDims = oldScalableDims.take_back();
7048 return VectorType::get(newShape, oldType.getElementType(), newScalableDims);
7051enum class UnitDimSide { Leading, Trailing };
7074template <UnitDimS
ide S
ide>
7075class ShapeCastCreateMaskFolderBoundaryUnitDim final
7076 :
public OpRewritePattern<ShapeCastOp> {
7080 LogicalResult matchAndRewrite(ShapeCastOp shapeOp,
7081 PatternRewriter &rewriter)
const override {
7082 Value shapeOpSrc = shapeOp->getOperand(0);
7083 auto createMaskOp = shapeOpSrc.
getDefiningOp<vector::CreateMaskOp>();
7084 auto constantMaskOp = shapeOpSrc.
getDefiningOp<vector::ConstantMaskOp>();
7085 if (!createMaskOp && !constantMaskOp)
7088 VectorType shapeOpResTy = shapeOp.getResultVectorType();
7089 VectorType shapeOpSrcTy = shapeOp.getSourceVectorType();
7091 VectorType newVecType = (Side == UnitDimSide::Trailing)
7092 ? trimTrailingUnitDims(shapeOpSrcTy)
7095 if (newVecType != shapeOpResTy) {
7096 return (Side == UnitDimSide::Trailing)
7098 shapeOp,
"Non-trailing-unit-dim dropping shape_cast Op")
7100 shapeOp,
"Non-leading-unit-dim dropping shape_cast Op");
7103 auto numDimsToDrop = shapeOpSrcTy.getRank() - shapeOpResTy.getRank();
7106 if (!numDimsToDrop) {
7107 return (Side == UnitDimSide::Trailing)
7109 shapeOp,
"Non-trailing-unit-dim dropping shape_cast Op")
7111 shapeOp,
"Non-leading-unit-dim dropping shape_cast Op");
7115 auto maskOperands = createMaskOp.getOperands();
7117 auto maskOperandsToDrop = (Side == UnitDimSide::Trailing)
7118 ? maskOperands.take_back(numDimsToDrop)
7119 : maskOperands.take_front(numDimsToDrop);
7123 if (llvm::any_of(maskOperandsToDrop, [](Value maskDim) {
7125 return !cst || (cst.value() != 1);
7129 auto newMaskOperands = (Side == UnitDimSide::Trailing)
7130 ? maskOperands.drop_back(numDimsToDrop)
7131 : maskOperands.drop_front(numDimsToDrop);
7137 if (constantMaskOp) {
7138 auto maskDimSizes = constantMaskOp.getMaskDimSizes();
7140 ArrayRef<int64_t> maskDimSizesToDrop =
7141 (Side == UnitDimSide::Trailing)
7142 ? maskDimSizes.take_back(numDimsToDrop)
7143 : maskDimSizes.take_front(numDimsToDrop);
7147 if (llvm::any_of(maskDimSizesToDrop,
7148 [](int64_t dim) {
return dim != 1; }))
7151 ArrayRef<int64_t> newMaskDimSizes =
7152 (Side == UnitDimSide::Trailing)
7153 ? maskDimSizes.drop_back(numDimsToDrop)
7154 : maskDimSizes.drop_front(numDimsToDrop);
7167int64_t getBroadcastStretchingFactor(ArrayRef<int64_t> srcShape,
7168 ArrayRef<int64_t> dstShape) {
7169 int stretchingFactor = 1;
7170 int numLeadingDims = dstShape.size() - srcShape.size();
7171 for (
int i = 0, e = srcShape.size(); i < e; i++) {
7172 int64_t dstDim = dstShape[numLeadingDims + i];
7173 if (srcShape[i] == 1 && dstDim != 1) {
7174 stretchingFactor *= dstDim;
7177 return stretchingFactor;
7181class ShapeCastBroadcastFolder final :
public OpRewritePattern<ShapeCastOp> {
7185 LogicalResult matchAndRewrite(ShapeCastOp shapeCastOp,
7186 PatternRewriter &rewriter)
const override {
7188 shapeCastOp.getSource().getDefiningOp<vector::BroadcastOp>();
7192 auto srcVectorType = dyn_cast<VectorType>(broadcastOp.getSourceType());
7193 bool srcIsScalar = !srcVectorType;
7201 VectorType dstVectorType = shapeCastOp.getResultVectorType();
7202 ArrayRef<int64_t> dstShape = dstVectorType.getShape();
7203 ArrayRef<int64_t> srcShape =
7204 srcIsScalar ? ArrayRef<int64_t>{} : srcVectorType.getShape();
7205 ArrayRef<int64_t> broadcastShape =
7206 broadcastOp.getResultVectorType().getShape();
7210 BroadcastableToResult::Success) {
7218 if (srcVectorType.getNumElements() != 1) {
7219 if (getBroadcastStretchingFactor(srcShape, dstShape) !=
7220 getBroadcastStretchingFactor(srcShape, broadcastShape)) {
7227 broadcastOp.getSource());
7246class FoldShapeCastOfFromElements final :
public OpRewritePattern<ShapeCastOp> {
7250 LogicalResult matchAndRewrite(ShapeCastOp shapeCastOp,
7251 PatternRewriter &rewriter)
const override {
7252 auto fromElements = shapeCastOp.getSource().getDefiningOp<FromElementsOp>();
7257 shapeCastOp, shapeCastOp.getResultVectorType(),
7258 fromElements.getElements());
7265void ShapeCastOp::getCanonicalizationPatterns(RewritePatternSet &results,
7266 MLIRContext *context) {
7267 results.
add<ShapeCastCreateMaskFolderBoundaryUnitDim<UnitDimSide::Leading>,
7268 ShapeCastCreateMaskFolderBoundaryUnitDim<UnitDimSide::Trailing>,
7269 ShapeCastBroadcastFolder, FoldShapeCastOfFromElements>(context);
7276LogicalResult BitCastOp::verify() {
7277 auto sourceVectorType = getSourceVectorType();
7278 auto resultVectorType = getResultVectorType();
7280 for (int64_t i = 0, e = sourceVectorType.getRank() - 1; i < e; i++) {
7281 if (sourceVectorType.getDimSize(i) != resultVectorType.getDimSize(i))
7282 return emitOpError(
"dimension size mismatch at: ") << i;
7285 DataLayout dataLayout = DataLayout::closest(*
this);
7286 auto sourceElementBits =
7288 auto resultElementBits =
7291 if (sourceVectorType.getRank() == 0) {
7292 if (sourceElementBits != resultElementBits)
7293 return emitOpError(
"source/result bitwidth of the 0-D vector element "
7294 "types must be equal");
7295 }
else if (sourceElementBits * sourceVectorType.getShape().back() !=
7296 resultElementBits * resultVectorType.getShape().back()) {
7298 "source/result bitwidth of the minor 1-D vectors must be equal");
7304OpFoldResult BitCastOp::fold(FoldAdaptor adaptor) {
7310 if (
auto otherOp = getSource().getDefiningOp<BitCastOp>()) {
7311 if (getResult().
getType() == otherOp.getSource().getType())
7312 return otherOp.getSource();
7314 setOperand(otherOp.getSource());
7318 Attribute sourceConstant = adaptor.getSource();
7319 if (!sourceConstant)
7322 Type srcElemType = getSourceVectorType().getElementType();
7323 Type dstElemType = getResultVectorType().getElementType();
7325 if (
auto floatPack = llvm::dyn_cast<DenseFPElementsAttr>(sourceConstant)) {
7326 if (floatPack.isSplat()) {
7327 auto splat = floatPack.getSplatValue<FloatAttr>();
7330 if (srcElemType.
isF16() && dstElemType.
isF32()) {
7331 uint32_t bits =
static_cast<uint32_t
>(
7332 splat.getValue().bitcastToAPInt().getZExtValue());
7334 bits = (bits << 16) | (bits & 0xffff);
7335 APInt intBits(32, bits);
7336 APFloat floatBits(llvm::APFloat::IEEEsingle(), intBits);
7342 if (
auto intPack = llvm::dyn_cast<DenseIntElementsAttr>(sourceConstant)) {
7343 if (intPack.isSplat()) {
7344 auto splat = intPack.getSplatValue<IntegerAttr>();
7346 if (llvm::isa<IntegerType>(dstElemType) && srcElemType.
isIntOrFloat()) {
7351 if (dstBitWidth > srcBitWidth && dstBitWidth % srcBitWidth == 0) {
7352 APInt intBits = splat.getValue().zext(dstBitWidth);
7355 for (uint64_t i = 0; i < dstBitWidth / srcBitWidth - 1; i++)
7356 intBits = (intBits << srcBitWidth) | intBits;
7366std::optional<SmallVector<int64_t, 4>> BitCastOp::getShapeForUnroll() {
7367 return llvm::to_vector<4>(getResultVectorType().
getShape());
7374static SmallVector<int64_t, 8> extractShape(MemRefType memRefType) {
7375 auto vectorType = llvm::dyn_cast<VectorType>(memRefType.getElementType());
7376 SmallVector<int64_t, 8> res(memRefType.getShape());
7378 res.append(vectorType.getShape().begin(), vectorType.getShape().end());
7384void TypeCastOp::build(OpBuilder &builder, OperationState &
result,
7386 result.addOperands(source);
7387 MemRefType memRefType = llvm::cast<MemRefType>(source.
getType());
7388 VectorType vectorType =
7389 VectorType::get(extractShape(memRefType),
7391 result.addTypes(MemRefType::get({}, vectorType, MemRefLayoutAttrInterface(),
7392 memRefType.getMemorySpace()));
7395LogicalResult TypeCastOp::verify() {
7396 MemRefType canonicalType =
getMemRefType().canonicalizeStridedLayout();
7397 if (!canonicalType.getLayout().isIdentity())
7398 return emitOpError(
"expects operand to be a memref with identity layout");
7399 if (!getResultMemRefType().getLayout().isIdentity())
7400 return emitOpError(
"expects result to be a memref with identity layout");
7401 if (getResultMemRefType().getMemorySpace() !=
7403 return emitOpError(
"expects result in same memory space");
7406 auto resultType = getResultMemRefType();
7410 "expects result and operand with same underlying scalar type: ")
7412 if (extractShape(sourceType) != extractShape(resultType))
7414 "expects concatenated result and operand shapes to be equal: ")
7423void vector::TransposeOp::build(OpBuilder &builder, OperationState &
result,
7424 Value vector, ArrayRef<int64_t> permutation) {
7425 VectorType vt = llvm::cast<VectorType>(vector.
getType());
7426 SmallVector<int64_t, 4> transposedShape(vt.getRank());
7427 SmallVector<bool, 4> transposedScalableDims(vt.getRank());
7428 for (
unsigned i = 0; i < permutation.size(); ++i) {
7429 transposedShape[i] = vt.getShape()[permutation[i]];
7430 transposedScalableDims[i] = vt.getScalableDims()[permutation[i]];
7433 result.addOperands(vector);
7434 result.addTypes(VectorType::get(transposedShape, vt.getElementType(),
7435 transposedScalableDims));
7436 result.addAttribute(TransposeOp::getPermutationAttrName(
result.name),
7440OpFoldResult vector::TransposeOp::fold(FoldAdaptor adaptor) {
7443 llvm::dyn_cast_if_present<SplatElementsAttr>(adaptor.getVector()))
7444 return splat.reshape(getResultVectorType());
7461 if (getSourceVectorType() == getResultVectorType() &&
7462 isOrderPreserving(*
this))
7468LogicalResult vector::TransposeOp::verify() {
7469 VectorType vectorType = getSourceVectorType();
7470 VectorType resultType = getResultVectorType();
7471 int64_t rank = resultType.getRank();
7472 if (vectorType.getRank() != rank)
7473 return emitOpError(
"vector result rank mismatch: ") << rank;
7475 ArrayRef<int64_t> perm = getPermutation();
7476 int64_t size = perm.size();
7478 return emitOpError(
"transposition length mismatch: ") << size;
7479 SmallVector<bool, 8> seen(rank,
false);
7480 for (
const auto &ta : llvm::enumerate(perm)) {
7481 if (ta.value() < 0 || ta.value() >= rank)
7482 return emitOpError(
"transposition index out of range: ") << ta.value();
7483 if (seen[ta.value()])
7484 return emitOpError(
"duplicate position index: ") << ta.value();
7485 seen[ta.value()] =
true;
7486 if (resultType.getDimSize(ta.index()) != vectorType.getDimSize(ta.value()))
7487 return emitOpError(
"dimension size mismatch at: ") << ta.value();
7492std::optional<SmallVector<int64_t, 4>> TransposeOp::getShapeForUnroll() {
7493 return llvm::to_vector<4>(getResultVectorType().
getShape());
7496void TransposeOp::inferResultRanges(ArrayRef<ConstantIntRanges> argRanges,
7498 setResultRanges(getResult(), argRanges.front());
7504class TransposeFolder final :
public OpRewritePattern<vector::TransposeOp> {
7508 LogicalResult matchAndRewrite(vector::TransposeOp transposeOp,
7509 PatternRewriter &rewriter)
const override {
7511 auto composePermutations = [](ArrayRef<int64_t> permutation1,
7512 ArrayRef<int64_t> permutation2) {
7513 SmallVector<int64_t, 4>
result;
7514 for (
auto index : permutation2)
7515 result.push_back(permutation1[index]);
7520 vector::TransposeOp parentTransposeOp =
7521 transposeOp.getVector().getDefiningOp<vector::TransposeOp>();
7522 if (!parentTransposeOp)
7525 SmallVector<int64_t, 4> permutation = composePermutations(
7526 parentTransposeOp.getPermutation(), transposeOp.getPermutation());
7529 transposeOp, transposeOp.getResult().
getType(),
7530 parentTransposeOp.getVector(), permutation);
7536class FoldTransposeSplat final :
public OpRewritePattern<TransposeOp> {
7540 LogicalResult matchAndRewrite(TransposeOp transposeOp,
7541 PatternRewriter &rewriter)
const override {
7542 Value splat = getScalarSplatSource(transposeOp.getVector());
7547 transposeOp, transposeOp.getResultVectorType(), splat);
7553class FoldTransposeCreateMask final :
public OpRewritePattern<TransposeOp> {
7557 LogicalResult matchAndRewrite(TransposeOp transpOp,
7558 PatternRewriter &rewriter)
const override {
7559 Value transposeSrc = transpOp.getVector();
7560 auto createMaskOp = transposeSrc.
getDefiningOp<vector::CreateMaskOp>();
7561 auto constantMaskOp = transposeSrc.
getDefiningOp<vector::ConstantMaskOp>();
7562 if (!createMaskOp && !constantMaskOp)
7567 ArrayRef<int64_t> permutation = transpOp.getPermutation();
7570 auto maskOperands = createMaskOp.getOperands();
7571 SmallVector<Value> newOperands(maskOperands.begin(), maskOperands.end());
7575 transpOp, transpOp.getResultVectorType(), newOperands);
7580 auto maskDimSizes = constantMaskOp.getMaskDimSizes();
7584 transpOp, transpOp.getResultVectorType(), newMaskDimSizes);
7590class FoldTransposeShapeCast final :
public OpRewritePattern<TransposeOp> {
7594 LogicalResult matchAndRewrite(TransposeOp transposeOp,
7595 PatternRewriter &rewriter)
const override {
7597 transposeOp.getVector().getDefiningOp<vector::ShapeCastOp>();
7600 if (!isOrderPreserving(transposeOp))
7603 VectorType resultType = transposeOp.getType();
7610 shapeCastOp.getSource());
7629class FoldTransposeFromElements final :
public OpRewritePattern<TransposeOp> {
7632 LogicalResult matchAndRewrite(vector::TransposeOp transposeOp,
7633 PatternRewriter &rewriter)
const override {
7634 auto fromElementsOp =
7635 transposeOp.getVector().getDefiningOp<vector::FromElementsOp>();
7636 if (!fromElementsOp)
7639 VectorType srcTy = fromElementsOp.getDest().getType();
7640 VectorType dstTy = transposeOp.getType();
7642 ArrayRef<int64_t> permutation = transposeOp.getPermutation();
7643 int64_t rank = srcTy.getRank();
7646 SmallVector<int64_t> inversePerm(rank, 0);
7647 for (int64_t i = 0; i < rank; ++i)
7648 inversePerm[permutation[i]] = i;
7650 ArrayRef<int64_t> srcShape = srcTy.getShape();
7651 ArrayRef<int64_t> dstShape = dstTy.getShape();
7652 SmallVector<int64_t> srcIdx(rank, 0);
7653 SmallVector<int64_t> dstIdx(rank, 0);
7657 auto elementsOld = fromElementsOp.getElements();
7658 SmallVector<Value> elementsNew;
7659 int64_t dstNumElements = dstTy.getNumElements();
7660 elementsNew.reserve(dstNumElements);
7664 for (int64_t linearIdx = 0; linearIdx < dstNumElements; ++linearIdx) {
7668 for (int64_t j = 0; j < rank; ++j)
7669 srcIdx[j] = dstIdx[inversePerm[j]];
7671 int64_t srcLin =
linearize(srcIdx, srcStrides);
7673 elementsNew.push_back(elementsOld[srcLin]);
7707class FoldTransposeBroadcast :
public OpRewritePattern<vector::TransposeOp> {
7710 FoldTransposeBroadcast(MLIRContext *context, PatternBenefit benefit = 1)
7711 : OpRewritePattern<vector::TransposeOp>(context, benefit) {}
7713 LogicalResult matchAndRewrite(vector::TransposeOp transpose,
7714 PatternRewriter &rewriter)
const override {
7720 "not preceded by a broadcast");
7723 auto inputType = dyn_cast<VectorType>(
broadcast.getSourceType());
7724 VectorType outputType = transpose.getResultVectorType();
7727 bool inputIsScalar = !inputType;
7728 if (inputIsScalar) {
7734 ArrayRef<int64_t> permutation = transpose.getPermutation();
7735 ArrayRef<int64_t> inputShape = inputType.getShape();
7736 int64_t inputRank = inputType.getRank();
7737 int64_t outputRank = transpose.getType().getRank();
7738 int64_t deltaRank = outputRank - inputRank;
7741 for (
int inputIndex = 0; inputIndex < inputRank; ++inputIndex) {
7742 bool notOne = inputShape[inputIndex] != 1;
7743 bool prevNotOne = (inputIndex != 0 && inputShape[inputIndex - 1] != 1);
7744 bool groupEndFound = notOne || prevNotOne;
7745 if (groupEndFound) {
7746 int high = inputIndex + deltaRank;
7750 for (
int i = low; i < high; ++i) {
7751 if (permutation[i] < low || permutation[i] >= high) {
7753 transpose,
"permutation not local to group");
7767 vector::BroadcastableToResult::Success &&
7768 "not broadcastable directly to transpose output");
7779void vector::TransposeOp::getCanonicalizationPatterns(
7780 RewritePatternSet &results, MLIRContext *context) {
7781 results.
add<FoldTransposeCreateMask, FoldTransposeShapeCast, TransposeFolder,
7782 FoldTransposeSplat, FoldTransposeFromElements,
7783 FoldTransposeBroadcast>(context);
7790void ConstantMaskOp::build(OpBuilder &builder, OperationState &
result,
7792 assert(kind == ConstantMaskKind::AllTrue ||
7793 kind == ConstantMaskKind::AllFalse);
7794 build(builder,
result, type,
7795 kind == ConstantMaskKind::AllTrue
7797 : SmallVector<int64_t>(type.getRank(), 0));
7800LogicalResult ConstantMaskOp::verify() {
7801 auto resultType = llvm::cast<VectorType>(getResult().
getType());
7803 if (resultType.getRank() == 0) {
7804 if (getMaskDimSizes().size() != 1)
7805 return emitError(
"array attr must have length 1 for 0-D vectors");
7806 auto dim = getMaskDimSizes()[0];
7807 if (dim != 0 && dim != 1)
7808 return emitError(
"mask dim size must be either 0 or 1 for 0-D vectors");
7813 if (
static_cast<int64_t
>(getMaskDimSizes().size()) != resultType.getRank())
7815 "must specify array attr of size equal vector result rank");
7818 auto resultShape = resultType.getShape();
7819 auto resultScalableDims = resultType.getScalableDims();
7820 ArrayRef<int64_t> maskDimSizes = getMaskDimSizes();
7821 for (
const auto [index, maskDimSize] : llvm::enumerate(maskDimSizes)) {
7822 if (maskDimSize < 0 || maskDimSize > resultShape[index])
7824 "array attr of size out of bounds of vector result dimension size");
7825 if (resultScalableDims[index] && maskDimSize != 0 &&
7826 maskDimSize != resultShape[index])
7828 "only supports 'none set' or 'all set' scalable dimensions");
7832 bool anyZeros = llvm::is_contained(maskDimSizes, 0);
7833 bool allZeros = llvm::all_of(maskDimSizes, [](int64_t s) {
return s == 0; });
7834 if (anyZeros && !allZeros)
7835 return emitOpError(
"expected all mask dim sizes to be zeros, "
7836 "as a result of conjunction with zero mask dim");
7840bool ConstantMaskOp::isAllOnesMask() {
7843 if (resultType.getRank() == 0) {
7844 assert(getMaskDimSizes().size() == 1 &&
"invalid sizes for zero rank mask");
7845 return getMaskDimSizes()[0] == 1;
7847 for (
const auto [resultSize, maskDimSize] :
7848 llvm::zip_equal(resultType.getShape(), getMaskDimSizes())) {
7849 if (maskDimSize < resultSize)
7855OpFoldResult ConstantMaskOp::fold(FoldAdaptor adaptor) {
7856 ArrayRef<int64_t> bounds = getMaskDimSizes();
7859 auto createBoolSplat = [&](
bool x) {
7865 if (vectorSizes.empty()) {
7866 assert(bounds.size() == 1 &&
"invalid sizes for zero rank mask");
7867 return createBoolSplat(bounds[0] == 1);
7870 if (bounds == vectorSizes)
7871 return createBoolSplat(
true);
7872 if (llvm::all_of(bounds, [](int64_t x) {
return x == 0; }))
7873 return createBoolSplat(
false);
7874 return OpFoldResult();
7881void CreateMaskOp::build(OpBuilder &builder, OperationState &
result,
7883 ArrayRef<OpFoldResult> mixedOperands) {
7884 SmallVector<Value> operands =
7886 build(builder,
result, type, operands);
7889LogicalResult CreateMaskOp::verify() {
7890 auto vectorType = llvm::cast<VectorType>(getResult().
getType());
7892 if (vectorType.getRank() == 0) {
7895 "must specify exactly one operand for 0-D create_mask");
7897 llvm::cast<VectorType>(getResult().
getType()).getRank()) {
7899 "must specify an operand for each result vector dimension");
7929class CreateMaskFolder final :
public OpRewritePattern<CreateMaskOp> {
7933 LogicalResult matchAndRewrite(CreateMaskOp createMaskOp,
7934 PatternRewriter &rewriter)
const override {
7935 VectorType maskType = createMaskOp.getVectorType();
7936 ArrayRef<int64_t> maskTypeDimSizes = maskType.getShape();
7937 ArrayRef<bool> maskTypeDimScalableFlags = maskType.getScalableDims();
7940 constexpr std::array<int64_t, 1> rankZeroShape{1};
7941 constexpr std::array<bool, 1> rankZeroScalableDims{
false};
7942 if (maskType.getRank() == 0) {
7943 maskTypeDimSizes = rankZeroShape;
7944 maskTypeDimScalableFlags = rankZeroScalableDims;
7949 SmallVector<int64_t, 4> constantDims;
7950 for (
auto [i, dimSize] : llvm::enumerate(createMaskOp.getOperands())) {
7955 if (maskTypeDimScalableFlags[i] && intSize >= 0)
7957 constantDims.push_back(*intSize);
7961 if (vscaleMultiplier < maskTypeDimSizes[i])
7963 constantDims.push_back(*vscaleMultiplier);
7970 for (
auto [value, maskDimSize] : llvm::zip(constantDims, maskTypeDimSizes))
7971 value = std::clamp<int64_t>(value, 0, maskDimSize);
7974 if (llvm::is_contained(constantDims, 0))
7975 constantDims.assign(constantDims.size(), 0);
7986void CreateMaskOp::getCanonicalizationPatterns(RewritePatternSet &results,
7987 MLIRContext *context) {
7988 results.
add<CreateMaskFolder>(context);
7996 OpBuilder &builder, OperationState &
result, Value mask,
7997 Operation *maskableOp,
7998 function_ref<
void(OpBuilder &, Operation *)> maskRegionBuilder) {
7999 assert(maskRegionBuilder &&
8000 "builder callback for 'maskRegion' must be present");
8002 result.addOperands(mask);
8003 OpBuilder::InsertionGuard guard(builder);
8004 Region *maskRegion =
result.addRegion();
8006 maskRegionBuilder(builder, maskableOp);
8011 Value mask, Operation *maskableOp,
8012 function_ref<
void(OpBuilder &, Operation *)> maskRegionBuilder) {
8013 build(builder,
result, resultTypes, mask, Value(), maskableOp,
8019 Value mask, Value passthru, Operation *maskableOp,
8020 function_ref<
void(OpBuilder &, Operation *)> maskRegionBuilder) {
8021 build(builder,
result, mask, maskableOp, maskRegionBuilder);
8023 result.addOperands(passthru);
8024 result.addTypes(resultTypes);
8027ParseResult MaskOp::parse(OpAsmParser &parser, OperationState &
result) {
8029 result.regions.reserve(1);
8030 Region &maskRegion = *
result.addRegion();
8035 OpAsmParser::UnresolvedOperand mask;
8040 OpAsmParser::UnresolvedOperand passthru;
8042 if (parsePassthru.succeeded() && parser.
parseOperand(passthru))
8049 MaskOp::ensureTerminator(maskRegion, builder,
result.location);
8060 SmallVector<Type> resultTypes;
8063 result.types.append(resultTypes);
8069 if (parsePassthru.succeeded()) {
8070 if (resultTypes.empty())
8073 "expects a result if passthru operand is provided");
8082void mlir::vector::MaskOp::print(OpAsmPrinter &p) {
8083 p <<
" " << getMask();
8085 p <<
", " << getPassthru();
8089 Block *singleBlock = &getMaskRegion().getBlocks().front();
8095 getOperation()->getDiscardableAttrDictionary().getValue());
8097 p <<
" : " << getMask().getType();
8098 if (getNumResults() > 0)
8099 p <<
" -> " << getResultTypes();
8102void MaskOp::ensureTerminator(Region ®ion, Builder &builder, Location loc) {
8105 OpTrait::SingleBlockImplicitTerminator<vector::YieldOp>::Impl<
8106 MaskOp>::ensureTerminator(region, builder, loc);
8112 if (isa<vector::YieldOp>(block.
back()))
8120 OpTrait::SingleBlockImplicitTerminator<vector::YieldOp>::Impl<
8121 MaskOp>::ensureTerminator(region, builder, loc);
8127 Operation *maskedOp = &block.
front();
8128 opBuilder.setInsertionPointToEnd(&block);
8129 vector::YieldOp::create(opBuilder, loc, maskedOp->
getResults());
8132LogicalResult MaskOp::verify() {
8134 Block &block = getMaskRegion().getBlocks().
front();
8136 return emitOpError(
"expects a terminator within the mask region");
8139 if (numMaskRegionOps > 2)
8140 return emitOpError(
"expects only one operation to mask");
8143 auto terminator = dyn_cast<vector::YieldOp>(block.
back());
8145 return emitOpError(
"expects a terminator within the mask region");
8147 if (terminator->getNumOperands() != getNumResults())
8149 "expects number of results to match mask region yielded values");
8152 if (numMaskRegionOps == 1)
8155 auto maskableOp = dyn_cast<MaskableOpInterface>(block.
front());
8157 return emitOpError(
"expects a MaskableOpInterface within the mask region");
8161 return emitOpError(
"expects number of results to match maskable operation "
8162 "number of results");
8164 if (!llvm::equal(maskableOp->
getResults(), terminator.getOperands()))
8165 return emitOpError(
"expects all the results from the MaskableOpInterface "
8166 "to match all the values returned by the terminator");
8168 if (!llvm::equal(maskableOp->
getResultTypes(), getResultTypes()))
8170 "expects result type to match maskable operation result type");
8173 [](Type t) { return llvm::isa<VectorType>(t); }) > 1)
8174 return emitOpError(
"multiple vector results not supported");
8177 Type expectedMaskType = maskableOp.getExpectedMaskType();
8178 if (getMask().
getType() != expectedMaskType)
8179 return emitOpError(
"expects a ")
8180 << expectedMaskType <<
" mask for the maskable operation";
8183 Value passthru = getPassthru();
8185 if (!maskableOp.supportsPassthru())
8187 "doesn't expect a passthru argument for this maskable operation");
8190 return emitOpError(
"expects result when passthru argument is provided");
8193 return emitOpError(
"expects passthru type to match result type");
8213static LogicalResult foldEmptyMaskOp(MaskOp maskOp, MaskOp::FoldAdaptor adaptor,
8214 SmallVectorImpl<OpFoldResult> &results) {
8215 if (!maskOp.isEmpty() || maskOp.hasPassthru())
8218 Block *block = maskOp.getMaskBlock();
8219 auto terminator = cast<vector::YieldOp>(block->
front());
8220 if (terminator.getNumOperands() == 0)
8224 llvm::append_range(results, terminator.getOperands());
8228LogicalResult MaskOp::fold(FoldAdaptor adaptor,
8229 SmallVectorImpl<OpFoldResult> &results) {
8230 if (succeeded(foldEmptyMaskOp(*
this, adaptor, results)))
8240 Operation *maskableOp = getMaskableOp();
8246 llvm::append_range(results, maskableOp->
getResults());
8262class CanonializeEmptyMaskOp :
public OpRewritePattern<MaskOp> {
8265 LogicalResult matchAndRewrite(MaskOp maskOp,
8266 PatternRewriter &rewriter)
const override {
8267 if (!maskOp.isEmpty())
8270 if (!maskOp.hasPassthru())
8277 VectorType maskType = maskOp.getMask().getType();
8278 for (Type resultType : maskOp.getResultTypes()) {
8279 auto vecResultType = dyn_cast<VectorType>(resultType);
8280 if (!vecResultType || vecResultType.getShape() != maskType.getShape())
8284 Block *block = maskOp.getMaskBlock();
8285 auto terminator = cast<vector::YieldOp>(block->
front());
8286 assert(terminator.getNumOperands() == 1 &&
8287 "expected one result when passthru is provided");
8290 maskOp, maskOp.getResultTypes(), maskOp.getMask(),
8291 terminator.getOperand(0), maskOp.getPassthru());
8297void MaskOp::getCanonicalizationPatterns(RewritePatternSet &results,
8298 MLIRContext *context) {
8299 results.
add<CanonializeEmptyMaskOp>(context);
8305Operation *MaskOp::getMaskableOp() {
8306 Block *block = getMaskBlock();
8310 return &block->
front();
8314bool MaskOp::hasPassthru() {
return getPassthru() != Value(); }
8320LogicalResult ScanOp::verify() {
8321 VectorType srcType = getSourceType();
8322 VectorType initialType = getInitialValueType();
8324 int64_t srcRank = srcType.getRank();
8325 int64_t reductionDim = getReductionDim();
8326 if (reductionDim >= srcRank)
8327 return emitOpError(
"reduction dimension ")
8328 << reductionDim <<
" has to be less than " << srcRank;
8331 int64_t initialValueRank = initialType.getRank();
8332 if (initialValueRank != srcRank - 1)
8333 return emitOpError(
"initial value rank ")
8334 << initialValueRank <<
" has to be equal to " << srcRank - 1;
8337 ArrayRef<int64_t> srcShape = srcType.getShape();
8338 ArrayRef<int64_t> initialValueShapes = initialType.getShape();
8339 SmallVector<int64_t> expectedShape;
8340 for (
int i = 0; i < srcRank; i++) {
8341 if (i != reductionDim)
8342 expectedShape.push_back(srcShape[i]);
8344 if (!llvm::equal(initialValueShapes, expectedShape)) {
8345 return emitOpError(
"incompatible input/initial value shapes");
8349 Type eltType = getDestType().getElementType();
8351 return emitOpError(
"unsupported reduction type ")
8352 << eltType <<
" for kind '" << stringifyCombiningKind(getKind())
8359 RewritePatternSet &patterns, PatternBenefit benefit) {
8361 .
add<CreateMaskFolder, MaskedLoadFolder, MaskedStoreFolder, GatherFolder,
8362 ScatterFolder, ExpandLoadFolder, CompressStoreFolder,
8363 StridedSliceConstantMaskFolder, TransposeFolder>(
8368 CombiningKind kind, Value v1, Value acc,
8369 arith::FastMathFlagsAttr fastmath,
8376 case CombiningKind::ADD:
8378 result =
b.createOrFold<arith::AddIOp>(loc, v1, acc);
8379 else if (llvm::isa<FloatType>(t1) && llvm::isa<FloatType>(tAcc))
8380 result =
b.createOrFold<arith::AddFOp>(loc, v1, acc, fastmath);
8382 llvm_unreachable(
"invalid value types for ADD reduction");
8384 case CombiningKind::AND:
8386 result =
b.createOrFold<arith::AndIOp>(loc, v1, acc);
8388 case CombiningKind::MAXNUMF:
8389 assert(llvm::isa<FloatType>(t1) && llvm::isa<FloatType>(tAcc) &&
8390 "expected float values");
8391 result =
b.createOrFold<arith::MaxNumFOp>(loc, v1, acc, fastmath);
8393 case CombiningKind::MAXIMUMNUMF:
8394 assert(llvm::isa<FloatType>(t1) && llvm::isa<FloatType>(tAcc) &&
8395 "expected float values");
8396 result =
b.createOrFold<arith::MaximumNumFOp>(loc, v1, acc, fastmath);
8398 case CombiningKind::MAXIMUMF:
8399 assert(llvm::isa<FloatType>(t1) && llvm::isa<FloatType>(tAcc) &&
8400 "expected float values");
8401 result =
b.createOrFold<arith::MaximumFOp>(loc, v1, acc, fastmath);
8403 case CombiningKind::MINNUMF:
8404 assert(llvm::isa<FloatType>(t1) && llvm::isa<FloatType>(tAcc) &&
8405 "expected float values");
8406 result =
b.createOrFold<arith::MinNumFOp>(loc, v1, acc, fastmath);
8408 case CombiningKind::MINIMUMNUMF:
8409 assert(llvm::isa<FloatType>(t1) && llvm::isa<FloatType>(tAcc) &&
8410 "expected float values");
8411 result =
b.createOrFold<arith::MinimumNumFOp>(loc, v1, acc, fastmath);
8413 case CombiningKind::MINIMUMF:
8414 assert(llvm::isa<FloatType>(t1) && llvm::isa<FloatType>(tAcc) &&
8415 "expected float values");
8416 result =
b.createOrFold<arith::MinimumFOp>(loc, v1, acc, fastmath);
8418 case CombiningKind::MAXSI:
8420 result =
b.createOrFold<arith::MaxSIOp>(loc, v1, acc);
8422 case CombiningKind::MINSI:
8424 result =
b.createOrFold<arith::MinSIOp>(loc, v1, acc);
8426 case CombiningKind::MAXUI:
8428 result =
b.createOrFold<arith::MaxUIOp>(loc, v1, acc);
8430 case CombiningKind::MINUI:
8432 result =
b.createOrFold<arith::MinUIOp>(loc, v1, acc);
8434 case CombiningKind::MUL:
8436 result =
b.createOrFold<arith::MulIOp>(loc, v1, acc);
8437 else if (llvm::isa<FloatType>(t1) && llvm::isa<FloatType>(tAcc))
8438 result =
b.createOrFold<arith::MulFOp>(loc, v1, acc, fastmath);
8440 llvm_unreachable(
"invalid value types for MUL reduction");
8442 case CombiningKind::OR:
8444 result =
b.createOrFold<arith::OrIOp>(loc, v1, acc);
8446 case CombiningKind::XOR:
8448 result =
b.createOrFold<arith::XOrIOp>(loc, v1, acc);
8452 assert(
result &&
"unknown CombiningKind");
8460void StepOp::inferResultRanges(ArrayRef<ConstantIntRanges> argRanges,
8462 auto resultType = cast<VectorType>(
getType());
8463 if (resultType.isScalable()) {
8469 uint64_t maxIndex = resultType.getDimSize(0) - 1;
8470 APInt umin = APInt::getZero(bitwidth);
8471 APInt umax = APInt::getMaxValue(bitwidth).ugt(maxIndex)
8472 ? APInt(bitwidth, maxIndex)
8473 : APInt::getMaxValue(bitwidth);
8504struct StepCompareFolder :
public OpRewritePattern<StepOp> {
8507 LogicalResult matchAndRewrite(StepOp stepOp,
8508 PatternRewriter &rewriter)
const override {
8509 const int64_t stepSize = stepOp.getResult().getType().getNumElements();
8511 for (OpOperand &use : stepOp.getResult().getUses()) {
8512 auto cmpiOp = dyn_cast<arith::CmpIOp>(use.getOwner());
8517 const unsigned stepOperandNumber = use.getOperandNumber();
8518 if (stepOperandNumber != 0)
8522 unsigned constOperandNumber = 1;
8523 Value otherOperand = cmpiOp.getOperand(constOperandNumber);
8524 std::optional<int64_t> maybeConstValue =
8526 if (!maybeConstValue.has_value())
8529 int64_t constValue = maybeConstValue.value();
8530 arith::CmpIPredicate pred = cmpiOp.getPredicate();
8532 auto maybeSplat = [&]() -> std::optional<bool> {
8534 if ((pred == arith::CmpIPredicate::ult ||
8535 pred == arith::CmpIPredicate::uge) &&
8536 stepSize <= constValue)
8537 return pred == arith::CmpIPredicate::ult;
8540 if ((pred == arith::CmpIPredicate::ule ||
8541 pred == arith::CmpIPredicate::ugt) &&
8542 stepSize - 1 <= constValue) {
8543 return pred == arith::CmpIPredicate::ule;
8547 if ((pred == arith::CmpIPredicate::eq ||
8548 pred == arith::CmpIPredicate::ne) &&
8549 stepSize <= constValue)
8550 return pred == arith::CmpIPredicate::ne;
8552 return std::nullopt;
8555 if (!maybeSplat.has_value())
8560 auto type = dyn_cast<VectorType>(cmpiOp.getResult().getType());
8565 Value splat = mlir::arith::ConstantOp::create(rewriter, cmpiOp.getLoc(),
8577void StepOp::getCanonicalizationPatterns(RewritePatternSet &results,
8578 MLIRContext *context) {
8579 results.
add<StepCompareFolder>(context);
8589 Operation *maskableOp) {
8590 assert(maskableOp->
getBlock() &&
"MaskableOp must be inserted into a block");
8602 Operation *maskableOp, Value mask,
8607 return MaskOp::create(builder, maskableOp->
getLoc(),
8610 return MaskOp::create(builder, maskableOp->
getLoc(),
8623 Value newValue, Value passthru) {
8627 return arith::SelectOp::create(builder, newValue.
getLoc(), newValue.
getType(),
8628 mask, newValue, passthru);
8639struct InterleaveDeinterleaveFolder :
public OpRewritePattern<InterleaveOp> {
8642 LogicalResult matchAndRewrite(InterleaveOp interleaveOp,
8643 PatternRewriter &rewriter)
const override {
8644 auto lhsDefOp = interleaveOp.getLhs().getDefiningOp<DeinterleaveOp>();
8645 auto rhsDefOp = interleaveOp.getRhs().getDefiningOp<DeinterleaveOp>();
8646 if (!lhsDefOp || !rhsDefOp || lhsDefOp != rhsDefOp)
8648 for (
auto [idx, operand] : llvm::enumerate(interleaveOp.getOperands())) {
8649 if (cast<OpResult>(operand).getResultNumber() != idx)
8652 rewriter.
replaceOp(interleaveOp, lhsDefOp.getSource());
8658void InterleaveOp::getCanonicalizationPatterns(RewritePatternSet &results,
8659 MLIRContext *context) {
8660 results.
add<InterleaveDeinterleaveFolder>(context);
8663OpFoldResult InterleaveOp::fold(FoldAdaptor adaptor) {
8665 auto splat = dyn_cast_if_present<SplatElementsAttr>(adaptor.getLhs());
8666 if (!splat || adaptor.getLhs() != adaptor.getRhs())
8668 return SplatElementsAttr::get(getResultVectorType(),
8669 splat.getSplatValue<Attribute>());
8672std::optional<SmallVector<int64_t, 4>> InterleaveOp::getShapeForUnroll() {
8673 return llvm::to_vector<4>(getResultVectorType().
getShape());
8680std::optional<SmallVector<int64_t, 4>> DeinterleaveOp::getShapeForUnroll() {
8681 return llvm::to_vector<4>(getResultVectorType().
getShape());
8688#define GET_ATTRDEF_CLASSES
8689#include "mlir/Dialect/Vector/IR/VectorAttributes.cpp.inc"
8691#define GET_OP_CLASSES
8692#include "mlir/Dialect/Vector/IR/VectorOps.cpp.inc"
if(failed(verifyVectorMemoryOp(getOperation(), memrefType, getVectorType()))) return failure()
getNumOperands() - 1))) return failure()
Given a list of lists of parsed operands, populates uniqueOperands with unique operands.
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 void copy(Location loc, Value dst, Value src, Value size, OpBuilder &builder)
Copies the given number of bytes from src to dst pointers.
static Value getBase(Value v)
Looks through known "view-like" ops to find the base memref.
static bool isLegalToInline(InlinerInterface &interface, Region *src, Region *insertRegion, bool shouldCloneInlinedRegion, IRMapping &valueMapping)
Utility to check that all of the operations within 'src' can be inlined.
static SmallVector< unsigned > extractPosition(ArrayRef< int64_t > indices)
Convert the value of a DenseI64ArrayAttr to a vector of unsigned indices.
*if copies could not be generated due to yet unimplemented cases *copyInPlacementStart and copyOutPlacementStart in copyPlacementBlock *specify the insertion points where the incoming copies and outgoing should be inserted(the insertion happens right before the *insertion point). Since `begin` can itself be invalidated due to the memref *rewriting done from this method
static ParseResult parseBoolAttr(OpAsmParser &parser, BoolAttr &result)
static void printBoolAttr(OpAsmPrinter &printer, Operation *, BoolAttr attr)
static std::optional< VectorShape > vectorShape(Type type)
static Value max(ImplicitLocOpBuilder &builder, Value value, Value bound)
static Value min(ImplicitLocOpBuilder &builder, Value value, Value bound)
static void contract(RootOrderingGraph &graph, ArrayRef< Value > cycle, const DenseMap< Value, unsigned > &parentDepths, DenseMap< Value, Value > &actualSource, DenseMap< Value, Value > &actualTarget)
Contracts the specified cycle in the given graph in-place.
static Type getElementType(Type type, ArrayRef< int32_t > indices, function_ref< InFlightDiagnostic(StringRef)> emitErrorFn)
Walks the given type hierarchy with the given indices, potentially down to component granularity,...
static Value broadcast(Location loc, Value toBroadcast, unsigned numElements, const TypeConverter &typeConverter, ConversionPatternRewriter &rewriter)
Broadcasts the value to vector with numElements number of elements.
static VectorType getVectorType(Type scalarTy, const VectorizationStrategy *strategy)
Returns the vector type resulting from applying the provided vectorization strategy on the scalar typ...
static ArrayRef< int64_t > getShape(Type type)
Returns the shape of the given type.
static SmallVector< Value > slice(OpBuilder &b, Location loc, Value value, VectorType nativeType)
static VectorType trimLeadingUnitDims(VectorType oldType, bool trimOnlyOneDim=false, bool allowRank0=false)
static MaskFormat getMaskFormat(Value mask)
Helper method to classify a mask value.
static OpFoldResult foldShuffleIdentityMask(ShuffleOp op)
Fold shuffle V1, V2, [0, 1, 2, 3] : <4xi32>, <2xi32> -> V1.
static LogicalResult foldExtractOpFromExtractChain(ExtractOp extractOp)
Fold the result of chains of ExtractOp in place by simply concatenating the positions.
static OpFoldResult foldFromElementsToElements(FromElementsOp fromElementsOp)
Folds vector.from_elements(vector.to_elements(vector)) into vector.
static bool hasZeroDimVectors(Operation *op)
Returns true if the operation has a 0-D vector type operand or result.
static void printTransferAttrs(OpAsmPrinter &p, VectorTransferOpInterface op)
static Value foldScalarExtractFromFromElements(ExtractOp extractOp)
Try to fold the extraction of a scalar from a vector defined by vector.from_elements.
static Attribute convertNumericAttr(Attribute attr, Type expectedType)
Converts numeric attributes to the expected type.
static Value foldExtractFromExtractStrided(ExtractOp extractOp)
Fold an ExtractOp from ExtractStridedSliceOp.
static llvm::SetVector< int64_t > computeBroadcastedUnitDims(ArrayRef< int64_t > srcShape, ArrayRef< int64_t > dstShape)
Return the dimensions of the result vector that were formerly ones in the source tensor and thus corr...
static Value foldExtractFromBroadcast(ExtractOp extractOp)
Fold extract(broadcast(X)) to either extract(X) or just X.
static LogicalResult foldToElementsFromElements(ToElementsOp toElementsOp, SmallVectorImpl< OpFoldResult > &results)
Folds vector.to_elements(vector.from_elements(e0, e1, ...)) into (e0, e1, ...).
static Attribute foldPoisonSrcExtractOp(Attribute srcAttr)
Fold a vector extract from is a poison source.
static LogicalResult foldBroadcastOfShapeCast(BroadcastOp broadcastOp)
static OpFoldResult foldShufflePoisonInputs(MLIRContext *context, Attribute v1Attr, Attribute v2Attr)
Fold shuffle poison, poison -> poison.
static bool isSupportedCombiningKind(CombiningKind combiningKind, Type elementType)
static Attribute foldPoisonIndexInsertExtractOp(MLIRContext *context, ArrayRef< int64_t > staticPos, int64_t poisonVal)
Fold an insert or extract operation into an poison value when a poison index is found at any dimensio...
MaskFormat
Helper enum to classify mask value.
static ArrayAttr makeI64ArrayAttr(ArrayRef< int64_t > values, MLIRContext *context)
static unsigned getEffectiveVectorRankForXferOp(ShapedType shapedType, VectorType vectorType)
Returns the effective rank of the vector to read/write for Xfer Ops.
static OpFoldResult foldFromElementsToConstant(FromElementsOp fromElementsOp, ArrayRef< Attribute > elements)
Fold vector.from_elements to a constant when all operands are constants.
static LogicalResult incSlicePosition(MutableArrayRef< int64_t > position, ArrayRef< int64_t > shape, ArrayRef< int64_t > offsets)
static Value extractInsertFoldConstantOp(OpType op, AdaptorType adaptor, SmallVectorImpl< Value > &operands)
If the dynamic indices of extractOp or insertOp are in fact constants, then fold it.
static LogicalResult foldToElementsOfBroadcast(ToElementsOp toElementsOp, SmallVectorImpl< OpFoldResult > &results)
Folds vector.to_elements(vector.broadcast(x)) for the scalar case only.
static bool isStepIndexArray(ArrayRef< T > idxArr, uint64_t begin, size_t width)
static LogicalResult isIntegerArrayAttrConfinedToRange(OpType op, ArrayAttr arrayAttr, int64_t min, int64_t max, StringRef attrName, bool halfOpen=true)
static bool haveSameDefiningOp(OperandRange operands, Operation *defOp)
Returns true if all the operands are defined by defOp.
static int64_t getResultIndex(AffineMap map, AffineExpr targetExpr)
static LogicalResult isIntegerArrayAttrConfinedToShape(OpType op, ArrayAttr arrayAttr, ArrayRef< int64_t > shape, StringRef attrName, bool halfOpen=true, int64_t min=0)
static bool isSplatWriteConsistentWithMaskedRead(vector::TransferWriteOp write, vector::TransferReadOp read)
Check if write is of a constant splat and the masked read is padded with the same splat value – meani...
static LogicalResult isSumOfIntegerArrayAttrConfinedToShape(OpType op, ArrayAttr arrayAttr1, ArrayAttr arrayAttr2, ArrayRef< int64_t > shape, StringRef attrName1, StringRef attrName2, bool halfOpen=true, int64_t min=1)
static Attribute foldDenseElementsAttrDestInsertOp(InsertOp insertOp, Attribute srcAttr, Attribute dstAttr, int64_t maxVectorSizeFoldThreshold)
static LogicalResult foldTransferFullMask(TransferOp op)
static SmallVector< IntType > extractVector(ArrayAttr arrayAttr)
static std::vector< std::pair< int64_t, int64_t > > getDimMap(ArrayRef< AffineMap > indexingMaps, ArrayAttr iteratorTypes, IteratorType targetIteratorType, MLIRContext *context)
static bool isValidPositiveIndexOrPoison(int64_t index, int64_t poisonValue, int64_t maxIndex)
static OpFoldResult foldShuffleConstantInputs(ShuffleOp op, Attribute v1Attr, Attribute v2Attr)
Fold a shuffle of constant 1-D inputs by evaluating the mask.
static OpFoldResult foldExtractStridedSliceNonSplatConstant(ExtractStridedSliceOp op, Attribute foldInput)
static LogicalResult verifyPermutationMap(AffineMap permutationMap, EmitFun emitOpError)
static LogicalResult rewriteFromElementsAsBroadcast(FromElementsOp fromElementsOp, PatternRewriter &rewriter)
Rewrite vector.from_elements as vector.broadcast if the elements are the same.
static Value foldInsertUseChain(InsertOp insertOp)
Folder to replace the dest operand of the insert op with the root dest of the insert op use chain.
static bool isBroadcastLike(Operation *op)
All BroadcastOps, as well as ShapeCastOps that only prepend 1s, are considered to be 'broadcastlike'.
static LogicalResult isIntegerArrayAttrSmallerThanShape(OpType op, ArrayAttr arrayAttr, ArrayRef< int64_t > shape, StringRef attrName)
static Value foldExtractFromShapeCast(ExtractOp extractOp)
static LogicalResult verifyTransferOp(VectorTransferOpInterface op, ShapedType shapedType, VectorType vectorType, VectorType maskType, VectorType inferredMaskType, AffineMap permutationMap, ArrayAttr inBounds)
static bool isInBounds(TransferOp op, int64_t resultIdx, int64_t indicesIdx)
static LogicalResult verifyOutputShape(ContractionOp op, VectorType lhsType, VectorType rhsType, Type accType, Type resType, const std::vector< std::pair< int64_t, int64_t > > &contractingDimMap, const std::vector< std::pair< int64_t, int64_t > > &batchDimMap)
static bool verifyDimMap(VectorType lhsType, VectorType rhsType, const std::vector< std::pair< int64_t, int64_t > > &map)
static Type inferStridedSliceOpResultType(VectorType vectorType, ArrayAttr offsets, ArrayAttr sizes, ArrayAttr strides)
static OpFoldResult foldShufflePoisonOperandToMask(ShuffleOp op)
If a shuffle operand is poison, replace all mask indices that reference it with kPoisonIndex.
static LogicalResult foldSize1TransferPermutationMap(TransferOp op)
When the vector type is vector<1xT>, the permutation map is irrelevant: the single vector lane always...
static Value foldExtractFromShuffle(ExtractOp extractOp)
Fold extractOp coming from ShuffleOp.
static LogicalResult foldTransferInBoundsAttribute(TransferOp op)
static Value foldExtractStridedOpFromInsertChain(ExtractOp extractOp)
Fold extract_op fed from a chain of insertStridedSlice ops.
static int64_t calculateInsertPosition(VectorType destTy, ArrayRef< int64_t > positions)
static Attribute foldDenseElementsAttrSrcExtractOp(ExtractOp extractOp, Attribute srcAttr)
Fold a vector extract extracting from a DenseElementsAttr.
static void populateFromInt64AttrArray(ArrayAttr arrayAttr, SmallVectorImpl< int64_t > &results)
Rewrite from_elements on multiple scalar extracts as a shape_cast on a single extract.
Base type for affine expression.
A multi-dimensional affine map Affine map's are immutable like Type's, and they are uniqued.
static AffineMap getMinorIdentityMap(unsigned dims, unsigned results, MLIRContext *context)
Returns an identity affine map (d0, ..., dn) -> (dp, ..., dn) on the most minor dimensions.
MLIRContext * getContext() const
bool isMinorIdentity() const
Returns true if this affine map is a minor identity, i.e.
unsigned getDimPosition(unsigned idx) const
Extracts the position of the dimensional expression at the given result, when the caller knows it is ...
static AffineMap get(MLIRContext *context)
Returns a zero result affine map with no dimensions or symbols: () -> ().
bool isProjectedPermutation(bool allowZeroInResults=false) const
Returns true if the AffineMap represents a subset (i.e.
unsigned getNumSymbols() const
unsigned getNumDims() const
ArrayRef< AffineExpr > getResults() const
unsigned getNumResults() const
static SmallVector< AffineMap, 4 > inferFromExprList(ArrayRef< ArrayRef< AffineExpr > > exprsList, MLIRContext *context)
Returns a vector of AffineMaps; each with as many results as exprs.size(), as many dims as the larges...
unsigned getNumInputs() const
AffineExpr getResult(unsigned idx) const
static AffineMap getPermutationMap(ArrayRef< unsigned > permutation, MLIRContext *context)
Returns an AffineMap representing a permutation.
SmallVector< unsigned > getBroadcastDims() const
Returns the list of broadcast dimensions (i.e.
AffineMap compose(AffineMap map) const
Returns the AffineMap resulting from composing this with map.
@ Square
Square brackets surrounding zero or more operands.
virtual ParseResult parseColonTypeList(SmallVectorImpl< Type > &result)=0
Parse a colon followed by a type list, which must have at least one type.
virtual Builder & getBuilder() const =0
Return a builder which provides useful access to MLIRContext, global objects like types and attribute...
virtual ParseResult parseOptionalAttrDict(NamedAttrList &result)=0
Parse a named dictionary into 'result' if it is present.
MLIRContext * getContext() const
virtual InFlightDiagnostic emitError(SMLoc loc, const Twine &message={})=0
Emit a diagnostic at the specified location and return failure.
ParseResult addTypeToList(Type type, SmallVectorImpl< Type > &result)
Add the specified type to the end of the specified type list and return success.
virtual ParseResult parseColonType(Type &result)=0
Parse a colon followed by a type.
virtual SMLoc getCurrentLocation()=0
Get the location of the next token and store it into the argument.
virtual ParseResult parseOptionalComma()=0
Parse a , token if present.
virtual SMLoc getNameLoc() const =0
Return the location of the original name token.
virtual ParseResult parseType(Type &result)=0
Parse a type.
virtual ParseResult parseComma()=0
Parse a , token.
virtual ParseResult parseOptionalArrowTypeList(SmallVectorImpl< Type > &result)=0
Parse an optional arrow followed by a type list.
ParseResult parseKeywordType(const char *keyword, Type &result)
Parse a keyword followed by a type.
virtual ParseResult parseAttribute(Attribute &result, Type type={})=0
Parse an arbitrary attribute of a given type and return it in result.
virtual void printAttribute(Attribute attr)
Base storage class appearing in an attribute.
Attributes are known-constant values of operations.
Dialect & getDialect() const
Get the dialect this attribute is registered to.
OpListType & getOperations()
static BoolAttr get(MLIRContext *context, bool value)
This class is a general helper class for creating context-global objects like types,...
IntegerAttr getIndexAttr(int64_t value)
DenseI32ArrayAttr getDenseI32ArrayAttr(ArrayRef< int32_t > values)
IntegerAttr getIntegerAttr(Type type, int64_t value)
DenseI64ArrayAttr getDenseI64ArrayAttr(ArrayRef< int64_t > values)
IntegerAttr getI64IntegerAttr(int64_t value)
IntegerType getIntegerType(unsigned width)
TypedAttr getZeroAttr(Type type)
ArrayAttr getArrayAttr(ArrayRef< Attribute > value)
MLIRContext * getContext() const
ArrayAttr getI64ArrayAttr(ArrayRef< int64_t > values)
ArrayAttr getBoolArrayAttr(ArrayRef< bool > values)
ArrayAttr getAffineMapArrayAttr(ArrayRef< AffineMap > values)
static ConstantIntRanges fromUnsigned(const APInt &umin, const APInt &umax)
Create an ConstantIntRanges with the unsigned minimum and maximum equal to umin and umax and the sign...
static unsigned getStorageBitwidth(Type type)
Return the bitwidth that should be used for integer ranges describing type.
The main mechanism for performing data layout queries.
static DataLayout closest(Operation *op)
Returns the layout of the closest parent operation carrying layout info.
llvm::TypeSize getTypeSizeInBits(Type t) const
Returns the size in bits of the given type in the current scope.
An attribute that represents a reference to a dense vector or tensor object.
std::enable_if_t<!std::is_base_of< Attribute, T >::value||std::is_same< Attribute, T >::value, T > getSplatValue() const
Return the splat value for this attribute.
bool isSplat() const
Returns true if this attribute corresponds to a splat, i.e.
static DenseElementsAttr get(ShapedType type, ArrayRef< Attribute > values)
Constructs a dense elements attribute from an array of element values.
virtual Operation * materializeConstant(OpBuilder &builder, Attribute value, Type type, Location loc)
Registered hook to materialize a single constant operation from a given attribute value with the desi...
This is a utility class for mapping one set of IR entities to another.
void map(Value from, Value to)
Inserts a new mapping for 'from' to 'to'.
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.
NamedAttrList is array of NamedAttributes that tracks whether it is sorted and does some basic work t...
The OpAsmParser has methods for interacting with the asm parser: parsing things from it,...
virtual ParseResult parseRegion(Region ®ion, ArrayRef< Argument > arguments={}, bool enableNameShadowing=false)=0
Parses a region.
ParseResult parseTrailingOperandList(SmallVectorImpl< UnresolvedOperand > &result, Delimiter delimiter=Delimiter::None)
Parse zero or more trailing SSA comma-separated trailing operand references with a specified surround...
virtual ParseResult resolveOperand(const UnresolvedOperand &operand, Type type, SmallVectorImpl< Value > &result)=0
Resolve an operand to an SSA value, emitting an error on failure.
ParseResult resolveOperands(Operands &&operands, Type type, SmallVectorImpl< Value > &result)
Resolve a list of operands to SSA values, emitting an error on failure, or appending the results to t...
virtual ParseResult parseOperand(UnresolvedOperand &result, bool allowResultNumber=true)=0
Parse a single SSA value operand name along with a result number if allowResultNumber is true.
virtual ParseResult parseOperandList(SmallVectorImpl< UnresolvedOperand > &result, Delimiter delimiter=Delimiter::None, bool allowResultNumber=true, int requiredOperandCount=-1)=0
Parse zero or more SSA comma-separated operand references with a specified surrounding delimiter,...
This is a pure-virtual base class that exposes the asmprinter hooks necessary to implement a custom p...
virtual void printOptionalAttrDict(ArrayRef< NamedAttribute > attrs, ArrayRef< StringRef > elidedAttrs={})=0
If the specified operation has attributes, print out an attribute dictionary with their values.
virtual void printCustomOrGenericOp(Operation *op)=0
Prints the entire operation with the custom assembly form, if available, or the generic assembly form...
RAII guard to reset the insertion point of the builder when destroyed.
This class helps build Operations.
Block * createBlock(Region *parent, Region::iterator insertPt={}, TypeRange argTypes={}, ArrayRef< Location > locs={})
Add new block with 'argTypes' arguments and set the insertion point to the end of it.
Operation * clone(Operation &op, IRMapping &mapper)
Creates a deep copy of the specified operation, remapping any operands that use values outside of the...
void setInsertionPoint(Block *block, Block::iterator insertPoint)
Set the insertion point to the specified location.
Block * getInsertionBlock() const
Return the block the current insertion point belongs to.
void createOrFold(SmallVectorImpl< Value > &results, Location location, Args &&...args)
Create an operation of specific op type at the current insertion point, and immediately try to fold i...
void setInsertionPointAfter(Operation *op)
Sets the insertion point to the node after the specified operation, which will cause subsequent inser...
This class represents a single result from folding an operation.
This class implements the operand iterators for the Operation class.
Operation is the basic unit of execution within MLIR.
Value getOperand(unsigned idx)
void dropAllUses()
Drop all uses of results of this operation.
void setOperand(unsigned idx, Value value)
Block * getBlock()
Returns the operation block that contains this operation.
Location getLoc()
The source location the operation was defined or derived from.
operand_type_range getOperandTypes()
result_type_range getResultTypes()
void moveBefore(Operation *existingOp)
Unlink this operation from its current block and insert it right before existingOp which may be in th...
result_range getResults()
InFlightDiagnostic emitOpError(const Twine &message={})
Emit an error with the op name prefixed, like "'dim' op " which is convenient for verifiers.
unsigned getNumResults()
Return the number of results held by this operation.
A special type of RewriterBase that coordinates the application of a rewrite pattern on the current I...
This class contains a list of basic blocks and a link to the parent operation it is attached to.
MLIRContext * getContext() const
RewritePatternSet & add(ConstructorArg &&arg, ConstructorArgs &&...args)
Add an instance of each of the pattern types 'Ts' to the pattern list with the given arguments.
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,...
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...
T * allocate()
Allocate an instance of the provided type.
Instances of the Type class are uniqued, have an immutable identifier and an optional mutable compone...
bool isIntOrIndexOrFloat() const
Return true if this is an integer (of any signedness), index, or float type.
bool isIntOrIndex() const
Return true if this is an integer (of any signedness) or an index type.
bool isIntOrFloat() const
Return true if this is an integer (of any signedness) or a float type.
unsigned getIntOrFloatBitWidth() const
Return the bit width of an integer or a float type, assert failure on other types.
static FailureOr< bool > areEqual(const Variable &var1, const Variable &var2)
Compute whether the given variables are equal.
static FailureOr< int64_t > computeConstantDelta(Value value1, Value value2, std::optional< int64_t > dim1=std::nullopt, std::optional< int64_t > dim2=std::nullopt)
Compute a constant delta between the given two values.
This class represents an instance of an SSA value in the MLIR system, representing a computable value...
Type getType() const
Return the type of this value.
Location getLoc() const
Return the location of this value.
Operation * getDefiningOp() const
If this value is the result of an operation, return the operation that defines it.
This is a builder type that keeps local references to arguments.
Builder & setElementType(Type newElementType)
Specialization of arith.constant op that returns an integer of index type.
static ConstantIndexOp create(OpBuilder &builder, Location location, int64_t value)
Speculatability
This enum is returned from the getSpeculatability method in the ConditionallySpeculatable op interfac...
constexpr auto Speculatable
constexpr auto NotSpeculatable
FailureOr< int64_t > fullyComposeAndComputeConstantDelta(Value value1, Value value2)
Compute a constant delta of the given two values.
constexpr void enumerate(std::tuple< Tys... > &tuple, CallbackT &&callback)
FailureOr< std::optional< SmallVector< Value > > > bubbleDownInPlaceMemorySpaceCastImpl(OpOperand &operand, ValueRange results)
Tries to bubble-down inplace a MemorySpaceCastOpInterface operation referenced by operand.
bool hasNegativeStaticStride(MemRefType memRefTy)
Returns true if any stride of memRefTy is statically known to be negative.
LogicalResult foldMemRefCast(Operation *op, Value inner=nullptr)
This is a common utility used for patterns of the form "someop(memref.cast) -> someop".
Operation::operand_range getIndices(Operation *op)
Get the indices that the given load/store operation is operating on.
MemRefType getMemRefType(T &&t)
Convenience method to abbreviate casting getType().
LogicalResult foldTensorCast(Operation *op)
Performs folding of any operand of op if it comes from a tensor::CastOp that can be folded.
detail::poison_attr_matcher m_Poison()
Matches a poison constant (any attribute implementing PoisonAttrInterface).
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.
ArrayAttr getVectorSubscriptAttr(Builder &b, ArrayRef< int64_t > values)
Returns an integer array attribute containing the given values using the integer type required for su...
Operation * maskOperation(OpBuilder &builder, Operation *maskableOp, Value mask, Value passthru=Value())
Creates a vector.mask operation around a maskable operation.
void buildTerminatedBody(OpBuilder &builder, Location loc)
Default callback to build a region with a 'vector.yield' terminator with no arguments.
std::optional< int64_t > getConstantVscaleMultiplier(Value value)
If value is a constant multiple of vector.vscale (e.g.
AffineMap getTransferMinorIdentityMap(ShapedType shapedType, VectorType vectorType)
Build the default minor identity map suitable for a vector transfer.
bool checkSameValueRAW(TransferWriteOp defWrite, TransferReadOp read)
Return true if the transfer_write fully writes the data accessed by the transfer_read.
ConstantMaskKind
Predefined constant_mask kinds.
BroadcastableToResult isBroadcastableTo(Type srcType, VectorType dstVectorType, std::pair< VectorDim, VectorDim > *mismatchingDims=nullptr)
Return whether srcType can be broadcast to dstVectorType under the semantics of the vector....
VectorType inferTransferOpMaskType(VectorType vecType, AffineMap permMap)
Infers the mask type for a transfer op given its vector type and permutation map.
Value selectPassthru(OpBuilder &builder, Value mask, Value newValue, Value passthru)
Creates a vector select operation that picks values from newValue or passthru for each result vector ...
bool isDisjointTransferIndices(VectorTransferOpInterface transferA, VectorTransferOpInterface transferB, bool testDynamicValueUsingBounds=false)
Return true if we can prove that the transfer operations access disjoint memory, without requring the...
bool isDisjointTransferSet(VectorTransferOpInterface transferA, VectorTransferOpInterface transferB, bool testDynamicValueUsingBounds=false)
Return true if we can prove that the transfer operations access disjoint memory, requiring the operat...
bool checkSameValueWAW(TransferWriteOp write, TransferWriteOp priorWrite)
Return true if the write op fully over-write the priorWrite transfer_write op.
SmallVector< int64_t > getAsIntegers(ArrayRef< Value > values)
Returns the integer numbers in values.
void populateVectorToVectorCanonicalizationPatterns(RewritePatternSet &patterns, PatternBenefit benefit=1)
Collect a set of vector-to-vector canonicalization patterns.
void createMaskOpRegion(OpBuilder &builder, Operation *maskableOp)
Create the vector.yield-ended region of a vector.mask op with maskableOp as masked operation.
SmallVector< Value > getAsValues(OpBuilder &builder, Location loc, ArrayRef< OpFoldResult > foldResults)
Convert foldResults into Values.
Value getVectorReductionOp(arith::AtomicRMWKind op, OpBuilder &builder, Location loc, Value vector)
Returns the value obtained by reducing the vector into a scalar using the operation kind associated w...
BroadcastableToResult
Models whether srcType can be broadcast to dstVectorType under the semantics of the vector....
IntegerType getVectorSubscriptType(Builder &builder)
Returns the integer type required for subscripts in the vector dialect.
Include the generated interface declarations.
AffineMap simplifyAffineMap(AffineMap map)
Simplifies an affine map by simplifying its underlying AffineExpr results.
bool matchPattern(Value value, const Pattern &pattern)
Entry point for matching a pattern over a Value.
std::optional< int64_t > getConstantIntValue(OpFoldResult ofr)
If ofr is a constant integer or an IntegerAttr, return the integer.
llvm::function_ref< void(Value, const ConstantIntRanges &)> SetIntRangeFn
The type of the setResultRanges callback provided to ops implementing InferIntRangeInterface.
SmallVector< int64_t > computeStrides(ArrayRef< int64_t > sizes)
bool isEqualConstantIntOrValue(OpFoldResult ofr1, OpFoldResult ofr2)
Return true if ofr1 and ofr2 are the same integer constant attribute values or the same SSA value.
Type getType(OpFoldResult ofr)
Returns the int type of the integer in ofr.
SmallVector< T > applyPermutation(ArrayRef< T > input, ArrayRef< int64_t > permutation)
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...
LogicalResult emitOptionalError(std::optional< Location > loc, Args &&...args)
Overloads of the above emission functions that take an optionally null location.
llvm::DenseSet< ValueT, ValueInfoT > DenseSet
InFlightDiagnostic emitError(Location loc)
Utility method to emit an error message using this location.
AffineMap inversePermutation(AffineMap map)
Returns a map of codomain to domain dimensions such that the first codomain dimension for a particula...
StorageUniquer::StorageAllocator AttributeStorageAllocator
Type getElementTypeOrSelf(Type type)
Return the element type or return the type itself.
SmallVector< int64_t > getI64SubArray(ArrayAttr arrayAttr, unsigned dropFront=0, unsigned dropBack=0)
Helper to return a subset of arrayAttr as a vector of int64_t.
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.
void dispatchIndexOpFoldResults(ArrayRef< OpFoldResult > ofrs, SmallVectorImpl< Value > &dynamicVec, SmallVectorImpl< int64_t > &staticVec)
Helper function to dispatch multiple OpFoldResults according to the behavior of dispatchIndexOpFoldRe...
AffineMap compressUnusedDims(AffineMap map)
Drop the dims that are not used.
Value getValueOrCreateConstantIndexOp(OpBuilder &b, Location loc, OpFoldResult ofr)
Converts an OpFoldResult to a Value.
AffineExpr getAffineConstantExpr(int64_t constant, MLIRContext *context)
LogicalResult verifyElementTypesMatch(Operation *op, ShapedType lhs, ShapedType rhs, StringRef lhsName, StringRef rhsName)
Verify that two shaped types have matching element types.
llvm::DenseMap< KeyT, ValueT, KeyInfoT, BucketT > DenseMap
SmallVector< T > applyPermutationMap(AffineMap map, llvm::ArrayRef< T > source)
Apply a permutation from map to source and return the result.
llvm::SmallBitVector getUnusedDimsBitVector(ArrayRef< AffineMap > maps)
int64_t linearize(ArrayRef< int64_t > offsets, ArrayRef< int64_t > basis)
Return the linearized index of 'offsets' w.r.t.
detail::constant_op_matcher m_Constant()
Matches a constant foldable operation.
void applyPermutationToVector(SmallVector< T, N > &inVec, ArrayRef< int64_t > permutation)
Apply the permutation defined by permutation to inVec.
AffineExpr getAffineDimExpr(unsigned position, MLIRContext *context)
These free functions allow clients of the API to not use classes in detail.
llvm::function_ref< Fn > function_ref
std::pair< SmallVector< int64_t >, SmallVector< Value > > decomposeMixedValues(ArrayRef< OpFoldResult > mixedValues)
Decompose a vector of mixed static or dynamic values into the corresponding pair of arrays.
SmallVector< int64_t > invertPermutationVector(ArrayRef< int64_t > permutation)
Helper method to apply to inverse a permutation.
Return a fused vector::ContractionOp which represents a patterns such as:
LogicalResult matchAndRewrite(AddOpType addOp, PatternRewriter &rewriter) const override
Canonicalize vector.to_elements(vector.broadcast(v)) where v is a vector.
LogicalResult matchAndRewrite(ToElementsOp toElementsOp, PatternRewriter &rewriter) const override
This is the representation of an operand reference.
OpRewritePattern is a wrapper around RewritePattern that allows for matching and rewriting against an...
OpRewritePattern Base
Type alias to allow derived classes to inherit constructors with using Base::Base;.
OpRewritePattern(MLIRContext *context, PatternBenefit benefit=1, ArrayRef< StringRef > generatedNames={})
This represents an operation in an abstracted form, suitable for use with the builder APIs.
static BitmaskEnumStorage * construct(AttributeStorageAllocator &allocator, const KeyTy &key)
bool operator==(const KeyTy &key) const
BitmaskEnumStorage(KeyTy val)