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) {
242 return !defWrite.hasOutOfBoundsDim() &&
243 defWrite.getIndices() == read.getIndices() &&
244 defWrite.getVectorType() == read.getVectorType() &&
245 defWrite.getPermutationMap() == read.getPermutationMap() &&
246 ((!defWrite.getMask() && !read.getMask()) ||
251 vector::TransferWriteOp priorWrite) {
252 return priorWrite.getIndices() == write.getIndices() &&
253 priorWrite.getMask() == write.getMask() &&
254 priorWrite.getVectorType() == write.getVectorType() &&
255 priorWrite.getPermutationMap() == write.getPermutationMap();
259 VectorTransferOpInterface transferA, VectorTransferOpInterface transferB,
260 bool testDynamicValueUsingBounds) {
262 if (transferA.getVectorType() != transferB.getVectorType())
264 unsigned rankOffset = transferA.getLeadingShapedRank();
265 for (
unsigned i = 0, e = transferA.getIndices().size(); i < e; i++) {
266 Value indexA = transferA.getIndices()[i];
267 Value indexB = transferB.getIndices()[i];
271 if (i < rankOffset) {
274 if (cstIndexA.has_value() && cstIndexB.has_value()) {
275 if (*cstIndexA != *cstIndexB)
279 if (testDynamicValueUsingBounds) {
282 FailureOr<uint64_t> delta =
284 if (succeeded(delta) && *delta != 0)
287 FailureOr<bool> testEqual =
289 if (succeeded(testEqual) && !testEqual.value())
295 int64_t vectorDim = transferA.getVectorType().getDimSize(i - rankOffset);
296 if (cstIndexA.has_value() && cstIndexB.has_value()) {
297 int64_t distance = std::abs(*cstIndexA - *cstIndexB);
298 if (distance >= vectorDim)
302 if (testDynamicValueUsingBounds) {
305 FailureOr<int64_t> delta =
307 if (succeeded(delta) && std::abs(*delta) >= vectorDim)
310 FailureOr<int64_t> computeDelta =
312 if (succeeded(computeDelta)) {
313 if (std::abs(computeDelta.value()) >= vectorDim)
323 VectorTransferOpInterface transferB,
324 bool testDynamicValueUsingBounds) {
325 if (transferA.getBase() != transferB.getBase())
328 testDynamicValueUsingBounds);
338 for (
auto [posInDim, dimSize, offsetInDim] :
339 llvm::reverse(llvm::zip_equal(position,
shape, offsets))) {
341 if (posInDim < dimSize + offsetInDim)
345 posInDim = offsetInDim;
355 llvm::transform(values, std::back_inserter(ints), [](
Value value) {
357 assert(constOp &&
"Unexpected non-constant index");
358 return constOp.value();
368 foldResults, std::back_inserter(ints), [](
OpFoldResult foldResult) {
369 assert(isa<Attribute>(foldResult) &&
"Unexpected non-constant index");
370 return cast<IntegerAttr>(cast<Attribute>(foldResult)).getInt();
380 llvm::transform(foldResults, std::back_inserter(values),
382 if (
auto attr = dyn_cast<Attribute>(foldResult))
384 builder, loc, cast<IntegerAttr>(attr).getInt())
387 return cast<Value>(foldResult);
398 auto lhs =
mul.getLhs();
399 auto rhs =
mul.getRhs();
400 if (lhs.getDefiningOp<vector::VectorScaleOp>())
402 if (rhs.getDefiningOp<vector::VectorScaleOp>())
412 if (
auto intAttr = dyn_cast<IntegerAttr>(attr)) {
413 if (
auto intType = dyn_cast<IntegerType>(expectedType)) {
414 if (intAttr.getType() != expectedType)
415 return IntegerAttr::get(expectedType, intAttr.getInt());
421 if (
auto floatAttr = dyn_cast<FloatAttr>(attr)) {
422 auto intType = dyn_cast<IntegerType>(expectedType);
426 APFloat floatVal = floatAttr.getValue();
427 APInt intVal = floatVal.bitcastToAPInt();
428 return IntegerAttr::get(expectedType, intVal);
437 Type srcType, VectorType dstVectorType,
438 std::pair<VectorDim, VectorDim> *mismatchingDims) {
440 if (isa<VectorElementTypeInterface>(srcType) && dstVectorType &&
444 VectorType srcVectorType = llvm::dyn_cast<VectorType>(srcType);
448 int64_t srcRank = srcVectorType.getRank();
449 int64_t dstRank = dstVectorType.getRank();
450 if (srcRank > dstRank)
454 int64_t lead = dstRank - srcRank;
455 for (
int64_t dimIdx = 0; dimIdx < srcRank; ++dimIdx) {
458 bool foundMismatchingDims =
false;
461 int64_t srcDim = srcVectorType.getDimSize(dimIdx);
462 int64_t dstDim = dstVectorType.getDimSize(lead + dimIdx);
463 if (srcDim != 1 && srcDim != dstDim)
464 foundMismatchingDims =
true;
467 bool srcDimScalableFlag = srcVectorType.getScalableDims()[dimIdx];
468 bool dstDimScalableFlag = dstVectorType.getScalableDims()[lead + dimIdx];
469 if ((srcDim == 1 && srcDimScalableFlag && dstDim != 1) ||
472 (srcDimScalableFlag != dstDimScalableFlag &&
473 (srcDim != 1 || srcDimScalableFlag)))
474 foundMismatchingDims =
true;
476 if (foundMismatchingDims) {
477 if (mismatchingDims !=
nullptr) {
478 mismatchingDims->first.dim = srcDim;
479 mismatchingDims->first.isScalable = srcDimScalableFlag;
481 mismatchingDims->second.dim = dstDim;
482 mismatchingDims->second.isScalable = dstDimScalableFlag;
524struct VectorInlinerInterface :
public DialectInlinerInterface {
525 using DialectInlinerInterface::DialectInlinerInterface;
534void VectorDialect::initialize() {
536#define GET_ATTRDEF_LIST
537#include "mlir/Dialect/Vector/IR/VectorAttributes.cpp.inc"
542#include "mlir/Dialect/Vector/IR/VectorOps.cpp.inc"
545 addInterfaces<VectorInlinerInterface>();
547 declarePromisedInterfaces<memref::IndexedAccessOpInterface, LoadOp, StoreOp,
548 MaskedLoadOp, MaskedStoreOp, ExpandLoadOp,
550 declarePromisedInterfaces<bufferization::BufferizableOpInterface,
551 TransferReadOp, TransferWriteOp, GatherOp, MaskOp,
553 declarePromisedInterfaces<SubsetOpInterface, TransferReadOp,
555 declarePromisedInterface<SubsetExtractionOpInterface, TransferReadOp>();
556 declarePromisedInterface<SubsetInsertionOpInterface, TransferWriteOp>();
557 declarePromisedInterface<ConvertToLLVMPatternInterface, VectorDialect>();
568 return arith::ConstantOp::materialize(builder, value, type, loc);
584void vector::MultiDimReductionOp::build(
OpBuilder &builder,
587 CombiningKind kind) {
589 for (
const auto &en : llvm::enumerate(reductionMask))
591 reductionDims.push_back(en.index());
592 build(builder,
result, kind, source,
acc, reductionDims);
595OpFoldResult MultiDimReductionOp::fold(FoldAdaptor adaptor) {
597 if (getReductionDims().empty())
602std::optional<SmallVector<int64_t, 4>>
603MultiDimReductionOp::getShapeForUnroll() {
604 return llvm::to_vector<4>(getSourceVectorType().
getShape());
607LogicalResult MultiDimReductionOp::verify() {
609 int64_t sourceRank = getSourceVectorType().getRank();
611 for (
int64_t dim : getReductionDims()) {
612 if (dim < 0 || dim >= sourceRank)
613 return emitOpError(
"reduction dimension out of range: ") << dim;
615 return emitOpError(
"duplicate reduction dimension: ") << dim;
616 isReduced[dim] =
true;
621 Type inferredReturnType;
622 auto sourceScalableDims = getSourceVectorType().getScalableDims();
623 for (
auto [dimIdx, dimSize] :
624 llvm::enumerate(getSourceVectorType().
getShape()))
625 if (!isReduced[dimIdx]) {
626 targetShape.push_back(dimSize);
627 scalableDims.push_back(sourceScalableDims[dimIdx]);
630 if (targetShape.empty())
631 inferredReturnType = getSourceVectorType().getElementType();
633 inferredReturnType = VectorType::get(
634 targetShape, getSourceVectorType().
getElementType(), scalableDims);
635 if (
getType() != inferredReturnType)
636 return emitOpError() <<
"destination type " <<
getType()
637 <<
" is incompatible with source type "
638 << getSourceVectorType();
644Type MultiDimReductionOp::getExpectedMaskType() {
645 auto vecType = getSourceVectorType();
646 return VectorType::get(vecType.getShape(),
647 IntegerType::get(vecType.getContext(), 1),
648 vecType.getScalableDims());
657struct ElideUnitDimsInMultiDimReduction
661 LogicalResult matchAndRewrite(MultiDimReductionOp reductionOp,
662 PatternRewriter &rewriter)
const override {
663 ArrayRef<int64_t> shape = reductionOp.getSourceVectorType().getShape();
664 for (
const auto &dim :
enumerate(shape)) {
665 if (reductionOp.isReducedDim(dim.index()) && dim.value() != 1)
670 OpBuilder::InsertionGuard guard(rewriter);
673 if (reductionOp.isMasked()) {
675 rootOp = reductionOp.getMaskingOp();
676 mask = reductionOp.getMaskingOp().getMask();
678 rootOp = reductionOp;
681 Location loc = reductionOp.getLoc();
682 Value acc = reductionOp.getAcc();
684 if (
auto dstVecType = dyn_cast<VectorType>(reductionOp.getDestType())) {
686 VectorType newMaskType =
687 VectorType::get(dstVecType.getShape(), rewriter.
getI1Type(),
688 dstVecType.getScalableDims());
689 mask = vector::ShapeCastOp::create(rewriter, loc, newMaskType, mask);
691 cast = vector::ShapeCastOp::create(
692 rewriter, loc, reductionOp.getDestType(), reductionOp.getSource());
697 mask = vector::ExtractOp::create(rewriter, loc, mask);
698 cast = vector::ExtractOp::create(rewriter, loc, reductionOp.getSource());
703 cast,
nullptr, mask);
710void MultiDimReductionOp::getCanonicalizationPatterns(
712 results.
add<ElideUnitDimsInMultiDimReduction>(context);
721 arith::FastMathFlags fastMathFlags) {
727 arith::FastMathFlags fastMathFlags) {
729 llvm::cast<VectorType>(
vector.getType()).getElementType(), kind,
vector,
733LogicalResult ReductionOp::verify() {
735 int64_t rank = getSourceVectorType().getRank();
737 return emitOpError(
"unsupported reduction rank: ") << rank;
740 Type eltType = getDest().getType();
742 return emitOpError(
"unsupported reduction type '")
743 << eltType <<
"' for kind '" << stringifyCombiningKind(getKind())
752Type ReductionOp::getExpectedMaskType() {
753 auto vecType = getSourceVectorType();
754 return VectorType::get(vecType.getShape(),
755 IntegerType::get(vecType.getContext(), 1),
756 vecType.getScalableDims());
763 case arith::AtomicRMWKind::addf:
764 case arith::AtomicRMWKind::addi:
765 return vector::ReductionOp::create(builder,
vector.getLoc(),
766 CombiningKind::ADD,
vector);
767 case arith::AtomicRMWKind::mulf:
768 case arith::AtomicRMWKind::muli:
769 return vector::ReductionOp::create(builder,
vector.getLoc(),
770 CombiningKind::MUL,
vector);
771 case arith::AtomicRMWKind::minimumf:
772 return vector::ReductionOp::create(builder,
vector.getLoc(),
773 CombiningKind::MINIMUMF,
vector);
774 case arith::AtomicRMWKind::mins:
775 return vector::ReductionOp::create(builder,
vector.getLoc(),
776 CombiningKind::MINSI,
vector);
777 case arith::AtomicRMWKind::minu:
778 return vector::ReductionOp::create(builder,
vector.getLoc(),
779 CombiningKind::MINUI,
vector);
780 case arith::AtomicRMWKind::maximumf:
781 return vector::ReductionOp::create(builder,
vector.getLoc(),
782 CombiningKind::MAXIMUMF,
vector);
783 case arith::AtomicRMWKind::maxs:
784 return vector::ReductionOp::create(builder,
vector.getLoc(),
785 CombiningKind::MAXSI,
vector);
786 case arith::AtomicRMWKind::maxu:
787 return vector::ReductionOp::create(builder,
vector.getLoc(),
788 CombiningKind::MAXUI,
vector);
789 case arith::AtomicRMWKind::andi:
790 return vector::ReductionOp::create(builder,
vector.getLoc(),
791 CombiningKind::AND,
vector);
792 case arith::AtomicRMWKind::ori:
793 return vector::ReductionOp::create(builder,
vector.getLoc(),
794 CombiningKind::OR,
vector);
795 case arith::AtomicRMWKind::minnumf:
796 return vector::ReductionOp::create(builder,
vector.getLoc(),
797 CombiningKind::MINNUMF,
vector);
798 case arith::AtomicRMWKind::maxnumf:
799 return vector::ReductionOp::create(builder,
vector.getLoc(),
800 CombiningKind::MAXNUMF,
vector);
801 case arith::AtomicRMWKind::xori:
802 return vector::ReductionOp::create(builder,
vector.getLoc(),
803 CombiningKind::XOR,
vector);
811std::optional<SmallVector<int64_t, 4>> ReductionOp::getShapeForUnroll() {
812 return llvm::to_vector<4>(getSourceVectorType().
getShape());
819 LogicalResult matchAndRewrite(ReductionOp reductionOp,
824 cast<vector::MaskableOpInterface>(reductionOp.getOperation());
827 if (maskableOp.isMasked()) {
829 rootOp = maskableOp.getMaskingOp();
830 mask = maskableOp.getMaskingOp().getMask();
832 rootOp = reductionOp;
835 auto vectorType = reductionOp.getSourceVectorType();
836 if (vectorType.getRank() != 0 && vectorType.getDimSize(0) != 1)
839 Location loc = reductionOp.getLoc();
841 mask = ExtractOp::create(rewriter, loc, mask);
842 Value
result = ExtractOp::create(rewriter, loc, reductionOp.getVector());
844 if (Value acc = reductionOp.getAcc())
847 reductionOp.getFastmathAttr(), mask);
857 results.
add<ElideSingleElementReduction>(context);
871 getIndexingMapsAttrName(
result.name),
875 getIteratorTypesAttrName(
result.name),
878 return IteratorTypeAttr::get(builder.getContext(), t);
886 build(builder,
result, lhs, rhs,
acc, indexingMaps, iteratorTypes,
887 ContractionOp::getDefaultKind());
893 ArrayAttr iteratorTypes, CombiningKind kind,
894 arith::FastMathFlags fastMathFlags) {
897 result.addAttribute(getIndexingMapsAttrName(
result.name), indexingMaps);
898 result.addAttribute(getIteratorTypesAttrName(
result.name), iteratorTypes);
900 CombiningKindAttr::get(builder.
getContext(), kind));
901 if (fastMathFlags != arith::FastMathFlags::none)
903 getFastmathAttrName(
result.name),
904 arith::FastMathFlagsAttr::get(builder.
getContext(), fastMathFlags));
915 DictionaryAttr dictAttr;
929 result.attributes.append(dictAttr.getValue().begin(),
930 dictAttr.getValue().end());
936 auto iteratorTypes = dyn_cast_or_null<ArrayAttr>(
937 result.attributes.get(getIteratorTypesAttrName(
result.name)));
938 if (!iteratorTypes) {
940 <<
"expected " << getIteratorTypesAttrName(
result.name)
941 <<
" array attribute";
946 for (StringRef s : iteratorTypes.getAsValueRange<StringAttr>()) {
947 auto maybeIteratorType = symbolizeIteratorType(s);
948 if (!maybeIteratorType.has_value())
949 return parser.
emitError(loc) <<
"unexpected iterator_type (" << s <<
")";
951 iteratorTypeAttrs.push_back(
952 IteratorTypeAttr::get(parser.
getContext(), maybeIteratorType.value()));
954 result.attributes.set(getIteratorTypesAttrName(
result.name),
957 if (!
result.attributes.get(getKindAttrName(
result.name))) {
959 getKindAttrName(
result.name),
960 CombiningKindAttr::get(
result.getContext(),
961 ContractionOp::getDefaultKind()));
963 if (masksInfo.empty())
965 if (masksInfo.size() != 2)
967 "expected zero or exactly 2 vector mask operands");
968 auto lhsType = llvm::cast<VectorType>(types[0]);
969 auto rhsType = llvm::cast<VectorType>(types[1]);
971 std::array<VectorType, 2> maskTypes = {
981 auto attrNames = getTraitAttrNames();
983 traitAttrsSet.insert_range(attrNames);
984 NamedAttrList allAttrs(getOperation()->getRawDictionaryAttrs());
985 getOperation()->getName().walkInherentAttrs(
987 [&](StringRef name,
Attribute &attr) { allAttrs.append(name, attr); });
989 for (
auto attr : allAttrs) {
990 if (attr.getName() == getIteratorTypesAttrName()) {
992 llvm::cast<ArrayAttr>(attr.getValue())
993 .getAsValueRange<IteratorTypeAttr, IteratorType>();
999 llvm::map_to_vector(iteratorTypes, [&](IteratorType t) ->
Attribute {
1000 return StringAttr::get(
getContext(), stringifyIteratorType(t));
1003 attrs.emplace_back(getIteratorTypesAttrName(),
1004 ArrayAttr::get(
getContext(), iteratorTypeNames));
1005 }
else if (traitAttrsSet.count(attr.getName().strref()) > 0) {
1007 if (attr.getName() == getFastmathAttrName() &&
1008 llvm::cast<arith::FastMathFlagsAttr>(attr.getValue()).getValue() ==
1009 arith::FastMathFlags::none)
1011 attrs.push_back(attr);
1015 auto dictAttr = DictionaryAttr::get(
getContext(), attrs);
1016 p <<
" " << dictAttr <<
" " << getLhs() <<
", ";
1017 p << getRhs() <<
", " << getAcc();
1020 p <<
" : " << getLhs().getType() <<
", " << getRhs().getType() <<
" into "
1025 const std::vector<std::pair<int64_t, int64_t>> &map) {
1026 for (
auto &dimPair : map) {
1027 if (dimPair.first < 0 || dimPair.first >= lhsType.getRank() ||
1028 dimPair.second < 0 || dimPair.second >= rhsType.getRank() ||
1029 lhsType.getDimSize(dimPair.first) != rhsType.getDimSize(dimPair.second))
1036 ContractionOp op, VectorType lhsType, VectorType rhsType,
Type accType,
1038 const std::vector<std::pair<int64_t, int64_t>> &contractingDimMap,
1039 const std::vector<std::pair<int64_t, int64_t>> &batchDimMap) {
1042 for (
auto &dimPair : contractingDimMap) {
1043 lhsContractingDimSet.insert(dimPair.first);
1044 rhsContractingDimSet.insert(dimPair.second);
1047 llvm::make_second_range(batchDimMap));
1051 for (
int64_t i = 0, e = lhsType.getRank(); i < e; ++i) {
1052 if (lhsContractingDimSet.count(i) > 0)
1054 expectedResultDims.push_back(lhsType.getDimSize(i));
1058 for (
int64_t i = 0, e = rhsType.getRank(); i < e; ++i) {
1059 if (rhsContractingDimSet.count(i) > 0 || rhsBatchDimSet.count(i) > 0)
1061 expectedResultDims.push_back(rhsType.getDimSize(i));
1065 if (expectedResultDims.empty()) {
1067 if (llvm::isa<VectorType>(resType) || llvm::isa<VectorType>(accType))
1068 return op.emitOpError(
"invalid accumulator/result vector shape");
1071 auto resVectorType = llvm::dyn_cast<VectorType>(resType);
1072 auto accVectorType = llvm::dyn_cast<VectorType>(accType);
1073 if (!resVectorType || !accVectorType)
1074 return op.emitOpError(
"invalid accumulator/result vector shape");
1080 AffineMap lhsMap = op.getIndexingMapsArray()[0];
1081 AffineMap rhsMap = op.getIndexingMapsArray()[1];
1083 return op.emitOpError(
1084 "expected all dimensions to be either a LHS or a RHS dimension");
1087 {std::make_pair(lhsType, lhsMap), std::make_pair(rhsType, rhsMap)}) {
1088 VectorType v = pair.first;
1089 auto map = pair.second;
1090 for (
unsigned idx = 0, e = v.getRank(); idx < e; ++idx) {
1091 unsigned pos = map.getDimPosition(idx);
1096 if (!llvm::all_of(extents, [](
AffineExpr e) {
return e; }))
1097 return op.emitOpError(
"expected all dimensions to get an extent as "
1098 "either a LHS or a RHS dimension");
1100 AffineMap resMap = op.getIndexingMapsArray()[2];
1105 assert(llvm::all_of(expectedMap.
getResults(),
1106 llvm::IsaPred<AffineConstantExpr>) &&
1107 "expected constant extent along all dimensions.");
1109 auto expectedShape =
1111 return cast<AffineConstantExpr>(e).getValue();
1114 VectorType::get(expectedShape, resVectorType.getElementType(),
1115 resVectorType.getScalableDims());
1116 if (resVectorType != expected || accVectorType != expected)
1117 return op.emitOpError(
1118 "invalid accumulator/result vector shape, expected: ")
1124LogicalResult ContractionOp::verify() {
1125 VectorType lhsType = getLhsType();
1126 VectorType rhsType = getRhsType();
1127 Type accType = getAccType();
1128 Type resType = getResultType();
1130 if (llvm::isa<IntegerType>(lhsType.getElementType())) {
1131 if (!lhsType.getElementType().isSignlessInteger())
1132 return emitOpError(
"only supports signless integer types");
1136 if (getIndexingMapsArray().size() != 3)
1137 return emitOpError(
"expected an indexing map for each vector operand");
1142 unsigned numIterators = getIteratorTypes().getValue().size();
1143 for (
const auto &it : llvm::enumerate(getIndexingMapsArray())) {
1144 auto index = it.index();
1145 auto map = it.value();
1146 if (map.getNumSymbols() != 0)
1147 return emitOpError(
"expected indexing map ")
1148 <<
index <<
" to have no symbols";
1149 auto vectorType = llvm::dyn_cast<VectorType>(getOperand(
index).
getType());
1150 unsigned rank = vectorType ? vectorType.getShape().size() : 0;
1153 if (map.getNumDims() != numIterators)
1154 return emitOpError(
"expected indexing map ")
1155 <<
index <<
" to have " << numIterators <<
" number of inputs";
1156 if (map.getNumResults() != rank)
1157 return emitOpError(
"expected indexing map ")
1158 <<
index <<
" to have " << rank <<
" number of outputs";
1159 if (!map.isProjectedPermutation())
1160 return emitOpError(
"expected indexing map ")
1161 <<
index <<
" to be a projected permutation of its inputs";
1164 auto contractingDimMap = getContractingDimMap();
1165 auto batchDimMap = getBatchDimMap();
1168 if (contractingDimMap.empty())
1169 return emitOpError(
"expected at least one contracting dimension pair");
1172 if (!
verifyDimMap(lhsType, rhsType, contractingDimMap))
1173 return emitOpError(
"invalid contracting dimension map");
1177 return emitOpError(
"invalid batch dimension map");
1181 contractingDimMap, batchDimMap)))
1184 if (!getKindAttr()) {
1185 return emitOpError(
"expected 'kind' attribute of type CombiningKind (e.g. "
1186 "'vector.kind<add>')");
1190 auto vectorType = llvm::dyn_cast<VectorType>(resType);
1191 auto elementType = vectorType ? vectorType.getElementType() : resType;
1193 return emitOpError(
"unsupported contraction type");
1196 return cast<IndexingMapOpInterface>(this->getOperation()).verifyImpl();
1203Type ContractionOp::getExpectedMaskType() {
1204 auto indexingMaps = this->getIndexingMapsArray();
1207 VectorType lhsType = this->getLhsType();
1208 VectorType rhsType = this->getRhsType();
1210 unsigned numVecDims = lhsIdxMap.
getNumDims();
1216 for (
auto [dimIdx, dimSize] : llvm::enumerate(lhsType.getShape())) {
1219 lhsType.getScalableDims()[dimIdx];
1221 for (
auto [dimIdx, dimSize] : llvm::enumerate(rhsType.getShape())) {
1224 rhsType.getScalableDims()[dimIdx];
1227 assert(ShapedType::isStaticShape(maskShape) &&
1228 "Mask shape couldn't be computed");
1230 return VectorType::get(maskShape,
1231 IntegerType::get(lhsType.getContext(), 1),
1232 maskShapeScalableDims);
1237 getIteratorTypesAttrName(), getKindAttrName(),
1238 getFastmathAttrName()};
1248static std::vector<std::pair<int64_t, int64_t>>
1250 IteratorType targetIteratorType,
MLIRContext *context) {
1251 std::vector<std::pair<int64_t, int64_t>> dimMap;
1252 for (
const auto &it : llvm::enumerate(iteratorTypes)) {
1253 auto iteratorType = llvm::cast<IteratorTypeAttr>(it.value()).getValue();
1254 if (iteratorType != targetIteratorType)
1260 if (lhsDim >= 0 && rhsDim >= 0)
1261 dimMap.emplace_back(lhsDim, rhsDim);
1266void ContractionOp::getIterationBounds(
1268 auto lhsShape = getLhsType().getShape();
1269 auto resVectorType = llvm::dyn_cast<VectorType>(getResultType());
1271 for (
const auto &it : llvm::enumerate(getIteratorTypes())) {
1274 auto iteratorType = llvm::cast<IteratorTypeAttr>(it.value()).getValue();
1275 if (iteratorType == IteratorType::reduction) {
1278 assert(lhsDimIndex >= 0);
1279 iterationBounds.push_back(lhsShape[lhsDimIndex]);
1284 assert(resDimIndex >= 0);
1285 assert(resVectorType !=
nullptr);
1286 iterationBounds.push_back(resVectorType.getShape()[resDimIndex]);
1290void ContractionOp::getIterationIndexMap(
1292 unsigned numMaps = getIndexingMapsArray().size();
1293 iterationIndexMap.resize(numMaps);
1294 for (
const auto &it : llvm::enumerate(getIndexingMapsArray())) {
1295 auto index = it.index();
1296 auto map = it.value();
1297 for (
unsigned i = 0, e = map.getNumResults(); i < e; ++i) {
1298 auto dim = cast<AffineDimExpr>(map.getResult(i));
1299 iterationIndexMap[
index][dim.getPosition()] = i;
1304std::vector<std::pair<int64_t, int64_t>> ContractionOp::getContractingDimMap() {
1306 return getDimMap(indexingMaps, getIteratorTypes(), IteratorType::reduction,
1310std::vector<std::pair<int64_t, int64_t>> ContractionOp::getBatchDimMap() {
1312 return getDimMap(indexingMaps, getIteratorTypes(), IteratorType::parallel,
1316std::optional<SmallVector<int64_t, 4>> ContractionOp::getShapeForUnroll() {
1318 getIterationBounds(
shape);
1340template <
typename AddOpType>
1346 auto canonicalize = [&](
Value maybeContraction,
1347 Value otherOperand) -> vector::ContractionOp {
1348 vector::ContractionOp contractionOp =
1349 dyn_cast_or_null<vector::ContractionOp>(
1352 return vector::ContractionOp();
1353 if (
auto maybeZero = dyn_cast_or_null<arith::ConstantOp>(
1354 contractionOp.getAcc().getDefiningOp())) {
1355 if (maybeZero.getValue() ==
1356 rewriter.
getZeroAttr(contractionOp.getAcc().getType())) {
1358 bvm.
map(contractionOp.getAcc(), otherOperand);
1359 auto newContraction =
1360 cast<vector::ContractionOp>(rewriter.
clone(*contractionOp, bvm));
1361 rewriter.
replaceOp(addOp, newContraction.getResult());
1362 return newContraction;
1365 return vector::ContractionOp();
1368 Value a = addOp->getOperand(0),
b = addOp->getOperand(1);
1369 vector::ContractionOp
contract = canonicalize(a,
b);
1394 setResultRanges(getResult(), argRanges.front());
1399 auto vectorTy = cast<VectorType>(source.
getType());
1424 build(builder,
result, source, dynamicPos,
1429ExtractOp::inferReturnTypes(
MLIRContext *, std::optional<Location>,
1430 ExtractOp::Adaptor adaptor,
1432 auto vectorType = llvm::cast<VectorType>(adaptor.getSource().getType());
1433 if (
static_cast<int64_t>(adaptor.getStaticPosition().size()) ==
1434 vectorType.getRank()) {
1435 inferredReturnTypes.push_back(vectorType.getElementType());
1437 auto n = std::min<size_t>(adaptor.getStaticPosition().size(),
1438 vectorType.getRank());
1439 inferredReturnTypes.push_back(VectorType::get(
1440 vectorType.getShape().drop_front(n), vectorType.getElementType(),
1441 vectorType.getScalableDims().drop_front(n)));
1446LogicalResult vector::ExtractOp::verify() {
1447 if (
auto resTy = dyn_cast<VectorType>(getResult().
getType()))
1448 if (resTy.getRank() == 0)
1450 "expected a scalar instead of a 0-d vector as the result type");
1453 auto dynamicMarkersCount =
1454 llvm::count_if(getStaticPosition(), ShapedType::isDynamic);
1455 if (
static_cast<size_t>(dynamicMarkersCount) != getDynamicPosition().size())
1457 "mismatch between dynamic and static positions (kDynamic marker but no "
1458 "corresponding dynamic position) -- this can only happen due to an "
1459 "incorrect fold/rewrite");
1460 auto position = getMixedPosition();
1461 if (position.size() >
static_cast<unsigned>(getSourceVectorType().getRank()))
1463 "expected position attribute of rank no greater than vector rank");
1464 for (
auto [idx, pos] : llvm::enumerate(position)) {
1465 if (
auto attr = dyn_cast<Attribute>(pos)) {
1466 int64_t constIdx = cast<IntegerAttr>(attr).getInt();
1468 constIdx, kPoisonIndex, getSourceVectorType().getDimSize(idx))) {
1469 return emitOpError(
"expected position attribute #")
1471 <<
" to be a non-negative integer smaller than the "
1472 "corresponding vector dimension or poison (-1)";
1479template <
typename IntType>
1481 return llvm::map_to_vector<4>(
1482 arrayAttr.getAsRange<IntegerAttr>(),
1483 [](IntegerAttr attr) { return static_cast<IntType>(attr.getInt()); });
1489 if (!extractOp.getSource().getDefiningOp<ExtractOp>())
1493 if (extractOp.hasDynamicPosition())
1497 ExtractOp currentOp = extractOp;
1499 globalPosition.append(extrPos.rbegin(), extrPos.rend());
1500 while (ExtractOp nextOp = currentOp.getSource().getDefiningOp<ExtractOp>()) {
1503 if (currentOp.hasDynamicPosition())
1506 globalPosition.append(extrPos.rbegin(), extrPos.rend());
1508 extractOp.setOperand(0, currentOp.getSource());
1511 std::reverse(globalPosition.begin(), globalPosition.end());
1512 extractOp.setStaticPosition(globalPosition);
1524class ExtractFromInsertTransposeChainState {
1526 ExtractFromInsertTransposeChainState(ExtractOp e);
1535 template <
typename ContainerA,
typename ContainerB>
1536 bool isContainedWithin(
const ContainerA &a,
const ContainerB &
b) {
1537 return a.size() <=
b.size() &&
1538 std::equal(a.begin(), a.begin() + a.size(),
b.begin());
1545 template <
typename ContainerA,
typename ContainerB>
1546 bool intersectsWhereNonNegative(
const ContainerA &a,
const ContainerB &
b) {
1547 for (
auto [elemA, elemB] : llvm::zip(a,
b)) {
1548 if (elemA < 0 || elemB < 0)
1559 return (sentinels == ArrayRef(extractPosition).drop_front(extractedRank));
1563 void updateStateForNextIteration(Value v) {
1570 LogicalResult handleTransposeOp();
1573 LogicalResult handleInsertOpWithMatchingPos(Value &res);
1588 LogicalResult handleInsertOpWithPrefixPos(Value &res);
1593 Value tryToFoldExtractOpInPlace(Value source);
1595 ExtractOp extractOp;
1597 int64_t extractedRank;
1599 InsertOp nextInsertOp;
1600 TransposeOp nextTransposeOp;
1610 SmallVector<int64_t> sentinels;
1611 SmallVector<int64_t> extractPosition;
1615ExtractFromInsertTransposeChainState::ExtractFromInsertTransposeChainState(
1617 : extractOp(e), vectorRank(extractOp.getSourceVectorType().getRank()),
1618 extractedRank(extractOp.getNumIndices()) {
1619 assert(vectorRank >= extractedRank &&
"Extracted position overflow");
1620 sentinels.reserve(vectorRank - extractedRank);
1621 for (
int64_t i = 0, e = vectorRank - extractedRank; i < e; ++i)
1622 sentinels.push_back(-(i + 1));
1624 extractOp.getStaticPosition().end());
1630LogicalResult ExtractFromInsertTransposeChainState::handleTransposeOp() {
1632 if (extractOp.hasDynamicPosition())
1635 if (!nextTransposeOp)
1638 nextTransposeOp.getPermutation(), extractOp.getContext()));
1645ExtractFromInsertTransposeChainState::handleInsertOpWithMatchingPos(
1648 if (extractOp.hasDynamicPosition() || nextInsertOp.hasDynamicPosition())
1651 ArrayRef<int64_t> insertedPos = nextInsertOp.getStaticPosition();
1652 if (insertedPos != llvm::ArrayRef(
extractPosition).take_front(extractedRank))
1655 res = nextInsertOp.getValueToStore();
1664ExtractFromInsertTransposeChainState::handleInsertOpWithPrefixPos(Value &res) {
1666 if (extractOp.hasDynamicPosition() || nextInsertOp.hasDynamicPosition())
1669 ArrayRef<int64_t> insertedPos = nextInsertOp.getStaticPosition();
1679 res = nextInsertOp.getValueToStore();
1687Value ExtractFromInsertTransposeChainState::tryToFoldExtractOpInPlace(
1690 if (extractOp.hasDynamicPosition())
1694 bool nothingToFold = (source == extractOp.getSource());
1695 if (nothingToFold || !canFold())
1699 OpBuilder
b(extractOp.getContext());
1700 extractOp.setStaticPosition(
1702 extractOp.getSourceMutable().assign(source);
1703 return extractOp.getResult();
1707Value ExtractFromInsertTransposeChainState::fold() {
1709 if (extractOp.hasDynamicPosition())
1712 Value valueToExtractFrom = extractOp.getSource();
1713 updateStateForNextIteration(valueToExtractFrom);
1714 while (nextInsertOp || nextTransposeOp) {
1717 if (succeeded(handleTransposeOp())) {
1718 valueToExtractFrom = nextTransposeOp.getVector();
1719 updateStateForNextIteration(valueToExtractFrom);
1725 if (succeeded(handleInsertOpWithMatchingPos(
result)))
1730 if (succeeded(handleInsertOpWithPrefixPos(
result)))
1731 return tryToFoldExtractOpInPlace(
result);
1735 ArrayRef<int64_t> insertedPos = nextInsertOp.getStaticPosition();
1741 valueToExtractFrom = nextInsertOp.getDest();
1742 updateStateForNextIteration(valueToExtractFrom);
1745 return tryToFoldExtractOpInPlace(valueToExtractFrom);
1750 auto hasZeroDimVectorType = [](
Type type) ->
bool {
1751 auto vecType = dyn_cast<VectorType>(type);
1752 return vecType && vecType.getRank() == 0;
1762 if (isa<BroadcastOp>(op))
1765 auto shapeCast = dyn_cast<ShapeCastOp>(op);
1773 VectorType srcType = shapeCast.getSourceVectorType();
1775 uint64_t srcRank = srcType.getRank();
1777 return dstShape.size() >= srcRank && dstShape.take_back(srcRank) == srcShape;
1803 Operation *defOp = extractOp.getSource().getDefiningOp();
1810 if (extractOp.getType() == input.
getType())
1816 auto inputType = llvm::dyn_cast<VectorType>(input.
getType());
1817 auto extractType = llvm::dyn_cast<VectorType>(extractOp.getType());
1818 unsigned inputRank = inputType ? inputType.getRank() : 0;
1819 unsigned broadcastRank = extractOp.getSourceVectorType().getRank();
1820 unsigned extractRank = extractType ? extractType.getRank() : 0;
1823 if (extractRank > inputRank)
1827 assert(inputType &&
"input must be a vector type because of previous checks");
1836 extractType.getShape() != inputShape.take_back(extractRank))
1841 unsigned deltaOverall = inputRank - extractRank;
1842 unsigned deltaBroadcast = broadcastRank - inputRank;
1846 for (
auto [i, size] : llvm::enumerate(inputShape.take_front(deltaOverall))) {
1847 newPositions[i] = size == 1 ? zero : oldPositions[i + deltaBroadcast];
1850 extractOp->setOperands(
1851 llvm::to_vector(llvm::concat<Value>(
ValueRange(input), dynPos)));
1852 extractOp.setStaticPosition(staticPos);
1853 return extractOp.getResult();
1869 if (extractOp.hasDynamicPosition())
1872 auto shuffleOp = extractOp.getSource().getDefiningOp<ShuffleOp>();
1877 if (shuffleOp.getResultVectorType().getRank() != 1)
1880 int64_t inputVecSize = shuffleOp.getV1().getType().getShape()[0];
1881 auto shuffleMask = shuffleOp.getMask();
1882 int64_t extractIdx = extractOp.getStaticPosition()[0];
1883 int64_t shuffleIdx = shuffleMask[extractIdx];
1886 if (shuffleIdx < inputVecSize) {
1887 extractOp.setOperand(0, shuffleOp.getV1());
1888 extractOp.setStaticPosition({shuffleIdx});
1890 extractOp.setOperand(0, shuffleOp.getV2());
1891 extractOp.setStaticPosition({shuffleIdx - inputVecSize});
1894 return extractOp.getResult();
1900 if (extractOp.hasDynamicPosition())
1903 auto shapeCastOp = extractOp.getSource().getDefiningOp<vector::ShapeCastOp>();
1908 auto getDimReverse = [](VectorType type,
int64_t n) {
1909 return type.getShape().take_back(n + 1).front();
1912 llvm::isa<VectorType>(extractOp.getType())
1913 ? llvm::cast<VectorType>(extractOp.getType()).getRank()
1915 if (destinationRank > shapeCastOp.getSourceVectorType().getRank())
1917 if (destinationRank > 0) {
1918 auto destinationType =
1919 llvm::cast<VectorType>(extractOp.getResult().getType());
1920 for (
int64_t i = 0; i < destinationRank; i++) {
1924 if (getDimReverse(shapeCastOp.getSourceVectorType(), i) !=
1925 getDimReverse(destinationType, i))
1932 std::reverse(extractedPos.begin(), extractedPos.end());
1935 for (
int64_t i = 0, e = extractedPos.size(); i < e; i++) {
1936 strides.push_back(stride);
1938 getDimReverse(extractOp.getSourceVectorType(), i + destinationRank);
1946 shapeCastOp.getSourceVectorType().getRank() - destinationRank;
1948 for (
int64_t i = 0; i < numDimension; i++) {
1949 newStrides.push_back(stride);
1951 getDimReverse(shapeCastOp.getSourceVectorType(), i + destinationRank);
1953 std::reverse(newStrides.begin(), newStrides.end());
1957 extractOp.setStaticPosition(newPosition);
1958 extractOp.setOperand(0, shapeCastOp.getSource());
1959 return extractOp.getResult();
1965 if (extractOp.hasDynamicPosition())
1968 auto extractStridedSliceOp =
1969 extractOp.getSource().getDefiningOp<vector::ExtractStridedSliceOp>();
1970 if (!extractStridedSliceOp)
1979 if (extractStridedSliceOp.hasNonUnitStrides())
1985 while (!sliceOffsets.empty()) {
1986 size_t lastOffset = sliceOffsets.size() - 1;
1987 if (sliceOffsets.back() != 0 ||
1988 extractStridedSliceOp.getType().getDimSize(lastOffset) !=
1989 extractStridedSliceOp.getSourceVectorType().getDimSize(lastOffset))
1991 sliceOffsets.pop_back();
1993 unsigned destinationRank = 0;
1994 if (
auto vecType = llvm::dyn_cast<VectorType>(extractOp.getType()))
1995 destinationRank = vecType.getRank();
1998 if (destinationRank > extractStridedSliceOp.getSourceVectorType().getRank() -
1999 sliceOffsets.size())
2003 assert(extractedPos.size() >= sliceOffsets.size());
2004 for (
size_t i = 0, e = sliceOffsets.size(); i < e; i++)
2005 extractedPos[i] = extractedPos[i] + sliceOffsets[i];
2006 extractOp.getSourceMutable().assign(extractStridedSliceOp.getSource());
2010 extractOp.setStaticPosition(extractedPos);
2011 return extractOp.getResult();
2017 if (extractOp.hasDynamicPosition())
2021 llvm::isa<VectorType>(extractOp.getType())
2022 ? llvm::cast<VectorType>(extractOp.getType()).getRank()
2024 auto insertOp = extractOp.getSource().getDefiningOp<InsertStridedSliceOp>();
2034 int64_t insertRankDiff = insertOp.getDestVectorType().getRank() -
2035 insertOp.getSourceVectorType().getRank();
2036 if (destinationRank > insertOp.getSourceVectorType().getRank())
2041 if (llvm::any_of(insertOp.getStrides(), [](
Attribute attr) {
2042 return llvm::cast<IntegerAttr>(attr).getInt() != 1;
2045 bool disjoint =
false;
2047 for (
unsigned dim = 0, e = extractOffsets.size(); dim < e; ++dim) {
2048 int64_t start = insertOffsets[dim];
2050 (dim < insertRankDiff)
2052 : insertOp.getSourceVectorType().getDimSize(dim - insertRankDiff);
2054 int64_t offset = extractOffsets[dim];
2056 if (start <= offset && offset < end) {
2057 if (dim >= insertRankDiff)
2058 offsetDiffs.push_back(offset - start);
2069 insertOp.getSourceVectorType().getRank() - destinationRank;
2070 for (
int64_t i = 0; i < destinationRank; i++) {
2071 if (insertOp.getSourceVectorType().getDimSize(i + srcRankDiff) !=
2072 insertOp.getDestVectorType().getDimSize(i + srcRankDiff +
2076 extractOp.getSourceMutable().assign(insertOp.getValueToStore());
2079 extractOp.setStaticPosition(offsetDiffs);
2080 return extractOp.getResult();
2084 insertOp = insertOp.getDest().getDefiningOp<InsertStridedSliceOp>();
2097 if (extractOp.hasDynamicPosition())
2101 auto fromElementsOp = extractOp.getSource().
getDefiningOp<FromElementsOp>();
2102 if (!fromElementsOp)
2106 auto vecType = llvm::cast<VectorType>(fromElementsOp.getType());
2107 if (vecType.isScalable())
2111 int64_t rank = vecType.getRank();
2113 if (extractOp.getType() != vecType.getElementType())
2116 "unexpected number of indices");
2121 for (
int i = rank - 1; i >= 0; --i) {
2122 flatIndex +=
indices[i] * stride;
2123 stride *= vecType.getDimSize(i);
2125 return fromElementsOp.getElements()[flatIndex];
2130template <
typename OpType,
typename AdaptorType>
2133 std::vector<int64_t> staticPosition = op.getStaticPosition().vec();
2134 OperandRange dynamicPosition = op.getDynamicPosition();
2137 if constexpr (std::is_same_v<OpType, ExtractOp>)
2138 vectorShape = op.getSourceVectorType().getShape();
2143 if (!dynamicPosition.size())
2150 bool opChange =
false;
2151 for (
unsigned i = 0, e = staticPosition.size(); i < e; ++i) {
2152 if (ShapedType::isStatic(staticPosition[i]))
2156 if (
auto attr = mlir::dyn_cast_if_present<IntegerAttr>(positionAttr)) {
2157 int64_t value = attr.getInt();
2161 staticPosition[i] = attr.getInt();
2166 operands.push_back(position);
2170 op.setStaticPosition(staticPosition);
2171 op.getOperation()->setOperands(operands);
2173 return op.getResult();
2183 if (!is_contained(staticPos, poisonVal))
2186 return ub::PoisonAttr::get(context);
2200 auto denseAttr = dyn_cast_if_present<DenseElementsAttr>(srcAttr);
2205 if (denseAttr.isSplat()) {
2207 if (
auto vecDstType = dyn_cast<VectorType>(extractOp.getType()))
2212 auto vecTy = cast<VectorType>(extractOp.getSourceVectorType());
2213 if (vecTy.isScalable())
2216 if (extractOp.hasDynamicPosition()) {
2231 copy(extractOp.getStaticPosition(), completePositions.begin());
2234 auto denseValuesBegin = denseAttr.value_begin<TypedAttr>() + startPos;
2237 if (
auto resVecTy = dyn_cast<VectorType>(extractOp.getType())) {
2239 denseValuesBegin, denseValuesBegin + resVecTy.getNumElements());
2242 newAttr = *denseValuesBegin;
2248OpFoldResult ExtractOp::fold(FoldAdaptor adaptor) {
2252 if (getNumIndices() == 0 && getSource().
getType() == getResult().
getType())
2259 SmallVector<Value> operands = {getSource()};
2263 getContext(), adaptor.getStaticPosition(), kPoisonIndex))
2269 if (
auto res = ExtractFromInsertTransposeChainState(*this).fold())
2284 return inplaceFolded;
2290class ExtractOpFromBroadcast final :
public OpRewritePattern<ExtractOp> {
2294 LogicalResult matchAndRewrite(ExtractOp extractOp,
2295 PatternRewriter &rewriter)
const override {
2298 VectorType outType = dyn_cast<VectorType>(extractOp.getType());
2304 BroadcastableToResult::Success)
2313class ExtractOpFromCreateMask final :
public OpRewritePattern<ExtractOp> {
2317 LogicalResult matchAndRewrite(ExtractOp extractOp,
2318 PatternRewriter &rewriter)
const override {
2320 extractOp.getSource().getDefiningOp<vector::CreateMaskOp>();
2324 VectorType extractedMaskType =
2325 llvm::dyn_cast<VectorType>(extractOp.getResult().getType());
2327 if (!extractedMaskType)
2330 auto maskOperands = createMaskOp.getOperands();
2331 ArrayRef<int64_t> extractOpPos = extractOp.getStaticPosition();
2332 VectorType maskType = createMaskOp.getVectorType();
2334 bool containsUnknownDims =
false;
2337 for (
size_t dimIdx = 0; !allFalse && dimIdx < extractOpPos.size();
2339 int64_t pos = extractOpPos[dimIdx];
2340 Value operand = maskOperands[dimIdx];
2341 auto constantOp = operand.
getDefiningOp<arith::ConstantOp>();
2344 containsUnknownDims =
true;
2348 int64_t createMaskBound =
2349 llvm::cast<IntegerAttr>(constantOp.getValue()).getInt();
2351 if (pos != ShapedType::kDynamic) {
2354 allFalse |= pos >= createMaskBound;
2355 }
else if (createMaskBound < maskType.getDimSize(dimIdx)) {
2359 containsUnknownDims =
true;
2366 }
else if (!containsUnknownDims) {
2368 extractOp, extractedMaskType,
2369 maskOperands.drop_front(extractOpPos.size()));
2378class ExtractOpFromConstantMask final :
public OpRewritePattern<ExtractOp> {
2382 LogicalResult matchAndRewrite(ExtractOp extractOp,
2383 PatternRewriter &rewriter)
const override {
2384 auto constantMaskOp =
2385 extractOp.getSource().getDefiningOp<vector::ConstantMaskOp>();
2386 if (!constantMaskOp)
2389 Type resultType = extractOp.getResult().getType();
2390 auto extractedMaskType = dyn_cast<VectorType>(resultType);
2392 ArrayRef<int64_t> extractOpPos = extractOp.getStaticPosition();
2393 ArrayRef<int64_t> maskDimSizes = constantMaskOp.getMaskDimSizes();
2395 VectorType maskType = constantMaskOp.getVectorType();
2398 for (
size_t dimIdx = 0; dimIdx < extractOpPos.size(); dimIdx++) {
2399 int64_t pos = extractOpPos[dimIdx];
2400 if (pos == ShapedType::kDynamic) {
2403 if (maskDimSizes[dimIdx] == maskType.getDimSize(dimIdx))
2412 if (pos >= maskDimSizes[dimIdx]) {
2413 if (extractedMaskType) {
2425 if (extractedMaskType) {
2429 extractOp, extractedMaskType,
2430 maskDimSizes.drop_front(extractOpPos.size()));
2443LogicalResult foldExtractFromShapeCastToShapeCast(ExtractOp extractOp,
2444 PatternRewriter &rewriter) {
2445 auto castOp = extractOp.getSource().getDefiningOp<ShapeCastOp>();
2449 VectorType sourceType = castOp.getSourceVectorType();
2450 auto targetType = dyn_cast<VectorType>(extractOp.getResult().getType());
2454 if (sourceType.getNumElements() != targetType.getNumElements())
2458 castOp.getSource());
2468LogicalResult foldExtractFromFromElements(ExtractOp extractOp,
2469 PatternRewriter &rewriter) {
2471 if (extractOp.hasDynamicPosition())
2475 auto resultType = dyn_cast<VectorType>(extractOp.getType());
2480 auto fromElementsOp = extractOp.getSource().getDefiningOp<FromElementsOp>();
2481 if (!fromElementsOp)
2483 VectorType inputType = fromElementsOp.getType();
2486 if (resultType.isScalable() || inputType.isScalable())
2491 SmallVector<int64_t> firstElementPos =
2492 llvm::to_vector(extractOp.getStaticPosition());
2493 firstElementPos.append(resultType.getRank(), 0);
2496 for (int64_t i = inputType.getRank() - 1; i >= 0; --i) {
2497 flatIndex += firstElementPos[i] * stride;
2498 stride *= inputType.getDimSize(i);
2503 extractOp, resultType,
2504 fromElementsOp.getElements().slice(flatIndex,
2505 resultType.getNumElements()));
2517struct ExtractToShapeCast final : OpRewritePattern<vector::ExtractOp> {
2519 LogicalResult matchAndRewrite(vector::ExtractOp extractOp,
2520 PatternRewriter &rewriter)
const override {
2521 VectorType sourceType = extractOp.getSourceVectorType();
2522 VectorType outType = dyn_cast<VectorType>(extractOp.getType());
2526 if (sourceType.getNumElements() != outType.getNumElements())
2528 extractOp,
"extract to vector with fewer elements");
2532 if (llvm::any_of(extractOp.getMixedPosition(),
2533 [](OpFoldResult v) { return !isConstantIntValue(v, 0); }))
2535 "leaving for extract poison folder");
2538 extractOp.getSource());
2559struct FoldExtractFromInsertUnitDim final
2560 : OpRewritePattern<vector::ExtractOp> {
2563 LogicalResult matchAndRewrite(vector::ExtractOp extractOp,
2564 PatternRewriter &rewriter)
const override {
2565 if (extractOp.hasDynamicPosition())
2568 auto insertOp = extractOp.getSource().getDefiningOp<vector::InsertOp>();
2569 if (!insertOp || insertOp.hasDynamicPosition())
2572 ArrayRef<int64_t> extractPos = extractOp.getStaticPosition();
2573 ArrayRef<int64_t> insertPos = insertOp.getStaticPosition();
2576 if (extractPos.size() >= insertPos.size() ||
2577 extractPos != insertPos.take_front(extractPos.size()))
2583 auto srcVecType = extractOp.getSourceVectorType();
2584 for (int64_t i = extractPos.size(), e = srcVecType.getRank(); i < e; ++i)
2585 if (srcVecType.getDimSize(i) != 1)
2588 Value
inserted = insertOp.getValueToStore();
2589 Type extractedType = extractOp.getResult().getType();
2590 if (isa<VectorType>(
inserted.getType())) {
2597 extractOp, extractOp.getResult().
getType(),
2598 insertOp.getValueToStore());
2606void ExtractOp::getCanonicalizationPatterns(RewritePatternSet &results,
2607 MLIRContext *context) {
2608 results.
add<ExtractOpFromBroadcast, ExtractOpFromCreateMask,
2609 ExtractOpFromConstantMask, ExtractToShapeCast,
2610 FoldExtractFromInsertUnitDim>(context);
2611 results.
add(foldExtractFromShapeCastToShapeCast);
2612 results.
add(foldExtractFromFromElements);
2617 for (
auto attr : arrayAttr)
2618 results.push_back(llvm::cast<IntegerAttr>(attr).getInt());
2625std::optional<SmallVector<int64_t, 4>> FMAOp::getShapeForUnroll() {
2636 if (operands.empty())
2639 return llvm::all_of(operands, [&](
Value operand) {
2641 return currentDef == defOp;
2659 auto fromElementsOp =
2660 toElementsOp.getSource().getDefiningOp<FromElementsOp>();
2661 if (!fromElementsOp)
2664 llvm::append_range(results, fromElementsOp.getElements());
2681 auto bcastOp = toElementsOp.getSource().getDefiningOp<BroadcastOp>();
2685 if (isa<VectorType>(bcastOp.getSource().getType()))
2688 auto resultVecType = cast<VectorType>(toElementsOp.getSource().getType());
2690 Value scalar = bcastOp.getSource();
2691 results.assign(resultVecType.getNumElements(), scalar);
2695LogicalResult ToElementsOp::fold(FoldAdaptor adaptor,
2696 SmallVectorImpl<OpFoldResult> &results) {
2701 if (
auto shapeCast = getSource().getDefiningOp<ShapeCastOp>()) {
2702 setOperand(shapeCast.getSource());
2710ToElementsOp::inferReturnTypes(MLIRContext *ctx, std::optional<Location> loc,
2711 ToElementsOp::Adaptor adaptor,
2712 SmallVectorImpl<Type> &inferredReturnTypes) {
2713 auto vecType = cast<VectorType>(adaptor.getSource().getType());
2714 Type elType = vecType.getElementType();
2715 inferredReturnTypes.append(vecType.getNumElements(), elType);
2737 auto bcastOp = toElementsOp.getSource().getDefiningOp<BroadcastOp>();
2742 auto srcType = dyn_cast<VectorType>(bcastOp.getSource().getType());
2746 auto dstType = cast<VectorType>(toElementsOp.getSource().getType());
2751 int64_t dstRank = dstShape.size();
2752 int64_t srcRank = srcShape.size();
2755 auto srcElems = vector::ToElementsOp::create(
2756 rewriter, toElementsOp.getLoc(), bcastOp.getSource());
2758 int64_t dstCount = llvm::product_of(dstShape);
2761 replacements.reserve(dstCount);
2786 for (
int64_t lin = 0; lin < dstCount; ++lin) {
2789 for (
int64_t k = 0; k < srcRank; ++k)
2790 srcIdx[k] = (srcShape[k] == 1) ? 0 : dstIdx[dstRank - srcRank + k];
2793 replacements.push_back(srcElems.getResult(srcLin));
2796 rewriter.
replaceOp(toElementsOp, replacements);
2801void ToElementsOp::getCanonicalizationPatterns(RewritePatternSet &results,
2802 MLIRContext *context) {
2803 results.
add<ToElementsOfBroadcast>(context);
2823 OperandRange fromElemsOperands = fromElementsOp.getElements();
2824 if (fromElemsOperands.empty())
2827 auto toElementsOp = fromElemsOperands[0].getDefiningOp<ToElementsOp>();
2835 Value toElementsInput = toElementsOp.getSource();
2836 if (fromElementsOp.getType() == toElementsInput.
getType() &&
2837 llvm::equal(fromElemsOperands, toElementsOp.getResults())) {
2838 return toElementsInput;
2858 if (llvm::any_of(elements, [](
Attribute attr) {
2864 auto destVecType = fromElementsOp.getDest().getType();
2865 auto destEltType = destVecType.getElementType();
2866 if (!destEltType.isIntOrIndexOrFloat() && !isa<ComplexType>(destEltType))
2871 auto convertedElements = llvm::map_to_vector(elements, [&](
Attribute attr) {
2878OpFoldResult FromElementsOp::fold(FoldAdaptor adaptor) {
2895 if (!llvm::all_equal(fromElementsOp.getElements()))
2898 fromElementsOp, fromElementsOp.getType(),
2899 fromElementsOp.getElements().front());
2927 LogicalResult matchAndRewrite(FromElementsOp fromElements,
2931 if (fromElements.getType().getNumElements() == 1)
2942 for (
auto [insertIndex, element] :
2943 llvm::enumerate(fromElements.getElements())) {
2946 auto extractOp = element.getDefiningOp<vector::ExtractOp>();
2949 "element not from vector.extract");
2954 if (insertIndex == 0) {
2955 source = extractOp.getSource();
2956 }
else if (extractOp.getSource() != source) {
2958 "element from different vector");
2962 int64_t rank = position.size();
2963 assert(rank == source.getType().getRank() &&
2964 "scalar extract must have full rank position");
2975 if (insertIndex == 0) {
2976 const int64_t numElms = fromElements.getType().getNumElements();
2979 while (
index > 0 && position[
index - 1] == 0 &&
2980 numSuffixElms < numElms) {
2981 numSuffixElms *= source.getType().getDimSize(
index - 1);
2984 if (numSuffixElms != numElms) {
2986 fromElements,
"elements do not form a suffix of source");
2988 expectedPosition = llvm::to_vector(position);
2989 combinedPosition = position.drop_back(rank -
index);
2993 else if (expectedPosition != position) {
2995 fromElements,
"elements not in ascending order (static order)");
2997 increment(expectedPosition, source.getType().getShape());
3000 auto extracted = rewriter.
createOrFold<vector::ExtractOp>(
3001 fromElements.getLoc(), source, combinedPosition);
3004 fromElements, fromElements.getType(), extracted);
3012 for (
int dim : llvm::reverse(llvm::seq<int>(0,
indices.size()))) {
3031void BroadcastOp::inferResultRanges(ArrayRef<ConstantIntRanges> argRanges,
3033 setResultRanges(getResult(), argRanges.front());
3036std::optional<SmallVector<int64_t, 4>> BroadcastOp::getShapeForUnroll() {
3037 return llvm::to_vector<4>(getResultVectorType().
getShape());
3042static llvm::SetVector<int64_t>
3045 int64_t rankDiff = dstShape.size() - srcShape.size();
3048 for (
auto [s1, s2] :
3049 llvm::zip_equal(srcShape, dstShape.drop_front(rankDiff))) {
3051 assert(s1 == 1 &&
"expected \"dim-1\" broadcasting");
3059llvm::SetVector<int64_t> BroadcastOp::computeBroadcastedUnitDims() {
3061 auto srcVectorType = llvm::dyn_cast<VectorType>(getSourceType());
3064 return ::computeBroadcastedUnitDims(srcVectorType.getShape(),
3080Value BroadcastOp::createOrFoldBroadcastOp(
3081 OpBuilder &
b, Value value, ArrayRef<int64_t> dstShape,
3082 const llvm::SetVector<int64_t> &broadcastedDims) {
3083 assert(!dstShape.empty() &&
"unexpected empty dst shape");
3086 SmallVector<int64_t> checkShape;
3087 for (
int i = 0, e = dstShape.size(); i < e; ++i) {
3088 if (broadcastedDims.contains(i))
3090 checkShape.push_back(dstShape[i]);
3092 assert(broadcastedDims.size() == dstShape.size() - checkShape.size() &&
3093 "ill-formed broadcastedDims contains values not confined to "
3096 Location loc = value.
getLoc();
3098 VectorType srcVectorType = llvm::dyn_cast<VectorType>(value.
getType());
3099 VectorType dstVectorType = VectorType::get(dstShape, elementType);
3102 if (!srcVectorType) {
3103 assert(checkShape.empty() &&
3104 "ill-formed createOrFoldBroadcastOp arguments");
3105 return b.createOrFold<vector::BroadcastOp>(loc, dstVectorType, value);
3108 assert(srcVectorType.getShape().equals(checkShape) &&
3109 "ill-formed createOrFoldBroadcastOp arguments");
3119 SmallVector<int64_t> broadcastShape, permutation(dstShape.size(), -1);
3120 broadcastShape.reserve(dstShape.size());
3136 int64_t nextSrcShapeDim = broadcastedDims.size();
3137 for (int64_t i = 0, e = dstShape.size(); i < e; ++i) {
3138 if (broadcastedDims.contains(i)) {
3143 broadcastShape.push_back(dstShape[i]);
3144 permutation[i] = broadcastShape.size() - 1;
3150 permutation[i] = nextSrcShapeDim++;
3154 llvm::append_range(broadcastShape, srcVectorType.getShape());
3159 "unexpected \"dim-1\" broadcast");
3161 VectorType broadcastType = VectorType::get(broadcastShape, elementType);
3163 vector::BroadcastableToResult::Success &&
3164 "must be broadcastable");
3165 Value res =
b.createOrFold<vector::BroadcastOp>(loc, broadcastType, value);
3168 for (int64_t i = 0, e = permutation.size(); i < e; ++i)
3169 if (permutation[i] != i)
3170 return b.createOrFold<vector::TransposeOp>(loc, res, permutation);
3175LogicalResult BroadcastOp::verify() {
3176 std::pair<VectorDim, VectorDim> mismatchingDims;
3178 getSourceType(), getResultVectorType(), &mismatchingDims);
3179 if (res == BroadcastableToResult::Success)
3181 if (res == BroadcastableToResult::SourceRankHigher)
3182 return emitOpError(
"source rank higher than destination rank");
3183 if (res == BroadcastableToResult::DimensionMismatch) {
3184 return emitOpError(
"dimension mismatch (")
3185 << (mismatchingDims.first.isScalable ?
"[" :
"")
3186 << mismatchingDims.first.dim
3187 << (mismatchingDims.first.isScalable ?
"]" :
"") <<
" vs. "
3188 << (mismatchingDims.second.isScalable ?
"[" :
"")
3189 << mismatchingDims.second.dim
3190 << (mismatchingDims.second.isScalable ?
"]" :
"") <<
")";
3192 if (res == BroadcastableToResult::SourceTypeNotAVector)
3193 return emitOpError(
"source type is not a vector");
3194 llvm_unreachable(
"unexpected vector.broadcast op error");
3201 auto srcShapeCast = broadcastOp.getSource().getDefiningOp<ShapeCastOp>();
3205 VectorType srcType = srcShapeCast.getSourceVectorType();
3206 VectorType destType = broadcastOp.getResultVectorType();
3214 srcShapeCast.getResultVectorType().getShape();
3217 unsigned numTrailingDims = std::min(srcShape.size(), shapecastShape.size());
3218 if (!llvm::equal(srcShape.take_back(numTrailingDims),
3219 shapecastShape.take_back(numTrailingDims)))
3222 assert(all_of(srcShape.drop_back(numTrailingDims),
3223 [](
int64_t E) { return E == 1; }) &&
3224 all_of(shapecastShape.drop_back(numTrailingDims),
3225 [](
int64_t E) { return E == 1; }) &&
3226 "ill-formed shape_cast");
3228 broadcastOp.getSourceMutable().assign(srcShapeCast.getSource());
3232OpFoldResult BroadcastOp::fold(FoldAdaptor adaptor) {
3233 if (getSourceType() == getResultVectorType())
3238 if (!adaptor.getSource())
3240 auto vectorType = getResultVectorType();
3241 if (
auto attr = llvm::dyn_cast<IntegerAttr>(adaptor.getSource())) {
3242 if (vectorType.getElementType() != attr.getType())
3246 if (
auto attr = llvm::dyn_cast<FloatAttr>(adaptor.getSource())) {
3247 if (vectorType.getElementType() != attr.getType())
3251 if (
auto attr = llvm::dyn_cast<SplatElementsAttr>(adaptor.getSource()))
3261struct BroadcastFolder :
public OpRewritePattern<BroadcastOp> {
3264 LogicalResult matchAndRewrite(BroadcastOp broadcastOp,
3265 PatternRewriter &rewriter)
const override {
3266 auto srcBroadcast = broadcastOp.getSource().getDefiningOp<BroadcastOp>();
3270 broadcastOp.getResultVectorType(),
3271 srcBroadcast.getSource());
3284struct BroadcastToShapeCast final
3285 :
public OpRewritePattern<vector::BroadcastOp> {
3287 LogicalResult matchAndRewrite(vector::BroadcastOp
broadcast,
3288 PatternRewriter &rewriter)
const override {
3290 auto sourceType = dyn_cast<VectorType>(
broadcast.getSourceType());
3293 broadcast,
"source is a scalar, shape_cast doesn't support scalar");
3297 if (sourceType.getNumElements() != outType.getNumElements()) {
3299 broadcast,
"broadcast to a greater number of elements");
3309void BroadcastOp::getCanonicalizationPatterns(RewritePatternSet &results,
3310 MLIRContext *context) {
3311 results.
add<BroadcastFolder, BroadcastToShapeCast>(context);
3318LogicalResult ShuffleOp::verify() {
3319 VectorType resultType = getResultVectorType();
3320 VectorType v1Type = getV1VectorType();
3321 VectorType v2Type = getV2VectorType();
3323 int64_t resRank = resultType.getRank();
3324 int64_t v1Rank = v1Type.getRank();
3325 int64_t v2Rank = v2Type.getRank();
3326 bool wellFormed0DCase = v1Rank == 0 && v2Rank == 0 && resRank == 1;
3327 bool wellFormedNDCase = v1Rank == resRank && v2Rank == resRank;
3328 if (!wellFormed0DCase && !wellFormedNDCase)
3329 return emitOpError(
"rank mismatch");
3332 for (int64_t r = 1; r < v1Rank; ++r) {
3333 int64_t resDim = resultType.getDimSize(r);
3334 int64_t v1Dim = v1Type.getDimSize(r);
3335 int64_t v2Dim = v2Type.getDimSize(r);
3336 if (resDim != v1Dim || v1Dim != v2Dim)
3337 return emitOpError(
"dimension mismatch");
3340 ArrayRef<int64_t> mask = getMask();
3341 int64_t maskLength = mask.size();
3342 if (maskLength <= 0)
3343 return emitOpError(
"invalid mask length");
3344 if (maskLength != resultType.getDimSize(0))
3345 return emitOpError(
"mask length mismatch");
3347 int64_t indexSize = (v1Type.getRank() == 0 ? 1 : v1Type.getDimSize(0)) +
3348 (v2Type.getRank() == 0 ? 1 : v2Type.getDimSize(0));
3349 for (
auto [idx, maskPos] : llvm::enumerate(mask)) {
3351 return emitOpError(
"mask index #") << (idx + 1) <<
" out of range";
3357ShuffleOp::inferReturnTypes(MLIRContext *, std::optional<Location> loc,
3358 ShuffleOp::Adaptor adaptor,
3359 SmallVectorImpl<Type> &inferredReturnTypes) {
3360 auto v1Type = llvm::dyn_cast<VectorType>(adaptor.getV1().getType());
3364 auto v1Rank = v1Type.getRank();
3367 SmallVector<int64_t, 4> shape;
3368 shape.reserve(v1Rank);
3369 shape.push_back(std::max<size_t>(1, adaptor.getMask().size()));
3372 llvm::append_range(shape, v1Type.getShape().drop_front());
3373 inferredReturnTypes.push_back(
3374 VectorType::get(shape, v1Type.getElementType()));
3378template <
typename T>
3381 return idxArr.size() == width && llvm::all_of(idxArr, [&expected](T value) {
3382 return value == expected++;
3389 auto v1Type = op.getV1VectorType();
3390 auto v2Type = op.getV2VectorType();
3391 auto mask = op.getMask();
3404 if (!isV1Poison && !isV2Poison)
3407 int64_t v1Size = op.getV1VectorType().getDimSize(0);
3408 bool changed =
false;
3410 for (
int64_t &idx : newMask) {
3411 if (idx == ShuffleOp::kPoisonIndex)
3413 if ((isV1Poison && idx < v1Size) || (isV2Poison && idx >= v1Size)) {
3414 idx = ShuffleOp::kPoisonIndex;
3422 op.setMask(newMask);
3423 return op.getResult();
3432 return ub::PoisonAttr::get(context);
3439 auto v1Type = op.getV1VectorType();
3440 if (v1Type.getRank() != 1)
3452 auto v2DenseAttr = dyn_cast<DenseElementsAttr>(v2Attr);
3455 v2Elements = to_vector(v2DenseAttr.getValues<
Attribute>());
3456 poisonElement = v2Elements[0];
3459 auto v1DenseAttr = dyn_cast<DenseElementsAttr>(v1Attr);
3462 v1Elements = to_vector(v1DenseAttr.getValues<
Attribute>());
3463 poisonElement = v1Elements[0];
3468 int64_t v1Size = v1Type.getDimSize(0);
3469 for (
int64_t maskIdx : mask) {
3472 if (maskIdx == ShuffleOp::kPoisonIndex) {
3473 indexedElm = poisonElement;
3475 if (maskIdx < v1Size)
3476 indexedElm = isV1Poison ? poisonElement : v1Elements[maskIdx];
3478 indexedElm = isV2Poison ? poisonElement : v2Elements[maskIdx - v1Size];
3481 results.push_back(indexedElm);
3487OpFoldResult vector::ShuffleOp::fold(FoldAdaptor adaptor) {
3488 auto v1Type = getV1VectorType();
3490 assert(!v1Type.isScalable() && !getV2VectorType().isScalable() &&
3491 "Vector shuffle does not support scalable vectors");
3495 if (v1Type.getRank() == 0)
3503 Attribute v1Attr = adaptor.getV1(), v2Attr = adaptor.getV2();
3504 if (!v1Attr || !v2Attr)
3519struct Canonicalize0DShuffleOp :
public OpRewritePattern<ShuffleOp> {
3522 LogicalResult matchAndRewrite(ShuffleOp shuffleOp,
3523 PatternRewriter &rewriter)
const override {
3524 VectorType v1VectorType = shuffleOp.getV1VectorType();
3525 ArrayRef<int64_t> mask = shuffleOp.getMask();
3526 if (v1VectorType.getRank() > 0)
3528 if (mask.size() != 1)
3530 VectorType resType = VectorType::Builder(v1VectorType).setShape({1});
3548static Value getScalarSplatSource(Value value) {
3554 auto broadcast = dyn_cast<vector::BroadcastOp>(defOp);
3561 if (isa<VectorType>(
broadcast.getSourceType()))
3569class ShuffleSplat final :
public OpRewritePattern<ShuffleOp> {
3573 LogicalResult matchAndRewrite(ShuffleOp op,
3574 PatternRewriter &rewriter)
const override {
3575 Value splat = getScalarSplatSource(op.getV1());
3576 if (!splat || getScalarSplatSource(op.getV2()) != splat)
3586class ShuffleInterleave :
public OpRewritePattern<ShuffleOp> {
3590 LogicalResult matchAndRewrite(ShuffleOp op,
3591 PatternRewriter &rewriter)
const override {
3592 VectorType resultType = op.getResultVectorType();
3593 if (resultType.isScalable())
3595 op,
"ShuffleOp can't represent a scalable interleave");
3597 if (resultType.getRank() != 1)
3599 op,
"ShuffleOp can't represent an n-D interleave");
3601 VectorType sourceType = op.getV1VectorType();
3602 if (sourceType != op.getV2VectorType() ||
3603 sourceType.getNumElements() * 2 != resultType.getNumElements()) {
3605 op,
"ShuffleOp types don't match an interleave");
3608 ArrayRef<int64_t> shuffleMask = op.getMask();
3609 int64_t resultVectorSize = resultType.getNumElements();
3610 for (
int i = 0, e = resultVectorSize / 2; i < e; ++i) {
3611 int64_t maskValueA = shuffleMask[i * 2];
3612 int64_t maskValueB = shuffleMask[(i * 2) + 1];
3613 if (maskValueA != i || maskValueB != (resultVectorSize / 2) + i)
3615 "ShuffleOp mask not interleaving");
3631class FoldUnusedShuffleOperand final :
public OpRewritePattern<ShuffleOp> {
3635 LogicalResult matchAndRewrite(ShuffleOp op,
3636 PatternRewriter &rewriter)
const override {
3638 if (llvm::all_of(op.getMask(), [](int64_t mask) {
3639 return mask == ShuffleOp::kPoisonIndex;
3646 auto replaceOperandWithPoison = [&](OpOperand &operand) {
3649 Value poison = ub::PoisonOp::create(rewriter, op.getLoc(),
3658 int64_t leadingV1Size = op.getV1VectorType().getRank() > 0
3659 ? op.getV1VectorType().getDimSize(0)
3661 bool isV1Used = llvm::any_of(op.getMask(), [&](int64_t mask) {
3662 return mask != ShuffleOp::kPoisonIndex && mask < leadingV1Size;
3664 if (!isV1Used && succeeded(replaceOperandWithPoison(op.getV1Mutable())))
3668 bool isV2Used = llvm::any_of(op.getMask(), [&](int64_t mask) {
3669 return mask != ShuffleOp::kPoisonIndex && mask >= leadingV1Size;
3671 if (!isV2Used && succeeded(replaceOperandWithPoison(op.getV2Mutable())))
3679void ShuffleOp::getCanonicalizationPatterns(RewritePatternSet &results,
3680 MLIRContext *context) {
3681 results.
add<ShuffleSplat, ShuffleInterleave, Canonicalize0DShuffleOp,
3682 FoldUnusedShuffleOperand>(context);
3689void vector::InsertOp::inferResultRanges(ArrayRef<ConstantIntRanges> argRanges,
3691 setResultRanges(getResult(), argRanges[0].rangeUnion(argRanges[1]));
3694void vector::InsertOp::build(OpBuilder &builder, OperationState &
result,
3695 Value source, Value dest) {
3696 auto vectorTy = cast<VectorType>(dest.
getType());
3697 build(builder,
result, source, dest,
3698 SmallVector<int64_t>(vectorTy.getRank(), 0));
3701void vector::InsertOp::build(OpBuilder &builder, OperationState &
result,
3702 Value source, Value dest, int64_t position) {
3703 build(builder,
result, source, dest, ArrayRef<int64_t>{position});
3706void vector::InsertOp::build(OpBuilder &builder, OperationState &
result,
3707 Value source, Value dest, OpFoldResult position) {
3708 build(builder,
result, source, dest, ArrayRef<OpFoldResult>{position});
3711void vector::InsertOp::build(OpBuilder &builder, OperationState &
result,
3712 Value source, Value dest,
3713 ArrayRef<int64_t> position) {
3714 SmallVector<OpFoldResult> posVals;
3715 posVals.reserve(position.size());
3716 llvm::transform(position, std::back_inserter(posVals),
3718 build(builder,
result, source, dest, posVals);
3721void vector::InsertOp::build(OpBuilder &builder, OperationState &
result,
3722 Value source, Value dest,
3723 ArrayRef<OpFoldResult> position) {
3724 SmallVector<int64_t> staticPos;
3725 SmallVector<Value> dynamicPos;
3727 build(builder,
result, source, dest, dynamicPos,
3731LogicalResult InsertOp::verify() {
3732 if (
auto srcTy = dyn_cast<VectorType>(getValueToStoreType()))
3733 if (srcTy.getRank() == 0)
3735 "expected a scalar instead of a 0-d vector as the source operand");
3737 SmallVector<OpFoldResult> position = getMixedPosition();
3738 auto destVectorType = getDestVectorType();
3739 if (position.size() >
static_cast<unsigned>(destVectorType.getRank()))
3741 "expected position attribute of rank no greater than dest vector rank");
3742 auto srcVectorType = llvm::dyn_cast<VectorType>(getValueToStoreType());
3743 if (srcVectorType &&
3744 (
static_cast<unsigned>(srcVectorType.getRank()) + position.size() !=
3745 static_cast<unsigned>(destVectorType.getRank())))
3746 return emitOpError(
"expected position attribute rank + source rank to "
3747 "match dest vector rank");
3748 if (!srcVectorType &&
3749 (position.size() !=
static_cast<unsigned>(destVectorType.getRank())))
3751 "expected position attribute rank to match the dest vector rank");
3752 for (
auto [idx, pos] : llvm::enumerate(position)) {
3753 if (
auto attr = dyn_cast<Attribute>(pos)) {
3754 int64_t constIdx = cast<IntegerAttr>(attr).getInt();
3756 destVectorType.getDimSize(idx))) {
3757 return emitOpError(
"expected position attribute #")
3759 <<
" to be a non-negative integer smaller than the "
3761 "dest vector dimension";
3774 assert(positions.size() <= completePositions.size() &&
3775 "positions size must be less than or equal to destTy rank");
3776 copy(positions, completePositions.begin());
3784class InsertToBroadcast final :
public OpRewritePattern<InsertOp> {
3788 LogicalResult matchAndRewrite(InsertOp insertOp,
3789 PatternRewriter &rewriter)
const override {
3791 llvm::dyn_cast<VectorType>(insertOp.getValueToStoreType());
3792 if (!srcVecType || insertOp.getDestVectorType().getNumElements() !=
3793 srcVecType.getNumElements())
3796 insertOp, insertOp.getDestVectorType(), insertOp.getValueToStore());
3802class InsertSplatToSplat final :
public OpRewritePattern<InsertOp> {
3806 LogicalResult matchAndRewrite(InsertOp op,
3807 PatternRewriter &rewriter)
const override {
3809 Value splat = getScalarSplatSource(op.getValueToStore());
3810 if (!splat || getScalarSplatSource(op.getDest()) != splat)
3838class InsertChainFullyInitialized final :
public OpRewritePattern<InsertOp> {
3841 LogicalResult matchAndRewrite(InsertOp op,
3842 PatternRewriter &rewriter)
const override {
3844 VectorType destTy = op.getDestVectorType();
3845 if (destTy.isScalable())
3848 for (Operation *user : op.getResult().getUsers())
3849 if (
auto insertOp = dyn_cast<InsertOp>(user))
3850 if (insertOp.getDest() == op.getResult())
3853 InsertOp currentOp = op;
3854 SmallVector<InsertOp> chainInsertOps;
3857 if (currentOp.hasDynamicPosition())
3860 chainInsertOps.push_back(currentOp);
3861 currentOp = currentOp.getDest().getDefiningOp<InsertOp>();
3864 if (currentOp && !currentOp->hasOneUse())
3868 int64_t vectorSize = destTy.getNumElements();
3869 int64_t initializedCount = 0;
3870 SmallVector<bool> initializedDestIdxs(vectorSize,
false);
3871 SmallVector<int64_t> pendingInsertPos;
3872 SmallVector<int64_t> pendingInsertSize;
3873 SmallVector<Value> pendingInsertValues;
3875 for (
auto insertOp : chainInsertOps) {
3877 if (is_contained(insertOp.getStaticPosition(), InsertOp::kPoisonIndex))
3881 int64_t insertBeginPosition =
3886 int64_t insertSize = 1;
3887 if (
auto srcVectorType =
3888 llvm::dyn_cast<VectorType>(insertOp.getValueToStoreType()))
3889 insertSize = srcVectorType.getNumElements();
3891 assert(insertBeginPosition + insertSize <= vectorSize &&
3892 "insert would overflow the vector");
3894 for (
auto index : llvm::seq<int64_t>(insertBeginPosition,
3895 insertBeginPosition + insertSize)) {
3896 if (initializedDestIdxs[index])
3898 initializedDestIdxs[index] =
true;
3904 pendingInsertPos.push_back(insertBeginPosition);
3905 pendingInsertSize.push_back(insertSize);
3906 pendingInsertValues.push_back(insertOp.getValueToStore());
3908 if (initializedCount == vectorSize)
3913 if (initializedCount != vectorSize)
3916 SmallVector<Value> elements(vectorSize);
3917 for (
auto [insertBeginPosition, insertSize, valueToStore] :
3918 llvm::reverse(llvm::zip(pendingInsertPos, pendingInsertSize,
3919 pendingInsertValues))) {
3920 auto srcVectorType = llvm::dyn_cast<VectorType>(valueToStore.getType());
3922 if (!srcVectorType) {
3923 elements[insertBeginPosition] = valueToStore;
3927 Repeated<Type> elementToInsertTypes(insertSize,
3928 srcVectorType.getElementType());
3930 auto elementsToInsert = vector::ToElementsOp::create(
3931 rewriter, op.getLoc(), elementToInsertTypes, valueToStore);
3932 for (int64_t linearIdx = 0; linearIdx < insertSize; linearIdx++) {
3933 elements[insertBeginPosition + linearIdx] =
3934 elementsToInsert.getResult(linearIdx);
3948 int64_t maxVectorSizeFoldThreshold) {
3949 if (insertOp.hasDynamicPosition())
3952 auto denseDst = llvm::dyn_cast_if_present<DenseElementsAttr>(dstAttr);
3960 VectorType destTy = insertOp.getDestVectorType();
3961 if (destTy.isScalable())
3965 if (destTy.getNumElements() > maxVectorSizeFoldThreshold &&
3966 !insertOp->hasOneUse())
3971 if (is_contained(insertOp.getStaticPosition(), InsertOp::kPoisonIndex))
3978 Type destEltType = destTy.getElementType();
3982 if (
auto denseSource = llvm::dyn_cast<DenseElementsAttr>(srcAttr)) {
3983 for (
auto value : denseSource.getValues<
Attribute>())
3989 auto allValues = llvm::to_vector(denseDst.getValues<
Attribute>());
3990 copy(insertedValues, allValues.begin() + insertBeginPosition);
3999 auto destInsert = insertOp.getDest().
getDefiningOp<InsertOp>();
4003 if (insertOp.getMixedPosition() != destInsert.getMixedPosition())
4006 insertOp.
setOperand(1, destInsert.getDest());
4007 return insertOp.getResult();
4010void InsertOp::getCanonicalizationPatterns(RewritePatternSet &results,
4011 MLIRContext *context) {
4012 results.
add<InsertToBroadcast, BroadcastFolder, InsertSplatToSplat,
4013 InsertChainFullyInitialized>(context);
4016OpFoldResult InsertOp::fold(FoldAdaptor adaptor) {
4019 constexpr int64_t vectorSizeFoldThreshold = 256;
4023 if (getNumIndices() == 0 && getValueToStoreType() ==
getType())
4024 return getValueToStore();
4028 SmallVector<Value> operands = {getValueToStore(), getDest()};
4034 getContext(), adaptor.getStaticPosition(), kPoisonIndex))
4037 *
this, adaptor.getValueToStore(), adaptor.getDest(),
4038 vectorSizeFoldThreshold)) {
4042 return inplaceFolded;
4049void InsertStridedSliceOp::build(OpBuilder &builder, OperationState &
result,
4050 Value source, Value dest,
4051 ArrayRef<int64_t> offsets,
4052 ArrayRef<int64_t> strides) {
4053 result.addOperands({source, dest});
4057 result.addAttribute(InsertStridedSliceOp::getOffsetsAttrName(
result.name),
4059 result.addAttribute(InsertStridedSliceOp::getStridesAttrName(
result.name),
4064template <
typename OpType>
4068 StringRef attrName) {
4069 if (arrayAttr.size() >
shape.size())
4070 return op.emitOpError(
"expected ")
4071 << attrName <<
" attribute of rank no greater than vector rank";
4078template <
typename OpType>
4082 bool halfOpen =
true) {
4083 for (
auto attr : arrayAttr) {
4084 auto val = llvm::cast<IntegerAttr>(attr).getInt();
4088 if (val < min || val >= upper)
4089 return op.emitOpError(
"expected ") << attrName <<
" to be confined to ["
4090 <<
min <<
", " << upper <<
")";
4098template <
typename OpType>
4103 for (
auto [
index, attrDimPair] :
4104 llvm::enumerate(llvm::zip_first(arrayAttr,
shape))) {
4105 int64_t val = llvm::cast<IntegerAttr>(std::get<0>(attrDimPair)).getInt();
4109 if (val < min || val >=
max)
4110 return op.emitOpError(
"expected ")
4111 << attrName <<
" dimension " <<
index <<
" to be confined to ["
4112 <<
min <<
", " <<
max <<
")";
4122template <
typename OpType>
4127 assert(arrayAttr1.size() <=
shape.size());
4128 assert(arrayAttr2.size() <=
shape.size());
4129 for (
auto [
index, it] :
4130 llvm::enumerate(llvm::zip(arrayAttr1, arrayAttr2,
shape))) {
4131 auto val1 = llvm::cast<IntegerAttr>(std::get<0>(it)).getInt();
4132 auto val2 = llvm::cast<IntegerAttr>(std::get<1>(it)).getInt();
4136 if (val1 + val2 < 0 || val1 + val2 >=
max)
4137 return op.emitOpError(
"expected sum(")
4138 << attrName1 <<
", " << attrName2 <<
") dimension " <<
index
4139 <<
" to be confined to [" <<
min <<
", " <<
max <<
")";
4147 return IntegerAttr::get(IntegerType::get(context, 64), APInt(64, v));
4149 return ArrayAttr::get(context, llvm::to_vector<8>(attrs));
4152LogicalResult InsertStridedSliceOp::verify() {
4153 auto sourceVectorType = getSourceVectorType();
4154 auto destVectorType = getDestVectorType();
4155 auto offsets = getOffsetsAttr();
4156 auto strides = getStridesAttr();
4157 if (offsets.size() !=
static_cast<unsigned>(destVectorType.getRank()))
4159 "expected offsets of same size as destination vector rank");
4160 if (strides.size() !=
static_cast<unsigned>(sourceVectorType.getRank()))
4161 return emitOpError(
"expected strides of same size as source vector rank");
4162 if (sourceVectorType.getRank() > destVectorType.getRank())
4164 "expected source rank to be no greater than destination rank");
4166 auto sourceShape = sourceVectorType.getShape();
4167 auto destShape = destVectorType.getShape();
4168 SmallVector<int64_t, 4> sourceShapeAsDestShape(
4169 destShape.size() - sourceShape.size(), 0);
4170 sourceShapeAsDestShape.append(sourceShape.begin(), sourceShape.end());
4171 auto offName = InsertStridedSliceOp::getOffsetsAttrName();
4172 auto stridesName = InsertStridedSliceOp::getStridesAttrName();
4181 offName,
"source vector shape",
4185 unsigned rankDiff = destShape.size() - sourceShape.size();
4186 for (
unsigned idx = 0; idx < sourceShape.size(); ++idx) {
4187 if (sourceVectorType.getScalableDims()[idx] !=
4188 destVectorType.getScalableDims()[idx + rankDiff]) {
4189 return emitOpError(
"mismatching scalable flags (at source vector idx=")
4192 if (sourceVectorType.getScalableDims()[idx]) {
4193 auto sourceSize = sourceShape[idx];
4194 auto destSize = destShape[idx + rankDiff];
4195 if (sourceSize != destSize) {
4196 return emitOpError(
"expected size at idx=")
4198 << (
" to match the corresponding base size from the input "
4200 << sourceSize << (
" vs ") << destSize << (
")");
4210class FoldInsertStridedSliceSplat final
4211 :
public OpRewritePattern<InsertStridedSliceOp> {
4215 LogicalResult matchAndRewrite(InsertStridedSliceOp insertStridedSliceOp,
4216 PatternRewriter &rewriter)
const override {
4218 auto dst = insertStridedSliceOp.getDest();
4219 auto splat = getScalarSplatSource(insertStridedSliceOp.getValueToStore());
4220 if (!splat || getScalarSplatSource(dst) != splat)
4223 rewriter.
replaceOp(insertStridedSliceOp, dst);
4230class FoldInsertStridedSliceOfExtract final
4231 :
public OpRewritePattern<InsertStridedSliceOp> {
4235 LogicalResult matchAndRewrite(InsertStridedSliceOp insertStridedSliceOp,
4236 PatternRewriter &rewriter)
const override {
4237 auto extractStridedSliceOp =
4238 insertStridedSliceOp.getValueToStore()
4239 .getDefiningOp<vector::ExtractStridedSliceOp>();
4241 if (!extractStridedSliceOp)
4244 if (extractStridedSliceOp.getOperand() != insertStridedSliceOp.getDest())
4248 if (extractStridedSliceOp.getStrides() !=
4249 insertStridedSliceOp.getStrides() ||
4250 extractStridedSliceOp.getOffsets() != insertStridedSliceOp.getOffsets())
4253 rewriter.
replaceOp(insertStridedSliceOp, insertStridedSliceOp.getDest());
4260class InsertStridedSliceConstantFolder final
4261 :
public OpRewritePattern<InsertStridedSliceOp> {
4267 static constexpr int64_t vectorSizeFoldThreshold = 256;
4269 LogicalResult matchAndRewrite(InsertStridedSliceOp op,
4270 PatternRewriter &rewriter)
const override {
4274 Attribute vectorDestCst;
4278 VectorType destTy = destVector.getType();
4279 if (destTy.isScalable())
4283 if (destTy.getNumElements() > vectorSizeFoldThreshold &&
4284 !destVector.hasOneUse())
4288 Attribute sourceCst;
4298 if (op.hasNonUnitStrides())
4301 VectorType sliceVecTy = sourceValue.getType();
4302 ArrayRef<int64_t> sliceShape = sliceVecTy.getShape();
4303 int64_t rankDifference = destTy.getRank() - sliceVecTy.getRank();
4304 SmallVector<int64_t, 4> offsets =
getI64SubArray(op.getOffsets());
4305 SmallVector<int64_t, 4> destStrides =
computeStrides(destTy.getShape());
4313 auto denseDest = llvm::cast<DenseElementsAttr>(vectorDestCst);
4314 auto denseSlice = llvm::cast<DenseElementsAttr>(sourceCst);
4315 auto sliceValuesIt = denseSlice.value_begin<Attribute>();
4316 auto newValues = llvm::to_vector(denseDest.getValues<Attribute>());
4317 SmallVector<int64_t> currDestPosition(offsets.begin(), offsets.end());
4318 MutableArrayRef<int64_t> currSlicePosition(
4319 currDestPosition.begin() + rankDifference, currDestPosition.end());
4320 ArrayRef<int64_t> sliceOffsets(offsets.begin() + rankDifference,
4323 int64_t linearizedPosition =
linearize(currDestPosition, destStrides);
4324 assert(linearizedPosition < destTy.getNumElements() &&
"Invalid index");
4325 assert(sliceValuesIt != denseSlice.value_end<Attribute>() &&
4326 "Invalid slice element");
4327 newValues[linearizedPosition] = *sliceValuesIt;
4340void vector::InsertStridedSliceOp::getCanonicalizationPatterns(
4341 RewritePatternSet &results, MLIRContext *context) {
4342 results.
add<FoldInsertStridedSliceSplat, FoldInsertStridedSliceOfExtract,
4343 InsertStridedSliceConstantFolder>(context);
4346OpFoldResult InsertStridedSliceOp::fold(FoldAdaptor adaptor) {
4347 if (getSourceVectorType() == getDestVectorType())
4348 return getValueToStore();
4357void OuterProductOp::build(OpBuilder &builder, OperationState &
result,
4358 Value
lhs, Value
rhs, Value acc) {
4363void OuterProductOp::print(OpAsmPrinter &p) {
4364 p <<
" " << getLhs() <<
", " << getRhs();
4366 p <<
", " << getAcc();
4367 SmallVector<NamedAttribute> attrs((*this)->getDiscardableAttrs());
4368 attrs.emplace_back(getKindAttrName(), getKindAttr());
4372 p <<
" : " << getLhs().getType() <<
", " << getRhs().getType();
4375ParseResult OuterProductOp::parse(OpAsmParser &parser, OperationState &
result) {
4376 SmallVector<OpAsmParser::UnresolvedOperand, 3> operandsInfo;
4383 if (operandsInfo.size() < 2)
4385 "expected at least 2 operands");
4386 VectorType vLHS = llvm::dyn_cast<VectorType>(tLHS);
4387 VectorType vRHS = llvm::dyn_cast<VectorType>(tRHS);
4390 "expected vector type for operand #1");
4394 if (vLHS.getRank() == 0)
4396 "expected 1-d vector for operand #1");
4397 if (vRHS && vRHS.getRank() == 0)
4399 "expected 1-d vector for operand #2");
4403 SmallVector<bool> scalableDimsRes{vLHS.getScalableDims()[0],
4404 vRHS.getScalableDims()[0]};
4405 resType = VectorType::get({vLHS.getDimSize(0), vRHS.getDimSize(0)},
4406 vLHS.getElementType(), scalableDimsRes);
4409 SmallVector<bool> scalableDimsRes{vLHS.getScalableDims()[0]};
4410 resType = VectorType::get({vLHS.getDimSize(0)}, vLHS.getElementType(),
4414 if (!
result.attributes.get(OuterProductOp::getKindAttrName(
result.name))) {
4415 result.attributes.append(
4416 OuterProductOp::getKindAttrName(
result.name),
4417 CombiningKindAttr::get(
result.getContext(),
4418 OuterProductOp::getDefaultKind()));
4424 (operandsInfo.size() > 2 &&
4429LogicalResult OuterProductOp::verify() {
4430 Type tRHS = getOperandTypeRHS();
4431 VectorType vLHS = getOperandVectorTypeLHS(),
4432 vRHS = llvm::dyn_cast<VectorType>(tRHS),
4433 vACC = getOperandVectorTypeACC(), vRES = getResultVectorType();
4435 if (vLHS.getRank() != 1)
4436 return emitOpError(
"expected 1-d vector for operand #1");
4440 if (vRHS.getRank() != 1)
4441 return emitOpError(
"expected 1-d vector for operand #2");
4442 if (vRES.getRank() != 2)
4443 return emitOpError(
"expected 2-d vector result");
4444 if (vLHS.getDimSize(0) != vRES.getDimSize(0))
4445 return emitOpError(
"expected #1 operand dim to match result dim #1");
4446 if (vRHS.getDimSize(0) != vRES.getDimSize(1))
4447 return emitOpError(
"expected #2 operand dim to match result dim #2");
4448 if (vLHS.isScalable() && !vRHS.isScalable()) {
4452 "expected either both or only #2 operand dim to be scalable");
4456 if (vRES.getRank() != 1)
4457 return emitOpError(
"expected 1-d vector result");
4458 if (vLHS.getDimSize(0) != vRES.getDimSize(0))
4459 return emitOpError(
"expected #1 operand dim to match result dim #1");
4462 if (vACC && vACC != vRES)
4463 return emitOpError(
"expected operand #3 of same type as result type");
4465 if (!getKindAttr()) {
4466 return emitOpError(
"expected 'kind' attribute of type CombiningKind (e.g. "
4467 "'vector.kind<add>')");
4472 return emitOpError(
"unsupported outerproduct type");
4481Type OuterProductOp::getExpectedMaskType() {
4482 auto vecType = this->getResultVectorType();
4483 return VectorType::get(vecType.getShape(),
4484 IntegerType::get(vecType.getContext(), 1),
4485 vecType.getScalableDims());
4499 assert(offsets.size() == sizes.size() && offsets.size() == strides.size());
4501 shape.reserve(vectorType.getRank());
4503 for (
unsigned e = offsets.size(); idx < e; ++idx)
4504 shape.push_back(llvm::cast<IntegerAttr>(sizes[idx]).getInt());
4505 for (
unsigned e = vectorType.getShape().size(); idx < e; ++idx)
4506 shape.push_back(vectorType.getShape()[idx]);
4508 return VectorType::get(
shape, vectorType.getElementType(),
4509 vectorType.getScalableDims());
4512void ExtractStridedSliceOp::build(OpBuilder &builder, OperationState &
result,
4513 Value source, ArrayRef<int64_t> offsets,
4514 ArrayRef<int64_t> sizes,
4515 ArrayRef<int64_t> strides) {
4516 result.addOperands(source);
4522 offsetsAttr, sizesAttr, stridesAttr));
4523 result.addAttribute(ExtractStridedSliceOp::getOffsetsAttrName(
result.name),
4525 result.addAttribute(ExtractStridedSliceOp::getSizesAttrName(
result.name),
4527 result.addAttribute(ExtractStridedSliceOp::getStridesAttrName(
result.name),
4531LogicalResult ExtractStridedSliceOp::verify() {
4532 auto type = getSourceVectorType();
4533 auto offsets = getOffsetsAttr();
4534 auto sizes = getSizesAttr();
4535 auto strides = getStridesAttr();
4536 if (offsets.size() != sizes.size() || offsets.size() != strides.size())
4538 "expected offsets, sizes and strides attributes of same size");
4540 auto shape = type.getShape();
4541 auto offName = getOffsetsAttrName();
4542 auto sizesName = getSizesAttrName();
4543 auto stridesName = getStridesAttrName();
4559 shape, offName, sizesName,
4564 offsets, sizes, strides);
4565 if (getResult().
getType() != resultType)
4566 return emitOpError(
"expected result type to be ") << resultType;
4568 for (
unsigned idx = 0; idx < sizes.size(); ++idx) {
4569 if (type.getScalableDims()[idx]) {
4570 auto inputDim = type.getShape()[idx];
4571 auto inputSize = llvm::cast<IntegerAttr>(sizes[idx]).getInt();
4572 if (inputDim != inputSize)
4573 return emitOpError(
"expected size at idx=")
4575 << (
" to match the corresponding base size from the input "
4577 << inputSize << (
" vs ") << inputDim << (
")");
4590 auto getElement = [](
ArrayAttr array,
int idx) {
4591 return llvm::cast<IntegerAttr>(array[idx]).getInt();
4593 ArrayAttr extractOffsets = op.getOffsets();
4596 auto insertOp = op.getSource().getDefiningOp<InsertStridedSliceOp>();
4598 if (op.getSourceVectorType().getRank() !=
4599 insertOp.getSourceVectorType().getRank())
4601 ArrayAttr insertOffsets = insertOp.getOffsets();
4602 ArrayAttr insertStrides = insertOp.getStrides();
4605 if (extractOffsets.size() > insertOffsets.size())
4607 bool patialoverlap =
false;
4608 bool disjoint =
false;
4610 for (
unsigned dim = 0, e = extractOffsets.size(); dim < e; ++dim) {
4611 if (getElement(
extractStrides, dim) != getElement(insertStrides, dim))
4613 int64_t start = getElement(insertOffsets, dim);
4614 int64_t end = start + insertOp.getSourceVectorType().getDimSize(dim);
4615 int64_t offset = getElement(extractOffsets, dim);
4616 int64_t size = getElement(extractSizes, dim);
4618 if (start <= offset && offset < end) {
4621 if (offset + size > end)
4622 patialoverlap =
true;
4623 offsetDiffs.push_back(offset - start);
4630 if (!disjoint && !patialoverlap) {
4631 op.setOperand(insertOp.getValueToStore());
4634 op.setOffsetsAttr(
b.getI64ArrayAttr(offsetDiffs));
4640 insertOp = insertOp.getDest().getDefiningOp<InsertStridedSliceOp>();
4655 auto dense = llvm::dyn_cast_if_present<DenseElementsAttr>(foldInput);
4660 if (op.hasNonUnitStrides())
4663 VectorType sourceVecTy = op.getSourceVectorType();
4667 VectorType sliceVecTy = op.getType();
4669 int64_t rank = sliceVecTy.getRank();
4681 const auto denseValuesBegin = dense.value_begin<
Attribute>();
4683 sliceValues.reserve(sliceVecTy.getNumElements());
4687 assert(linearizedPosition < sourceVecTy.getNumElements() &&
4689 sliceValues.push_back(*(denseValuesBegin + linearizedPosition));
4690 }
while (succeeded(
incSlicePosition(currSlicePosition, sliceShape, offsets)));
4692 assert(
static_cast<int64_t>(sliceValues.size()) ==
4693 sliceVecTy.getNumElements() &&
4694 "Invalid number of slice elements");
4698OpFoldResult ExtractStridedSliceOp::fold(FoldAdaptor adaptor) {
4699 if (getSourceVectorType() == getResult().
getType())
4706 llvm::dyn_cast_if_present<SplatElementsAttr>(adaptor.getSource()))
4713void ExtractStridedSliceOp::getOffsets(SmallVectorImpl<int64_t> &results) {
4735class StridedSliceFolder final
4736 :
public OpRewritePattern<ExtractStridedSliceOp> {
4738 using OpRewritePattern<ExtractStridedSliceOp>::OpRewritePattern;
4740 LogicalResult matchAndRewrite(ExtractStridedSliceOp secondOp,
4741 PatternRewriter &rewriter)
const override {
4742 auto firstOp = secondOp.getSource().getDefiningOp<ExtractStridedSliceOp>();
4746 if (secondOp.hasNonUnitStrides() || firstOp.hasNonUnitStrides())
4749 SmallVector<int64_t> firstOffsets =
getI64SubArray(firstOp.getOffsets());
4750 SmallVector<int64_t> firstSizes =
getI64SubArray(firstOp.getSizes());
4751 SmallVector<int64_t> secondOffsets =
getI64SubArray(secondOp.getOffsets());
4752 SmallVector<int64_t> secondSizes =
getI64SubArray(secondOp.getSizes());
4754 unsigned newRank = std::max(firstOffsets.size(), secondOffsets.size());
4755 SmallVector<int64_t> combinedOffsets(newRank, 0);
4756 SmallVector<int64_t> combinedSizes(newRank);
4757 ArrayRef<int64_t> firstSourceShape =
4758 firstOp.getSourceVectorType().getShape();
4759 for (
unsigned i = 0; i < newRank; ++i) {
4760 int64_t off1 = (i < firstOffsets.size()) ? firstOffsets[i] : 0;
4761 int64_t off2 = (i < secondOffsets.size()) ? secondOffsets[i] : 0;
4762 combinedOffsets[i] = off1 + off2;
4764 if (i < secondSizes.size()) {
4765 combinedSizes[i] = secondSizes[i];
4766 }
else if (i < firstSizes.size()) {
4767 combinedSizes[i] = firstSizes[i];
4769 combinedSizes[i] = firstSourceShape[i];
4773 SmallVector<int64_t> combinedStrides(newRank, 1);
4775 secondOp, firstOp.getSource(), combinedOffsets, combinedSizes,
4793class StridedSliceCreateMaskFolder final
4794 :
public OpRewritePattern<ExtractStridedSliceOp> {
4798 LogicalResult matchAndRewrite(ExtractStridedSliceOp extractStridedSliceOp,
4799 PatternRewriter &rewriter)
const override {
4800 Location loc = extractStridedSliceOp.getLoc();
4804 extractStridedSliceOp.getSource().getDefiningOp<CreateMaskOp>();
4808 if (extractStridedSliceOp.hasNonUnitStrides())
4811 SmallVector<Value> maskDimSizes(createMaskOp.getOperands());
4813 SmallVector<int64_t> sliceOffsets;
4816 SmallVector<int64_t> sliceSizes;
4820 SmallVector<Value> sliceMaskDimSizes;
4821 sliceMaskDimSizes.reserve(maskDimSizes.size());
4825 for (
auto [maskDimSize, sliceOffset, sliceSize] :
4826 llvm::zip(maskDimSizes, sliceOffsets, sliceSizes)) {
4830 IntegerAttr offsetAttr =
4832 Value offset = arith::ConstantOp::create(rewriter, loc, offsetAttr);
4833 Value sliceMaskDimSize =
4834 arith::SubIOp::create(rewriter, loc, maskDimSize, offset);
4835 sliceMaskDimSizes.push_back(sliceMaskDimSize);
4840 llvm::drop_begin(maskDimSizes, sliceMaskDimSizes.size()));
4844 extractStridedSliceOp, extractStridedSliceOp.getResult().
getType(),
4852class StridedSliceConstantMaskFolder final
4853 :
public OpRewritePattern<ExtractStridedSliceOp> {
4857 LogicalResult matchAndRewrite(ExtractStridedSliceOp extractStridedSliceOp,
4858 PatternRewriter &rewriter)
const override {
4861 auto *defOp = extractStridedSliceOp.getSource().getDefiningOp();
4862 auto constantMaskOp = dyn_cast_or_null<ConstantMaskOp>(defOp);
4863 if (!constantMaskOp)
4866 if (extractStridedSliceOp.hasNonUnitStrides())
4869 ArrayRef<int64_t> maskDimSizes = constantMaskOp.getMaskDimSizes();
4871 SmallVector<int64_t> sliceOffsets;
4874 SmallVector<int64_t> sliceSizes;
4878 SmallVector<int64_t> sliceMaskDimSizes;
4879 sliceMaskDimSizes.reserve(maskDimSizes.size());
4880 for (
auto [maskDimSize, sliceOffset, sliceSize] :
4881 llvm::zip(maskDimSizes, sliceOffsets, sliceSizes)) {
4882 int64_t sliceMaskDimSize = std::max(
4883 static_cast<int64_t
>(0),
4884 std::min(sliceOffset + sliceSize, maskDimSize) - sliceOffset);
4885 sliceMaskDimSizes.push_back(sliceMaskDimSize);
4888 if (sliceMaskDimSizes.size() < maskDimSizes.size())
4889 for (
size_t i = sliceMaskDimSizes.size(); i < maskDimSizes.size(); ++i)
4890 sliceMaskDimSizes.push_back(maskDimSizes[i]);
4893 if (llvm::is_contained(sliceMaskDimSizes, 0))
4894 sliceMaskDimSizes.assign(maskDimSizes.size(), 0);
4899 extractStridedSliceOp, extractStridedSliceOp.getResult().
getType(),
4907class StridedSliceBroadcast final
4908 :
public OpRewritePattern<ExtractStridedSliceOp> {
4912 LogicalResult matchAndRewrite(ExtractStridedSliceOp op,
4913 PatternRewriter &rewriter)
const override {
4919 unsigned srcRank = srcVecType ? srcVecType.getRank() : 0;
4920 auto dstVecType = llvm::cast<VectorType>(op.getType());
4921 unsigned dstRank = dstVecType.getRank();
4922 unsigned rankDiff = dstRank - srcRank;
4926 bool needsSlice =
false;
4927 for (
unsigned i = 0; i < srcRank; i++) {
4928 if (srcVecType.getDimSize(i) != 1 &&
4929 srcVecType.getDimSize(i) != dstVecType.getDimSize(i + rankDiff)) {
4936 SmallVector<int64_t> offsets =
4938 SmallVector<int64_t> sizes =
4940 for (
unsigned i = 0; i < srcRank; i++) {
4941 if (srcVecType.getDimSize(i) == 1) {
4949 source = ExtractStridedSliceOp::create(
4950 rewriter, op->getLoc(), source, offsets, sizes,
4959class StridedSliceSplat final :
public OpRewritePattern<ExtractStridedSliceOp> {
4963 LogicalResult matchAndRewrite(ExtractStridedSliceOp op,
4964 PatternRewriter &rewriter)
const override {
4966 Value splat = getScalarSplatSource(op.getSource());
4990class ContiguousExtractStridedSliceToExtract final
4991 :
public OpRewritePattern<ExtractStridedSliceOp> {
4995 LogicalResult matchAndRewrite(ExtractStridedSliceOp op,
4996 PatternRewriter &rewriter)
const override {
4997 if (op.hasNonUnitStrides())
4999 Value source = op.getOperand();
5000 auto sourceType = cast<VectorType>(source.
getType());
5001 if (sourceType.isScalable() || sourceType.getRank() == 0)
5010 for (numOffsets = sizes.size(); numOffsets > 0; --numOffsets) {
5011 if (sizes[numOffsets - 1] != sourceType.getDimSize(numOffsets - 1))
5018 if (numOffsets == 0)
5023 if (numOffsets == sourceType.getRank() &&
5024 static_cast<int>(sizes.size()) == sourceType.getRank())
5028 for (
int i = 0; i < numOffsets; ++i) {
5036 while (numOffsets <
static_cast<int>(sizes.size()) - 1 &&
5037 sizes[numOffsets] == 1) {
5042 auto extractOffsets = ArrayRef(offsets).take_front(numOffsets);
5043 Value extract = vector::ExtractOp::create(rewriter, op->getLoc(), source,
5052void ExtractStridedSliceOp::getCanonicalizationPatterns(
5053 RewritePatternSet &results, MLIRContext *context) {
5056 results.
add<StridedSliceFolder, StridedSliceCreateMaskFolder,
5057 StridedSliceConstantMaskFolder, StridedSliceBroadcast,
5058 StridedSliceSplat, ContiguousExtractStridedSliceToExtract>(
5068void TransferReadOp::build(OpBuilder &builder, OperationState &
result,
5069 VectorType vectorType, Value source,
5071 AffineMapAttr permutationMapAttr,
5074 Type elemType = llvm::cast<ShapedType>(source.
getType()).getElementType();
5076 padding = ub::PoisonOp::create(builder,
result.location, elemType);
5079 build(builder,
result, vectorType, source,
indices, permutationMapAttr,
5080 *padding, Value(), inBoundsAttr);
5088void TransferReadOp::build(OpBuilder &builder, OperationState &
result,
5089 VectorType vectorType, Value source,
5091 AffineMap permutationMap,
5092 std::optional<ArrayRef<bool>> inBounds) {
5093 if (!permutationMap)
5095 llvm::cast<ShapedType>(source.
getType()), vectorType);
5096 auto permutationMapAttr = AffineMapAttr::get(permutationMap);
5097 auto inBoundsAttr = (inBounds && !inBounds.value().empty())
5100 SmallVector<bool>(vectorType.getRank(),
false));
5102 build(builder,
result, vectorType, source,
indices, padding,
5103 permutationMapAttr, inBoundsAttr);
5109void TransferReadOp::build(OpBuilder &builder, OperationState &
result,
5110 VectorType vectorType, Value source,
5112 std::optional<ArrayRef<bool>> inBounds) {
5114 build(builder,
result, vectorType, source,
indices, padding,
5115 AffineMap(), inBounds);
5118template <
typename EmitFun>
5120 EmitFun emitOpError) {
5122 for (
auto expr : permutationMap.
getResults()) {
5123 auto dim = dyn_cast<AffineDimExpr>(expr);
5124 auto zero = dyn_cast<AffineConstantExpr>(expr);
5126 if (zero.getValue() != 0) {
5128 "requires a projected permutation_map (at most one dim or the zero "
5129 "constant can appear in each result)");
5134 return emitOpError(
"requires a projected permutation_map (at most one "
5135 "dim or the zero constant can appear in each result)");
5137 if (seen[dim.getPosition()]) {
5139 "requires a permutation_map that is a permutation (found one dim "
5140 "used more than once)");
5142 seen[dim.getPosition()] =
true;
5149 VectorType vectorType, VectorType maskType,
5150 VectorType inferredMaskType,
AffineMap permutationMap,
5152 if (op->hasDiscardableAttr(
"masked")) {
5153 return op->emitOpError(
"masked attribute has been removed. "
5154 "Use in_bounds instead.");
5157 if (!llvm::isa<MemRefType, RankedTensorType>(shapedType))
5158 return op->emitOpError(
5159 "requires source to be a memref or ranked tensor type");
5161 auto elementType = shapedType.getElementType();
5163 if (
auto vectorElementType = llvm::dyn_cast<VectorType>(elementType)) {
5165 unsigned sourceVecSize =
5167 vectorElementType.getShape().back();
5168 unsigned resultVecSize =
5170 vectorType.getShape().back();
5171 if (resultVecSize % sourceVecSize != 0)
5172 return op->emitOpError(
5173 "requires the bitwidth of the minor 1-D vector to be an integral "
5174 "multiple of the bitwidth of the minor 1-D vector of the source");
5176 unsigned sourceVecEltRank = vectorElementType.getRank();
5177 unsigned resultVecRank = vectorType.getRank();
5178 if (sourceVecEltRank > resultVecRank)
5179 return op->emitOpError(
5180 "requires source vector element and vector result ranks to match.");
5181 unsigned rankOffset = resultVecRank - sourceVecEltRank;
5184 return op->emitOpError(
"requires a permutation_map with result dims of "
5185 "the same rank as the vector type");
5188 return op->emitOpError(
"does not support masks with vector element type");
5191 unsigned minorSize =
5192 vectorType.getRank() == 0 ? 1 : vectorType.getShape().back();
5193 unsigned resultVecSize =
5196 return op->emitOpError(
5197 "requires the bitwidth of the minor 1-D vector to be an integral "
5198 "multiple of the bitwidth of the source element type");
5202 return op->emitOpError(
"requires a permutation_map with result dims of "
5203 "the same rank as the vector type");
5207 return op->emitOpError(
"requires permutation_map without symbols");
5209 if (permutationMap.
getNumInputs() != shapedType.getRank())
5210 return op->emitOpError(
"requires a permutation_map with input dims of the "
5211 "same rank as the source type");
5213 if (maskType && maskType != inferredMaskType)
5214 return op->emitOpError(
"inferred mask type (")
5215 << inferredMaskType <<
") and mask operand type (" << maskType
5219 return op->emitOpError(
"expects the in_bounds attr of same rank "
5220 "as permutation_map results: ")
5221 << AffineMapAttr::get(permutationMap)
5222 <<
" vs inBounds of size: " << inBounds.size();
5230 if (llvm::any_of(op.getInBoundsValues(), [](
bool b) { return b; }))
5231 attrs.emplace_back(op.getInBoundsAttrName(), op.getInBounds());
5232 if (!op.getPermutationMap().isMinorIdentity())
5233 attrs.emplace_back(op.getPermutationMapAttrName(),
5234 AffineMapAttr::get(op.getPermutationMap()));
5239void TransferReadOp::print(OpAsmPrinter &p) {
5242 p <<
", " << getMask();
5249 auto i1Type = IntegerType::get(permMap.
getContext(), 1);
5251 assert(invPermMap &&
"Inversed permutation map couldn't be computed");
5256 if (maskShape.empty())
5257 maskShape.push_back(1);
5262 return VectorType::get(maskShape, i1Type, scalableDims);
5279 if (hasMask.succeeded()) {
5286 if (types.size() != 2)
5287 return parser.
emitError(typesLoc,
"requires two types");
5289 auto shapedType = llvm::dyn_cast<ShapedType>(types[0]);
5290 if (!shapedType || !llvm::isa<MemRefType, RankedTensorType>(shapedType))
5291 return parser.
emitError(typesLoc,
"requires memref or ranked tensor type");
5292 VectorType vectorType = llvm::dyn_cast<VectorType>(types[1]);
5294 return parser.
emitError(typesLoc,
"requires vector type");
5295 auto permMapAttrName = TransferReadOp::getPermutationMapAttrName(
result.name);
5299 if (shapedType.getRank() <
5302 "expected a custom permutation_map when "
5303 "rank(source) != rank(destination)");
5305 result.attributes.set(permMapAttrName, AffineMapAttr::get(permMap));
5307 permMap = llvm::cast<AffineMapAttr>(permMapAttr).getValue();
5309 auto inBoundsAttrName = TransferReadOp::getInBoundsAttrName(
result.name);
5310 Attribute inBoundsAttr =
result.attributes.get(inBoundsAttrName);
5311 if (!inBoundsAttr) {
5312 result.addAttribute(inBoundsAttrName,
5321 if (hasMask.succeeded()) {
5322 if (llvm::dyn_cast<VectorType>(shapedType.getElementType()))
5324 maskInfo.
location,
"does not support masks with vector element type");
5327 "expected the same rank for the vector and the "
5328 "results of the permutation map");
5336 result.addAttribute(TransferReadOp::getOperandSegmentSizeAttr(),
5338 {1, static_cast<int32_t>(indexInfo.size()), 1,
5339 static_cast<int32_t>(hasMask.succeeded())}));
5343LogicalResult TransferReadOp::verify() {
5345 ShapedType shapedType = getShapedType();
5347 VectorType maskType = getMaskType();
5348 auto paddingType = getPadding().getType();
5349 auto permutationMap = getPermutationMap();
5350 VectorType inferredMaskType =
5353 auto sourceElementType = shapedType.getElementType();
5355 if (
static_cast<int64_t
>(
getIndices().size()) != shapedType.getRank())
5356 return emitOpError(
"requires ") << shapedType.getRank() <<
" indices";
5359 shapedType, vectorType, maskType,
5360 inferredMaskType, permutationMap, getInBounds())))
5363 if (
auto sourceVectorElementType =
5364 llvm::dyn_cast<VectorType>(sourceElementType)) {
5367 if (sourceVectorElementType != paddingType)
5369 "requires source element type and padding type to match.");
5373 if (!VectorType::isValidElementType(paddingType))
5374 return emitOpError(
"requires valid padding vector elemental type");
5377 if (paddingType != sourceElementType)
5379 "requires formal padding and source of the same elemental type");
5383 [&](Twine t) {
return emitOpError(t); });
5390Type TransferReadOp::getExpectedMaskType() {
5397VectorType TransferReadOp::getVectorType() {
5398 return cast<VectorType>(getVector().
getType());
5401template <
typename TransferOp>
5405 if (op.getShapedType().isDynamicDim(indicesIdx))
5409 if (op.getVectorType().getScalableDims()[resultIdx])
5413 if (!cstOp.has_value())
5416 int64_t sourceSize = op.getShapedType().getDimSize(indicesIdx);
5417 int64_t vectorSize = op.getVectorType().getDimSize(resultIdx);
5423 int64_t maxStart = sourceSize - vectorSize;
5427 return *cstOp >= 0 && *cstOp <= maxStart;
5430template <
typename TransferOp>
5434 if (op.getTransferRank() == 0)
5437 bool changed =
false;
5439 newInBounds.reserve(op.getTransferRank());
5444 for (
unsigned i = 0; i < op.getTransferRank(); ++i) {
5446 if (op.isDimInBounds(i)) {
5447 newInBounds.push_back(
true);
5452 bool inBounds =
false;
5453 auto dimExpr = dyn_cast<AffineDimExpr>(permutationMap.
getResult(i));
5456 dimExpr.getPosition());
5457 nonBcastDims.push_back(i);
5460 newInBounds.push_back(inBounds);
5462 changed |= inBounds;
5468 bool allNonBcastDimsInBounds = llvm::all_of(
5469 nonBcastDims, [&newInBounds](
unsigned idx) {
return newInBounds[idx]; });
5470 if (allNonBcastDimsInBounds) {
5472 changed |= !newInBounds[idx];
5473 newInBounds[idx] =
true;
5481 op.setInBoundsAttr(
b.getBoolArrayAttr(newInBounds));
5485template <
typename TransferOp>
5487 auto mask = op.getMask();
5494 op.getMaskMutable().clear();
5502template <
typename TransferOp>
5504 VectorType vecType = op.getVectorType();
5505 if (vecType.getRank() != 1 || vecType.getShape()[0] != 1 ||
5506 vecType.isScalable())
5513 int64_t srcRank = op.getShapedType().getRank();
5519 op.setPermutationMapAttr(AffineMapAttr::get(minorIdentity));
5533static Value foldRAW(TransferReadOp readOp) {
5534 if (!llvm::isa<RankedTensorType>(readOp.getShapedType()))
5536 auto defWrite = readOp.getBase().getDefiningOp<vector::TransferWriteOp>();
5539 return defWrite.getVector();
5541 cast<VectorTransferOpInterface>(defWrite.getOperation()),
5542 cast<VectorTransferOpInterface>(readOp.getOperation())))
5544 defWrite = defWrite.getBase().getDefiningOp<vector::TransferWriteOp>();
5549OpFoldResult TransferReadOp::fold(FoldAdaptor) {
5550 if (Value vec = foldRAW(*
this))
5563 return OpFoldResult();
5566std::optional<SmallVector<int64_t, 4>> TransferReadOp::getShapeForUnroll() {
5570void TransferReadOp::getEffects(
5571 SmallVectorImpl<SideEffects::EffectInstance<MemoryEffects::Effect>>
5573 if (llvm::isa<MemRefType>(getShapedType()))
5574 effects.emplace_back(MemoryEffects::Read::get(), &getBaseMutable(),
5575 SideEffects::DefaultResource::get());
5579 if (hasPureTensorSemantics())
5586static AffineMap inverseWithUnusedDims(AffineMap map) {
5588 "expected a projected permutation map");
5593 int64_t pos = cast<AffineDimExpr>(
result).getPosition();
5623struct TransferReadAfterWriteToBroadcast
5624 :
public OpRewritePattern<TransferReadOp> {
5627 LogicalResult matchAndRewrite(TransferReadOp readOp,
5628 PatternRewriter &rewriter)
const override {
5629 auto defWrite = readOp.getBase().getDefiningOp<vector::TransferWriteOp>();
5633 if (!readOp.hasPureTensorSemantics() || !defWrite.hasPureTensorSemantics())
5637 if (readOp.getMask() || defWrite.getMask())
5640 if (readOp.getIndices() != defWrite.getIndices())
5643 if (readOp.hasOutOfBoundsDim() || defWrite.hasOutOfBoundsDim())
5647 if (readOp.getTransferChunkAccessed() !=
5648 defWrite.getTransferChunkAccessed())
5655 AffineMap readMap = readOp.getPermutationMap();
5656 AffineMap writeMap = defWrite.getPermutationMap();
5657 AffineMap invWriteMap = inverseWithUnusedDims(writeMap);
5658 AffineMap composedMap = readMap.
compose(invWriteMap);
5672 int64_t numBroadcastedDims = broadcastedDims.size();
5673 auto invPerm = llvm::to_vector_of<int64_t>(broadcastedDims);
5675 for (
auto [idx, expr] : llvm::enumerate(composedMap.
getResults())) {
5676 if (
auto dim = dyn_cast<AffineDimExpr>(expr)) {
5677 int64_t effectiveDim = dim.getPosition() + numBroadcastedDims;
5678 invPerm[effectiveDim] = idx;
5683 VectorType readVecTy = readOp.getVectorType();
5685 auto broadcastedVecTy =
5687 readVecTy.getElementType(),
5690 Value vec = defWrite.getVector();
5691 Location loc = readOp.getLoc();
5692 vec = vector::BroadcastOp::create(rewriter, loc, broadcastedVecTy, vec);
5699void TransferReadOp::getCanonicalizationPatterns(RewritePatternSet &results,
5700 MLIRContext *context) {
5701 results.
add<TransferReadAfterWriteToBroadcast>(context);
5704FailureOr<std::optional<SmallVector<Value>>>
5705TransferReadOp::bubbleDownCasts(OpBuilder &builder) {
5706 if (!hasPureBufferSemantics())
5717void TransferWriteOp::build(OpBuilder &builder, OperationState &
result,
5719 AffineMapAttr permutationMapAttr,
5722 Type resultType = llvm::dyn_cast<RankedTensorType>(dest.
getType());
5723 build(builder,
result, resultType, vector, dest,
indices, permutationMapAttr,
5724 mask, inBoundsAttr);
5728void TransferWriteOp::build(OpBuilder &builder, OperationState &
result,
5730 AffineMapAttr permutationMapAttr,
5732 build(builder,
result, vector, dest,
indices, permutationMapAttr,
5733 Value(), inBoundsAttr);
5738void TransferWriteOp::build(OpBuilder &builder, OperationState &
result,
5740 AffineMap permutationMap,
5741 std::optional<ArrayRef<bool>> inBounds) {
5742 if (!permutationMap)
5745 llvm::cast<VectorType>(vector.
getType()));
5746 auto permutationMapAttr = AffineMapAttr::get(permutationMap);
5748 (inBounds && !inBounds.value().empty())
5751 llvm::cast<VectorType>(vector.
getType()).getRank(),
false));
5752 build(builder,
result, vector, dest,
indices, permutationMapAttr,
5753 Value(), inBoundsAttr);
5758void TransferWriteOp::build(OpBuilder &builder, OperationState &
result,
5760 std::optional<ArrayRef<bool>> inBounds) {
5765ParseResult TransferWriteOp::parse(OpAsmParser &parser,
5766 OperationState &
result) {
5769 OpAsmParser::UnresolvedOperand vectorInfo, sourceInfo;
5770 SmallVector<OpAsmParser::UnresolvedOperand, 8> indexInfo;
5771 SmallVector<Type, 2> types;
5772 OpAsmParser::UnresolvedOperand maskInfo;
5778 if (hasMask.succeeded() && parser.
parseOperand(maskInfo))
5783 if (types.size() != 2)
5784 return parser.
emitError(typesLoc,
"requires two types");
5786 VectorType vectorType = llvm::dyn_cast<VectorType>(types[0]);
5788 return parser.
emitError(typesLoc,
"requires vector type");
5789 ShapedType shapedType = llvm::dyn_cast<ShapedType>(types[1]);
5790 if (!shapedType || !llvm::isa<MemRefType, RankedTensorType>(shapedType))
5791 return parser.
emitError(typesLoc,
"requires memref or ranked tensor type");
5792 auto permMapAttrName =
5793 TransferWriteOp::getPermutationMapAttrName(
result.name);
5794 auto permMapAttr =
result.attributes.get(permMapAttrName);
5797 if (shapedType.getRank() <
5800 "expected a custom permutation_map when "
5801 "rank(source) != rank(destination)");
5803 result.attributes.set(permMapAttrName, AffineMapAttr::get(permMap));
5805 permMap = llvm::cast<AffineMapAttr>(permMapAttr).getValue();
5807 auto inBoundsAttrName = TransferWriteOp::getInBoundsAttrName(
result.name);
5808 Attribute inBoundsAttr =
result.attributes.get(inBoundsAttrName);
5809 if (!inBoundsAttr) {
5810 result.addAttribute(inBoundsAttrName,
5818 if (hasMask.succeeded()) {
5819 if (llvm::dyn_cast<VectorType>(shapedType.getElementType()))
5821 maskInfo.
location,
"does not support masks with vector element type");
5824 "expected the same rank for the vector and the "
5825 "results of the permutation map");
5831 result.addAttribute(TransferWriteOp::getOperandSegmentSizeAttr(),
5833 {1, 1, static_cast<int32_t>(indexInfo.size()),
5834 static_cast<int32_t>(hasMask.succeeded())}));
5835 return failure(llvm::isa<RankedTensorType>(shapedType) &&
5839void TransferWriteOp::print(OpAsmPrinter &p) {
5842 p <<
", " << getMask();
5847LogicalResult TransferWriteOp::verify() {
5849 ShapedType shapedType = getShapedType();
5851 VectorType maskType = getMaskType();
5852 auto permutationMap = getPermutationMap();
5853 VectorType inferredMaskType =
5857 if (llvm::size(
getIndices()) != shapedType.getRank())
5858 return emitOpError(
"requires ") << shapedType.getRank() <<
" indices";
5862 if (hasBroadcastDim())
5863 return emitOpError(
"should not have broadcast dimensions");
5866 shapedType, vectorType, maskType,
5867 inferredMaskType, permutationMap, getInBounds())))
5871 [&](Twine t) {
return emitOpError(t); });
5880Type TransferWriteOp::getExpectedMaskType() {
5887Value TransferWriteOp::getVector() {
return getOperand(0); }
5888VectorType TransferWriteOp::getVectorType() {
5889 return cast<VectorType>(getValueToStore().
getType());
5912static LogicalResult foldReadInitWrite(TransferWriteOp write,
5913 ArrayRef<Attribute>,
5914 SmallVectorImpl<OpFoldResult> &results) {
5916 if (write.getTransferRank() == 0)
5918 auto rankedTensorType =
5919 llvm::dyn_cast<RankedTensorType>(write.getBase().getType());
5921 if (!rankedTensorType)
5924 auto read = write.getVector().getDefiningOp<vector::TransferReadOp>();
5928 if (read.getTransferRank() == 0)
5931 if (!read.getPermutationMap().isMinorIdentity() ||
5932 !write.getPermutationMap().isMinorIdentity())
5935 if (read.getTransferRank() != write.getTransferRank())
5938 if (read.hasOutOfBoundsDim() || write.hasOutOfBoundsDim())
5941 if (read.getMask() || write.getMask())
5944 if (read.getBase().getType() != rankedTensorType)
5947 if (read.getVectorType() != write.getVectorType())
5950 if (read.getVectorType().getShape() != rankedTensorType.getShape())
5953 auto isNotConstantZero = [](Value v) {
5955 return !cstOp.has_value() || cstOp.value() != 0;
5957 if (llvm::any_of(read.getIndices(), isNotConstantZero) ||
5958 llvm::any_of(write.getIndices(), isNotConstantZero))
5961 results.push_back(read.getBase());
5965static bool checkSameValueWAR(vector::TransferReadOp read,
5966 vector::TransferWriteOp write) {
5967 return read.getBase() == write.getBase() &&
5968 read.getIndices() == write.getIndices() &&
5969 read.getPermutationMap() == write.getPermutationMap() &&
5970 read.getVectorType() == write.getVectorType() && !read.getMask() &&
5987static LogicalResult foldWAR(TransferWriteOp write,
5988 SmallVectorImpl<OpFoldResult> &results) {
5989 if (!llvm::isa<RankedTensorType>(write.getBase().getType()))
5991 auto read = write.getVector().getDefiningOp<vector::TransferReadOp>();
5995 if (!checkSameValueWAR(read, write))
5997 results.push_back(read.getBase());
6001LogicalResult TransferWriteOp::fold(FoldAdaptor adaptor,
6002 SmallVectorImpl<OpFoldResult> &results) {
6003 if (succeeded(foldReadInitWrite(*
this, adaptor.getOperands(), results)))
6005 if (succeeded(foldWAR(*
this, results)))
6019std::optional<SmallVector<int64_t, 4>> TransferWriteOp::getShapeForUnroll() {
6023void TransferWriteOp::getEffects(
6024 SmallVectorImpl<SideEffects::EffectInstance<MemoryEffects::Effect>>
6026 if (llvm::isa<MemRefType>(getShapedType()))
6027 effects.emplace_back(MemoryEffects::Write::get(), &getBaseMutable(),
6028 SideEffects::DefaultResource::get());
6032 if (hasPureTensorSemantics())
6062class FoldWaw final :
public OpRewritePattern<TransferWriteOp> {
6065 LogicalResult matchAndRewrite(TransferWriteOp writeOp,
6066 PatternRewriter &rewriter)
const override {
6067 if (!llvm::isa<RankedTensorType>(writeOp.getShapedType()))
6069 vector::TransferWriteOp writeToModify = writeOp;
6071 auto defWrite = writeOp.getBase().getDefiningOp<vector::TransferWriteOp>();
6075 writeToModify.getBaseMutable().assign(defWrite.getBase());
6080 cast<VectorTransferOpInterface>(defWrite.getOperation()),
6081 cast<VectorTransferOpInterface>(writeOp.getOperation())))
6085 if (!defWrite->hasOneUse())
6087 writeToModify = defWrite;
6088 defWrite = defWrite.getBase().getDefiningOp<vector::TransferWriteOp>();
6117struct SwapExtractSliceOfTransferWrite
6118 :
public OpRewritePattern<tensor::InsertSliceOp> {
6122 LogicalResult matchAndRewrite(tensor::InsertSliceOp insertOp,
6123 PatternRewriter &rewriter)
const override {
6124 if (!insertOp.hasUnitStride())
6127 insertOp.getSource().getDefiningOp<tensor::ExtractSliceOp>();
6128 if (!extractOp || !extractOp.hasUnitStride() || !extractOp->hasOneUse())
6130 auto transferOp = extractOp.getSource().getDefiningOp<TransferWriteOp>();
6131 if (!transferOp || !transferOp->hasOneUse())
6136 if (insertOp.getSourceType().getRank() != transferOp.getTransferRank()) {
6138 "use-def chain is rank-reducing");
6142 if (!extractOp.hasZeroOffset()) {
6144 "ExtractSliceOp has non-zero offset");
6148 if (!llvm::all_of(transferOp.getIndices(), [](Value value) {
6149 return getConstantIntValue(value) == static_cast<int64_t>(0);
6152 "TranferWriteOp has non-zero offset");
6156 if (insertOp.getMixedSizes().size() != extractOp.getMixedSizes().size()) {
6158 insertOp,
"InsertSliceOp and ExtractSliceOp ranks differ");
6161 for (
auto [insertSize, extractSize] :
6162 llvm::zip_equal(insertOp.getMixedSizes(), extractOp.getMixedSizes())) {
6165 insertOp,
"InsertSliceOp and ExtractSliceOp sizes differ");
6170 assert(transferOp.getVectorType().hasStaticShape() &&
6171 "expected vector to have a static shape");
6172 ArrayRef<int64_t>
vectorShape = transferOp.getVectorType().getShape();
6174 transferOp.getPermutationMap(), transferOp.getShapedType().getShape());
6175 if (transferOp.getMask() || !
vectorShape.equals(resultShape)) {
6177 insertOp,
"TransferWriteOp may not write the full tensor.");
6182 SmallVector<bool> newInBounds(
vectorShape.size(),
false);
6183 auto newExtractOp = tensor::ExtractSliceOp::create(
6184 rewriter, extractOp.getLoc(), insertOp.getSourceType(),
6185 insertOp.getDest(), insertOp.getMixedOffsets(),
6186 insertOp.getMixedSizes(), insertOp.getMixedStrides());
6187 auto newTransferWriteOp = TransferWriteOp::create(
6188 rewriter, transferOp.getLoc(), transferOp.getVector(),
6189 newExtractOp.getResult(), transferOp.getIndices(),
6190 transferOp.getPermutationMapAttr(),
6193 insertOp.getSourceMutable().assign(newTransferWriteOp.getResult());
6201void TransferWriteOp::getCanonicalizationPatterns(RewritePatternSet &results,
6202 MLIRContext *context) {
6203 results.
add<FoldWaw, SwapExtractSliceOfTransferWrite>(context);
6206FailureOr<std::optional<SmallVector<Value>>>
6207TransferWriteOp::bubbleDownCasts(OpBuilder &builder) {
6208 if (!hasPureBufferSemantics())
6222 result = dyn_cast<BoolAttr>(attr);
6225 "expected boolean attribute");
6229static void printBoolAttr(OpAsmPrinter &printer, Operation *, BoolAttr attr) {
6233static LogicalResult verifyLoadStoreMemRefLayout(Operation *op,
6235 MemRefType memRefTy) {
6238 if (!vecTy.isScalable() &&
6239 (vecTy.getRank() == 0 || vecTy.getNumElements() == 1))
6242 if (!memRefTy.isLastDimUnitStride())
6243 return op->
emitOpError(
"most minor memref dim must have unit stride");
6247LogicalResult vector::LoadOp::verify() {
6251 if (
failed(verifyLoadStoreMemRefLayout(*
this, resVecTy, memRefTy)))
6258 return emitOpError(
"memref strides must be non-negative");
6260 if (memRefTy.getRank() < resVecTy.getRank())
6262 "destination memref has lower rank than the result vector");
6265 Type memElemTy = memRefTy.getElementType();
6266 if (
auto memVecTy = llvm::dyn_cast<VectorType>(memElemTy)) {
6267 if (memVecTy != resVecTy)
6268 return emitOpError(
"base memref and result vector types should match");
6269 memElemTy = memVecTy.getElementType();
6272 if (resVecTy.getElementType() != memElemTy)
6273 return emitOpError(
"base and result element types should match");
6274 if (llvm::size(
getIndices()) != memRefTy.getRank())
6275 return emitOpError(
"requires ") << memRefTy.getRank() <<
" indices";
6279OpFoldResult LoadOp::fold(FoldAdaptor) {
6282 return OpFoldResult();
6285std::optional<SmallVector<int64_t, 4>> LoadOp::getShapeForUnroll() {
6289FailureOr<std::optional<SmallVector<Value>>>
6290LoadOp::bubbleDownCasts(OpBuilder &builder) {
6299LogicalResult vector::StoreOp::verify() {
6303 if (
failed(verifyLoadStoreMemRefLayout(*
this, valueVecTy, memRefTy)))
6310 return emitOpError(
"memref strides must be non-negative");
6312 if (memRefTy.getRank() < valueVecTy.getRank())
6313 return emitOpError(
"source memref has lower rank than the vector to store");
6316 Type memElemTy = memRefTy.getElementType();
6317 if (
auto memVecTy = llvm::dyn_cast<VectorType>(memElemTy)) {
6318 if (memVecTy != valueVecTy)
6320 "base memref and valueToStore vector types should match");
6321 memElemTy = memVecTy.getElementType();
6324 if (valueVecTy.getElementType() != memElemTy)
6325 return emitOpError(
"base and valueToStore element type should match");
6326 if (llvm::size(
getIndices()) != memRefTy.getRank())
6327 return emitOpError(
"requires ") << memRefTy.getRank() <<
" indices";
6331LogicalResult StoreOp::fold(FoldAdaptor adaptor,
6332 SmallVectorImpl<OpFoldResult> &results) {
6336std::optional<SmallVector<int64_t, 4>> StoreOp::getShapeForUnroll() {
6340FailureOr<std::optional<SmallVector<Value>>>
6341StoreOp::bubbleDownCasts(OpBuilder &builder) {
6350LogicalResult MaskedLoadOp::verify() {
6351 VectorType maskVType = getMaskVectorType();
6352 VectorType passVType = getPassThruVectorType();
6356 if (
failed(verifyLoadStoreMemRefLayout(*
this, resVType, memType)))
6363 return emitOpError(
"memref strides must be non-negative");
6368 if (llvm::size(
getIndices()) != memType.getRank())
6369 return emitOpError(
"requires ") << memType.getRank() <<
" indices";
6370 if (resVType.getShape() != maskVType.getShape())
6371 return emitOpError(
"expected result shape to match mask shape");
6372 if (resVType != passVType)
6373 return emitOpError(
"expected pass_thru of same type as result type");
6378class MaskedLoadFolder final :
public OpRewritePattern<MaskedLoadOp> {
6381 LogicalResult matchAndRewrite(MaskedLoadOp
load,
6382 PatternRewriter &rewriter)
const override {
6394 llvm_unreachable(
"Unexpected 1DMaskFormat on MaskedLoad");
6399void MaskedLoadOp::getCanonicalizationPatterns(RewritePatternSet &results,
6400 MLIRContext *context) {
6401 results.
add<MaskedLoadFolder>(context);
6404OpFoldResult MaskedLoadOp::fold(FoldAdaptor) {
6407 return OpFoldResult();
6410FailureOr<std::optional<SmallVector<Value>>>
6411MaskedLoadOp::bubbleDownCasts(OpBuilder &builder) {
6420LogicalResult MaskedStoreOp::verify() {
6421 VectorType maskVType = getMaskVectorType();
6425 if (
failed(verifyLoadStoreMemRefLayout(*
this, valueVType, memType)))
6432 return emitOpError(
"memref strides must be non-negative");
6437 if (llvm::size(
getIndices()) != memType.getRank())
6438 return emitOpError(
"requires ") << memType.getRank() <<
" indices";
6439 if (valueVType.getShape() != maskVType.getShape())
6440 return emitOpError(
"expected valueToStore shape to match mask shape");
6445class MaskedStoreFolder final :
public OpRewritePattern<MaskedStoreOp> {
6448 LogicalResult matchAndRewrite(MaskedStoreOp store,
6449 PatternRewriter &rewriter)
const override {
6453 store, store.getValueToStore(), store.getBase(), store.getIndices());
6461 llvm_unreachable(
"Unexpected 1DMaskFormat on MaskedStore");
6466void MaskedStoreOp::getCanonicalizationPatterns(RewritePatternSet &results,
6467 MLIRContext *context) {
6468 results.
add<MaskedStoreFolder>(context);
6471LogicalResult MaskedStoreOp::fold(FoldAdaptor adaptor,
6472 SmallVectorImpl<OpFoldResult> &results) {
6476FailureOr<std::optional<SmallVector<Value>>>
6477MaskedStoreOp::bubbleDownCasts(OpBuilder &builder) {
6486LogicalResult GatherOp::verify() {
6487 VectorType indVType = getIndexVectorType();
6488 VectorType maskVType = getMaskVectorType();
6490 ShapedType baseType = getBaseType();
6492 if (!llvm::isa<MemRefType, RankedTensorType>(baseType))
6493 return emitOpError(
"requires base to be a memref or ranked tensor type");
6498 if (
auto memRefType = dyn_cast<MemRefType>(baseType))
6500 return emitOpError(
"memref strides must be non-negative");
6505 if (llvm::size(getOffsets()) != baseType.getRank())
6506 return emitOpError(
"requires ") << baseType.getRank() <<
" indices";
6507 if (resVType.getShape() != indVType.getShape())
6508 return emitOpError(
"expected result dim to match indices dim");
6509 if (resVType.getShape() != maskVType.getShape())
6510 return emitOpError(
"expected result dim to match mask dim");
6511 if (resVType != getPassThruVectorType())
6512 return emitOpError(
"expected pass_thru of same type as result type");
6513 if (getAlignmentAttr() && !isa<MemRefType>(baseType)) {
6515 "alignment is only supported for memref bases, not tensor bases");
6524Type GatherOp::getExpectedMaskType() {
6525 auto vecType = this->getIndexVectorType();
6526 return VectorType::get(vecType.getShape(),
6527 IntegerType::get(vecType.getContext(), 1),
6528 vecType.getScalableDims());
6531std::optional<SmallVector<int64_t, 4>> GatherOp::getShapeForUnroll() {
6536static LogicalResult isZeroBasedContiguousSeq(Value indexVec) {
6537 auto vecType = dyn_cast<VectorType>(indexVec.
getType());
6538 if (!vecType || vecType.getRank() != 1 || vecType.isScalable())
6544 DenseIntElementsAttr elements;
6549 llvm::equal(elements, llvm::seq<int64_t>(0, vecType.getNumElements())));
6553class GatherFolder final :
public OpRewritePattern<GatherOp> {
6556 LogicalResult matchAndRewrite(GatherOp gather,
6557 PatternRewriter &rewriter)
const override {
6562 rewriter.
replaceOp(gather, gather.getPassThru());
6567 llvm_unreachable(
"Unexpected 1DMaskFormat on GatherFolder");
6573class FoldContiguousGather final :
public OpRewritePattern<GatherOp> {
6576 LogicalResult matchAndRewrite(GatherOp op,
6577 PatternRewriter &rewriter)
const override {
6578 if (!isa<MemRefType>(op.getBase().getType()))
6581 if (
failed(isZeroBasedContiguousSeq(op.getIndices())))
6585 op.getOffsets(), op.getMask(),
6592void GatherOp::getCanonicalizationPatterns(RewritePatternSet &results,
6593 MLIRContext *context) {
6594 results.
add<GatherFolder, FoldContiguousGather>(context);
6597FailureOr<std::optional<SmallVector<Value>>>
6598GatherOp::bubbleDownCasts(OpBuilder &builder) {
6607LogicalResult ScatterOp::verify() {
6608 VectorType indVType = getIndexVectorType();
6609 VectorType maskVType = getMaskVectorType();
6611 ShapedType baseType = getBaseType();
6613 if (!llvm::isa<MemRefType, RankedTensorType>(baseType))
6614 return emitOpError(
"requires base to be a memref or ranked tensor type");
6619 if (
auto memRefType = dyn_cast<MemRefType>(baseType))
6621 return emitOpError(
"memref strides must be non-negative");
6626 if (llvm::size(getOffsets()) != baseType.getRank())
6627 return emitOpError(
"requires ") << baseType.getRank() <<
" indices";
6628 if (valueVType.getShape() != indVType.getShape())
6629 return emitOpError(
"expected valueToStore dim to match indices dim");
6630 if (valueVType.getShape() != maskVType.getShape())
6631 return emitOpError(
"expected valueToStore dim to match mask dim");
6632 if (getAlignmentAttr() && !isa<MemRefType>(baseType)) {
6634 "alignment is only supported for memref bases, not tensor bases");
6639class ScatterFolder final :
public OpRewritePattern<ScatterOp> {
6642 LogicalResult matchAndRewrite(ScatterOp scatter,
6643 PatternRewriter &rewriter)
const override {
6644 ShapedType baseType = scatter.getBaseType();
6645 bool isMemRef = isa<MemRefType>(baseType);
6646 if (!isMemRef && !isa<RankedTensorType>(baseType))
6659 rewriter.
replaceOp(scatter, scatter.getBase());
6664 llvm_unreachable(
"Unexpected 1DMaskFormat on ScatterFolder");
6670class FoldContiguousScatter final :
public OpRewritePattern<ScatterOp> {
6673 LogicalResult matchAndRewrite(ScatterOp op,
6674 PatternRewriter &rewriter)
const override {
6677 if (!isa<MemRefType>(op.getBase().getType()))
6680 if (
failed(isZeroBasedContiguousSeq(op.getIndices())))
6684 op, op.getBase(), op.getOffsets(), op.getMask(), op.getValueToStore());
6690void ScatterOp::getCanonicalizationPatterns(RewritePatternSet &results,
6691 MLIRContext *context) {
6692 results.
add<ScatterFolder, FoldContiguousScatter>(context);
6695FailureOr<std::optional<SmallVector<Value>>>
6696ScatterOp::bubbleDownCasts(OpBuilder &builder) {
6705LogicalResult ExpandLoadOp::verify() {
6706 VectorType maskVType = getMaskVectorType();
6707 VectorType passVType = getPassThruVectorType();
6711 if (
failed(verifyLoadStoreMemRefLayout(*
this, resVType, memType)))
6718 return emitOpError(
"memref strides must be non-negative");
6723 if (llvm::size(
getIndices()) != memType.getRank())
6724 return emitOpError(
"requires ") << memType.getRank() <<
" indices";
6725 if (resVType.getShape() != maskVType.getShape())
6726 return emitOpError(
"expected result shape to match mask shape");
6727 if (resVType.getScalableDims() != maskVType.getScalableDims())
6729 "expected result scalable dims to match mask scalable dims");
6730 if (resVType != passVType)
6731 return emitOpError(
"expected pass_thru of same type as result type");
6736class ExpandLoadFolder final :
public OpRewritePattern<ExpandLoadOp> {
6739 LogicalResult matchAndRewrite(ExpandLoadOp expand,
6740 PatternRewriter &rewriter)
const override {
6744 expand, expand.getType(), expand.getBase(), expand.getIndices());
6747 rewriter.
replaceOp(expand, expand.getPassThru());
6752 llvm_unreachable(
"Unexpected 1DMaskFormat on ExpandLoadFolder");
6757void ExpandLoadOp::getCanonicalizationPatterns(RewritePatternSet &results,
6758 MLIRContext *context) {
6759 results.
add<ExpandLoadFolder>(context);
6762FailureOr<std::optional<SmallVector<Value>>>
6763ExpandLoadOp::bubbleDownCasts(OpBuilder &builder) {
6772LogicalResult CompressStoreOp::verify() {
6773 VectorType maskVType = getMaskVectorType();
6777 if (
failed(verifyLoadStoreMemRefLayout(*
this, valueVType, memType)))
6784 return emitOpError(
"memref strides must be non-negative");
6789 if (llvm::size(
getIndices()) != memType.getRank())
6790 return emitOpError(
"requires ") << memType.getRank() <<
" indices";
6791 if (valueVType.getShape() != maskVType.getShape())
6792 return emitOpError(
"expected valueToStore shape to match mask shape");
6793 if (valueVType.getScalableDims() != maskVType.getScalableDims())
6795 "expected valueToStore scalable dims to match mask scalable dims");
6800class CompressStoreFolder final :
public OpRewritePattern<CompressStoreOp> {
6803 LogicalResult matchAndRewrite(CompressStoreOp compress,
6804 PatternRewriter &rewriter)
const override {
6808 compress, compress.getValueToStore(), compress.getBase(),
6809 compress.getIndices());
6817 llvm_unreachable(
"Unexpected 1DMaskFormat on CompressStoreFolder");
6822void CompressStoreOp::getCanonicalizationPatterns(RewritePatternSet &results,
6823 MLIRContext *context) {
6824 results.
add<CompressStoreFolder>(context);
6827FailureOr<std::optional<SmallVector<Value>>>
6828CompressStoreOp::bubbleDownCasts(OpBuilder &builder) {
6837void ShapeCastOp::inferResultRanges(ArrayRef<ConstantIntRanges> argRanges,
6839 setResultRanges(getResult(), argRanges.front());
6842std::optional<SmallVector<int64_t, 4>> ShapeCastOp::getShapeForUnroll() {
6843 return llvm::to_vector<4>(getResultVectorType().
getShape());
6846LogicalResult ShapeCastOp::verify() {
6848 VectorType sourceType = getSourceVectorType();
6849 VectorType resultType = getResultVectorType();
6857 int64_t sourceNElms = sourceType.getNumElements();
6858 int64_t resultNElms = resultType.getNumElements();
6859 if (sourceNElms != resultNElms) {
6860 return emitOpError() <<
"has different number of elements at source ("
6861 << sourceNElms <<
") and result (" << resultNElms
6866 int64_t sourceNScalableDims = sourceType.getNumScalableDims();
6867 int64_t resultNScalableDims = resultType.getNumScalableDims();
6868 if (sourceNScalableDims != resultNScalableDims)
6869 return emitOpError() <<
"has different number of scalable dims at source ("
6870 << sourceNScalableDims <<
") and result ("
6871 << resultNScalableDims <<
")";
6882bool ShapeCastOp::isBroadcastLike() {
6883 auto srcType = getSourceVectorType();
6884 auto resType = getResultVectorType();
6887 std::pair<VectorDim, VectorDim> mismatchingDims;
6889 BroadcastableToResult::Success)
6896 size_t rankDiff = resType.getRank() - srcType.getRank();
6901 if (!llvm::all_of(resType.getShape().take_front(rankDiff),
6902 [](int64_t dim) { return dim == 1; }))
6906 return resType.getShape().take_back(srcType.getRank()) == srcType.getShape();
6913static bool isOrderPreserving(TransposeOp transpose) {
6914 ArrayRef<int64_t> permutation = transpose.getPermutation();
6915 VectorType sourceType = transpose.getSourceVectorType();
6916 ArrayRef<int64_t> inShape = sourceType.getShape();
6917 ArrayRef<bool> inDimIsScalable = sourceType.getScalableDims();
6918 auto isNonScalableUnitDim = [&](int64_t dim) {
6919 return inShape[dim] == 1 && !inDimIsScalable[dim];
6921 int64_t current = 0;
6922 for (
auto p : permutation) {
6923 if (!isNonScalableUnitDim(p)) {
6933OpFoldResult ShapeCastOp::fold(FoldAdaptor adaptor) {
6935 VectorType resultType =
getType();
6938 if (getSource().
getType() == resultType)
6942 if (
auto precedingShapeCast = getSource().getDefiningOp<ShapeCastOp>()) {
6943 setOperand(precedingShapeCast.getSource());
6948 if (
auto transpose = getSource().getDefiningOp<TransposeOp>()) {
6949 if (isOrderPreserving(transpose)) {
6950 setOperand(transpose.getVector());
6958 if (
auto bcastOp = getSource().getDefiningOp<BroadcastOp>()) {
6959 if (bcastOp.getSourceType() == resultType)
6960 return bcastOp.getSource();
6964 if (
auto denseAttr =
6965 dyn_cast_if_present<DenseElementsAttr>(adaptor.getSource()))
6966 return denseAttr.reshape(
getType());
6982static VectorType trimTrailingUnitDims(VectorType oldType) {
6983 ArrayRef<int64_t> oldShape = oldType.getShape();
6984 ArrayRef<int64_t> newShape = oldShape;
6986 ArrayRef<bool> oldScalableDims = oldType.getScalableDims();
6987 ArrayRef<bool> newScalableDims = oldScalableDims;
6989 while (!newShape.empty() && newShape.back() == 1 && !newScalableDims.back()) {
6990 newShape = newShape.drop_back(1);
6991 newScalableDims = newScalableDims.drop_back(1);
6996 if (newShape.empty()) {
6997 newShape = oldShape.take_back();
6998 newScalableDims = oldScalableDims.take_back();
7001 return VectorType::get(newShape, oldType.getElementType(), newScalableDims);
7010 ArrayRef<int64_t> oldShape = oldType.getShape();
7011 ArrayRef<int64_t> newShape = oldShape;
7013 ArrayRef<bool> oldScalableDims = oldType.getScalableDims();
7014 ArrayRef<bool> newScalableDims = oldScalableDims;
7016 while (!newShape.empty() && newShape.front() == 1 &&
7017 !newScalableDims.front()) {
7018 newShape = newShape.drop_front(1);
7019 newScalableDims = newScalableDims.drop_front(1);
7024 if (newShape.empty()) {
7025 newShape = oldShape.take_back();
7026 newScalableDims = oldScalableDims.take_back();
7029 return VectorType::get(newShape, oldType.getElementType(), newScalableDims);
7032enum class UnitDimSide { Leading, Trailing };
7055template <UnitDimS
ide S
ide>
7056class ShapeCastCreateMaskFolderBoundaryUnitDim final
7057 :
public OpRewritePattern<ShapeCastOp> {
7061 LogicalResult matchAndRewrite(ShapeCastOp shapeOp,
7062 PatternRewriter &rewriter)
const override {
7063 Value shapeOpSrc = shapeOp->getOperand(0);
7064 auto createMaskOp = shapeOpSrc.
getDefiningOp<vector::CreateMaskOp>();
7065 auto constantMaskOp = shapeOpSrc.
getDefiningOp<vector::ConstantMaskOp>();
7066 if (!createMaskOp && !constantMaskOp)
7069 VectorType shapeOpResTy = shapeOp.getResultVectorType();
7070 VectorType shapeOpSrcTy = shapeOp.getSourceVectorType();
7072 VectorType newVecType = (Side == UnitDimSide::Trailing)
7073 ? trimTrailingUnitDims(shapeOpSrcTy)
7076 if (newVecType != shapeOpResTy) {
7077 return (Side == UnitDimSide::Trailing)
7079 shapeOp,
"Non-trailing-unit-dim dropping shape_cast Op")
7081 shapeOp,
"Non-leading-unit-dim dropping shape_cast Op");
7084 auto numDimsToDrop = shapeOpSrcTy.getRank() - shapeOpResTy.getRank();
7087 if (!numDimsToDrop) {
7088 return (Side == UnitDimSide::Trailing)
7090 shapeOp,
"Non-trailing-unit-dim dropping shape_cast Op")
7092 shapeOp,
"Non-leading-unit-dim dropping shape_cast Op");
7096 auto maskOperands = createMaskOp.getOperands();
7097 size_t numOperands = maskOperands.size();
7099 auto maskOperandsToDrop =
7100 (Side == UnitDimSide::Trailing)
7101 ? maskOperands.take_back(numOperands - numDimsToDrop)
7102 : maskOperands.take_front(numOperands - numDimsToDrop);
7106 if (llvm::all_of(maskOperandsToDrop, [](Value maskDim) {
7108 return !cst || (cst.value() != 1);
7112 auto newMaskOperands = (Side == UnitDimSide::Trailing)
7113 ? maskOperands.drop_back(numDimsToDrop)
7114 : maskOperands.drop_front(numDimsToDrop);
7120 if (constantMaskOp) {
7121 auto maskDimSizes = constantMaskOp.getMaskDimSizes();
7122 size_t numDims = maskDimSizes.size();
7124 ArrayRef<int64_t> maskDimSizesToDrop =
7125 (Side == UnitDimSide::Trailing)
7126 ? maskDimSizes.take_back(numDims - numDimsToDrop)
7127 : maskDimSizes.take_front(numDims - numDimsToDrop);
7131 if (llvm::any_of(maskDimSizesToDrop,
7132 [](int64_t dim) {
return dim != 1; }))
7135 ArrayRef<int64_t> newMaskDimSizes =
7136 (Side == UnitDimSide::Trailing)
7137 ? maskDimSizes.drop_back(numDimsToDrop)
7138 : maskDimSizes.drop_front(numDimsToDrop);
7151int64_t getBroadcastStretchingFactor(ArrayRef<int64_t> srcShape,
7152 ArrayRef<int64_t> dstShape) {
7153 int stretchingFactor = 1;
7154 int numLeadingDims = dstShape.size() - srcShape.size();
7155 for (
int i = 0, e = srcShape.size(); i < e; i++) {
7156 int64_t dstDim = dstShape[numLeadingDims + i];
7157 if (srcShape[i] == 1 && dstDim != 1) {
7158 stretchingFactor *= dstDim;
7161 return stretchingFactor;
7165class ShapeCastBroadcastFolder final :
public OpRewritePattern<ShapeCastOp> {
7169 LogicalResult matchAndRewrite(ShapeCastOp shapeCastOp,
7170 PatternRewriter &rewriter)
const override {
7172 shapeCastOp.getSource().getDefiningOp<vector::BroadcastOp>();
7176 auto srcVectorType = dyn_cast<VectorType>(broadcastOp.getSourceType());
7177 bool srcIsScalar = !srcVectorType;
7185 VectorType dstVectorType = shapeCastOp.getResultVectorType();
7186 ArrayRef<int64_t> dstShape = dstVectorType.getShape();
7187 ArrayRef<int64_t> srcShape =
7188 srcIsScalar ? ArrayRef<int64_t>{} : srcVectorType.getShape();
7189 ArrayRef<int64_t> broadcastShape =
7190 broadcastOp.getResultVectorType().getShape();
7194 BroadcastableToResult::Success) {
7202 if (srcVectorType.getNumElements() != 1) {
7203 if (getBroadcastStretchingFactor(srcShape, dstShape) !=
7204 getBroadcastStretchingFactor(srcShape, broadcastShape)) {
7211 broadcastOp.getSource());
7230class FoldShapeCastOfFromElements final :
public OpRewritePattern<ShapeCastOp> {
7234 LogicalResult matchAndRewrite(ShapeCastOp shapeCastOp,
7235 PatternRewriter &rewriter)
const override {
7236 auto fromElements = shapeCastOp.getSource().getDefiningOp<FromElementsOp>();
7241 shapeCastOp, shapeCastOp.getResultVectorType(),
7242 fromElements.getElements());
7249void ShapeCastOp::getCanonicalizationPatterns(RewritePatternSet &results,
7250 MLIRContext *context) {
7251 results.
add<ShapeCastCreateMaskFolderBoundaryUnitDim<UnitDimSide::Leading>,
7252 ShapeCastCreateMaskFolderBoundaryUnitDim<UnitDimSide::Trailing>,
7253 ShapeCastBroadcastFolder, FoldShapeCastOfFromElements>(context);
7260LogicalResult BitCastOp::verify() {
7261 auto sourceVectorType = getSourceVectorType();
7262 auto resultVectorType = getResultVectorType();
7264 for (int64_t i = 0, e = sourceVectorType.getRank() - 1; i < e; i++) {
7265 if (sourceVectorType.getDimSize(i) != resultVectorType.getDimSize(i))
7266 return emitOpError(
"dimension size mismatch at: ") << i;
7269 DataLayout dataLayout = DataLayout::closest(*
this);
7270 auto sourceElementBits =
7272 auto resultElementBits =
7275 if (sourceVectorType.getRank() == 0) {
7276 if (sourceElementBits != resultElementBits)
7277 return emitOpError(
"source/result bitwidth of the 0-D vector element "
7278 "types must be equal");
7279 }
else if (sourceElementBits * sourceVectorType.getShape().back() !=
7280 resultElementBits * resultVectorType.getShape().back()) {
7282 "source/result bitwidth of the minor 1-D vectors must be equal");
7288OpFoldResult BitCastOp::fold(FoldAdaptor adaptor) {
7294 if (
auto otherOp = getSource().getDefiningOp<BitCastOp>()) {
7295 if (getResult().
getType() == otherOp.getSource().getType())
7296 return otherOp.getSource();
7298 setOperand(otherOp.getSource());
7302 Attribute sourceConstant = adaptor.getSource();
7303 if (!sourceConstant)
7306 Type srcElemType = getSourceVectorType().getElementType();
7307 Type dstElemType = getResultVectorType().getElementType();
7309 if (
auto floatPack = llvm::dyn_cast<DenseFPElementsAttr>(sourceConstant)) {
7310 if (floatPack.isSplat()) {
7311 auto splat = floatPack.getSplatValue<FloatAttr>();
7314 if (srcElemType.
isF16() && dstElemType.
isF32()) {
7315 uint32_t bits =
static_cast<uint32_t
>(
7316 splat.getValue().bitcastToAPInt().getZExtValue());
7318 bits = (bits << 16) | (bits & 0xffff);
7319 APInt intBits(32, bits);
7320 APFloat floatBits(llvm::APFloat::IEEEsingle(), intBits);
7326 if (
auto intPack = llvm::dyn_cast<DenseIntElementsAttr>(sourceConstant)) {
7327 if (intPack.isSplat()) {
7328 auto splat = intPack.getSplatValue<IntegerAttr>();
7330 if (llvm::isa<IntegerType>(dstElemType) && srcElemType.
isIntOrFloat()) {
7335 if (dstBitWidth > srcBitWidth && dstBitWidth % srcBitWidth == 0) {
7336 APInt intBits = splat.getValue().zext(dstBitWidth);
7339 for (uint64_t i = 0; i < dstBitWidth / srcBitWidth - 1; i++)
7340 intBits = (intBits << srcBitWidth) | intBits;
7350std::optional<SmallVector<int64_t, 4>> BitCastOp::getShapeForUnroll() {
7351 return llvm::to_vector<4>(getResultVectorType().
getShape());
7358static SmallVector<int64_t, 8> extractShape(MemRefType memRefType) {
7359 auto vectorType = llvm::dyn_cast<VectorType>(memRefType.getElementType());
7360 SmallVector<int64_t, 8> res(memRefType.getShape());
7362 res.append(vectorType.getShape().begin(), vectorType.getShape().end());
7368void TypeCastOp::build(OpBuilder &builder, OperationState &
result,
7370 result.addOperands(source);
7371 MemRefType memRefType = llvm::cast<MemRefType>(source.
getType());
7372 VectorType vectorType =
7373 VectorType::get(extractShape(memRefType),
7375 result.addTypes(MemRefType::get({}, vectorType, MemRefLayoutAttrInterface(),
7376 memRefType.getMemorySpace()));
7379LogicalResult TypeCastOp::verify() {
7380 MemRefType canonicalType =
getMemRefType().canonicalizeStridedLayout();
7381 if (!canonicalType.getLayout().isIdentity())
7382 return emitOpError(
"expects operand to be a memref with identity layout");
7383 if (!getResultMemRefType().getLayout().isIdentity())
7384 return emitOpError(
"expects result to be a memref with identity layout");
7385 if (getResultMemRefType().getMemorySpace() !=
7387 return emitOpError(
"expects result in same memory space");
7390 auto resultType = getResultMemRefType();
7394 "expects result and operand with same underlying scalar type: ")
7396 if (extractShape(sourceType) != extractShape(resultType))
7398 "expects concatenated result and operand shapes to be equal: ")
7407void vector::TransposeOp::build(OpBuilder &builder, OperationState &
result,
7408 Value vector, ArrayRef<int64_t> permutation) {
7409 VectorType vt = llvm::cast<VectorType>(vector.
getType());
7410 SmallVector<int64_t, 4> transposedShape(vt.getRank());
7411 SmallVector<bool, 4> transposedScalableDims(vt.getRank());
7412 for (
unsigned i = 0; i < permutation.size(); ++i) {
7413 transposedShape[i] = vt.getShape()[permutation[i]];
7414 transposedScalableDims[i] = vt.getScalableDims()[permutation[i]];
7417 result.addOperands(vector);
7418 result.addTypes(VectorType::get(transposedShape, vt.getElementType(),
7419 transposedScalableDims));
7420 result.addAttribute(TransposeOp::getPermutationAttrName(
result.name),
7424OpFoldResult vector::TransposeOp::fold(FoldAdaptor adaptor) {
7427 llvm::dyn_cast_if_present<SplatElementsAttr>(adaptor.getVector()))
7428 return splat.reshape(getResultVectorType());
7445 if (getSourceVectorType() == getResultVectorType() &&
7446 isOrderPreserving(*
this))
7452LogicalResult vector::TransposeOp::verify() {
7453 VectorType vectorType = getSourceVectorType();
7454 VectorType resultType = getResultVectorType();
7455 int64_t rank = resultType.getRank();
7456 if (vectorType.getRank() != rank)
7457 return emitOpError(
"vector result rank mismatch: ") << rank;
7459 ArrayRef<int64_t> perm = getPermutation();
7460 int64_t size = perm.size();
7462 return emitOpError(
"transposition length mismatch: ") << size;
7463 SmallVector<bool, 8> seen(rank,
false);
7464 for (
const auto &ta : llvm::enumerate(perm)) {
7465 if (ta.value() < 0 || ta.value() >= rank)
7466 return emitOpError(
"transposition index out of range: ") << ta.value();
7467 if (seen[ta.value()])
7468 return emitOpError(
"duplicate position index: ") << ta.value();
7469 seen[ta.value()] =
true;
7470 if (resultType.getDimSize(ta.index()) != vectorType.getDimSize(ta.value()))
7471 return emitOpError(
"dimension size mismatch at: ") << ta.value();
7476std::optional<SmallVector<int64_t, 4>> TransposeOp::getShapeForUnroll() {
7477 return llvm::to_vector<4>(getResultVectorType().
getShape());
7480void TransposeOp::inferResultRanges(ArrayRef<ConstantIntRanges> argRanges,
7482 setResultRanges(getResult(), argRanges.front());
7488class TransposeFolder final :
public OpRewritePattern<vector::TransposeOp> {
7492 LogicalResult matchAndRewrite(vector::TransposeOp transposeOp,
7493 PatternRewriter &rewriter)
const override {
7495 auto composePermutations = [](ArrayRef<int64_t> permutation1,
7496 ArrayRef<int64_t> permutation2) {
7497 SmallVector<int64_t, 4>
result;
7498 for (
auto index : permutation2)
7499 result.push_back(permutation1[index]);
7504 vector::TransposeOp parentTransposeOp =
7505 transposeOp.getVector().getDefiningOp<vector::TransposeOp>();
7506 if (!parentTransposeOp)
7509 SmallVector<int64_t, 4> permutation = composePermutations(
7510 parentTransposeOp.getPermutation(), transposeOp.getPermutation());
7513 transposeOp, transposeOp.getResult().
getType(),
7514 parentTransposeOp.getVector(), permutation);
7520class FoldTransposeSplat final :
public OpRewritePattern<TransposeOp> {
7524 LogicalResult matchAndRewrite(TransposeOp transposeOp,
7525 PatternRewriter &rewriter)
const override {
7526 Value splat = getScalarSplatSource(transposeOp.getVector());
7531 transposeOp, transposeOp.getResultVectorType(), splat);
7537class FoldTransposeCreateMask final :
public OpRewritePattern<TransposeOp> {
7541 LogicalResult matchAndRewrite(TransposeOp transpOp,
7542 PatternRewriter &rewriter)
const override {
7543 Value transposeSrc = transpOp.getVector();
7544 auto createMaskOp = transposeSrc.
getDefiningOp<vector::CreateMaskOp>();
7545 auto constantMaskOp = transposeSrc.
getDefiningOp<vector::ConstantMaskOp>();
7546 if (!createMaskOp && !constantMaskOp)
7551 ArrayRef<int64_t> permutation = transpOp.getPermutation();
7554 auto maskOperands = createMaskOp.getOperands();
7555 SmallVector<Value> newOperands(maskOperands.begin(), maskOperands.end());
7559 transpOp, transpOp.getResultVectorType(), newOperands);
7564 auto maskDimSizes = constantMaskOp.getMaskDimSizes();
7568 transpOp, transpOp.getResultVectorType(), newMaskDimSizes);
7574class FoldTransposeShapeCast final :
public OpRewritePattern<TransposeOp> {
7578 LogicalResult matchAndRewrite(TransposeOp transposeOp,
7579 PatternRewriter &rewriter)
const override {
7581 transposeOp.getVector().getDefiningOp<vector::ShapeCastOp>();
7584 if (!isOrderPreserving(transposeOp))
7587 VectorType resultType = transposeOp.getType();
7594 shapeCastOp.getSource());
7613class FoldTransposeFromElements final :
public OpRewritePattern<TransposeOp> {
7616 LogicalResult matchAndRewrite(vector::TransposeOp transposeOp,
7617 PatternRewriter &rewriter)
const override {
7618 auto fromElementsOp =
7619 transposeOp.getVector().getDefiningOp<vector::FromElementsOp>();
7620 if (!fromElementsOp)
7623 VectorType srcTy = fromElementsOp.getDest().getType();
7624 VectorType dstTy = transposeOp.getType();
7626 ArrayRef<int64_t> permutation = transposeOp.getPermutation();
7627 int64_t rank = srcTy.getRank();
7630 SmallVector<int64_t> inversePerm(rank, 0);
7631 for (int64_t i = 0; i < rank; ++i)
7632 inversePerm[permutation[i]] = i;
7634 ArrayRef<int64_t> srcShape = srcTy.getShape();
7635 ArrayRef<int64_t> dstShape = dstTy.getShape();
7636 SmallVector<int64_t> srcIdx(rank, 0);
7637 SmallVector<int64_t> dstIdx(rank, 0);
7641 auto elementsOld = fromElementsOp.getElements();
7642 SmallVector<Value> elementsNew;
7643 int64_t dstNumElements = dstTy.getNumElements();
7644 elementsNew.reserve(dstNumElements);
7648 for (int64_t linearIdx = 0; linearIdx < dstNumElements; ++linearIdx) {
7652 for (int64_t j = 0; j < rank; ++j)
7653 srcIdx[j] = dstIdx[inversePerm[j]];
7655 int64_t srcLin =
linearize(srcIdx, srcStrides);
7657 elementsNew.push_back(elementsOld[srcLin]);
7691class FoldTransposeBroadcast :
public OpRewritePattern<vector::TransposeOp> {
7694 FoldTransposeBroadcast(MLIRContext *context, PatternBenefit benefit = 1)
7695 : OpRewritePattern<vector::TransposeOp>(context, benefit) {}
7697 LogicalResult matchAndRewrite(vector::TransposeOp transpose,
7698 PatternRewriter &rewriter)
const override {
7704 "not preceded by a broadcast");
7707 auto inputType = dyn_cast<VectorType>(
broadcast.getSourceType());
7708 VectorType outputType = transpose.getResultVectorType();
7711 bool inputIsScalar = !inputType;
7712 if (inputIsScalar) {
7718 ArrayRef<int64_t> permutation = transpose.getPermutation();
7719 ArrayRef<int64_t> inputShape = inputType.getShape();
7720 int64_t inputRank = inputType.getRank();
7721 int64_t outputRank = transpose.getType().getRank();
7722 int64_t deltaRank = outputRank - inputRank;
7725 for (
int inputIndex = 0; inputIndex < inputRank; ++inputIndex) {
7726 bool notOne = inputShape[inputIndex] != 1;
7727 bool prevNotOne = (inputIndex != 0 && inputShape[inputIndex - 1] != 1);
7728 bool groupEndFound = notOne || prevNotOne;
7729 if (groupEndFound) {
7730 int high = inputIndex + deltaRank;
7734 for (
int i = low; i < high; ++i) {
7735 if (permutation[i] < low || permutation[i] >= high) {
7737 transpose,
"permutation not local to group");
7751 vector::BroadcastableToResult::Success &&
7752 "not broadcastable directly to transpose output");
7763void vector::TransposeOp::getCanonicalizationPatterns(
7764 RewritePatternSet &results, MLIRContext *context) {
7765 results.
add<FoldTransposeCreateMask, FoldTransposeShapeCast, TransposeFolder,
7766 FoldTransposeSplat, FoldTransposeFromElements,
7767 FoldTransposeBroadcast>(context);
7774void ConstantMaskOp::build(OpBuilder &builder, OperationState &
result,
7776 assert(kind == ConstantMaskKind::AllTrue ||
7777 kind == ConstantMaskKind::AllFalse);
7778 build(builder,
result, type,
7779 kind == ConstantMaskKind::AllTrue
7781 : SmallVector<int64_t>(type.getRank(), 0));
7784LogicalResult ConstantMaskOp::verify() {
7785 auto resultType = llvm::cast<VectorType>(getResult().
getType());
7787 if (resultType.getRank() == 0) {
7788 if (getMaskDimSizes().size() != 1)
7789 return emitError(
"array attr must have length 1 for 0-D vectors");
7790 auto dim = getMaskDimSizes()[0];
7791 if (dim != 0 && dim != 1)
7792 return emitError(
"mask dim size must be either 0 or 1 for 0-D vectors");
7797 if (
static_cast<int64_t
>(getMaskDimSizes().size()) != resultType.getRank())
7799 "must specify array attr of size equal vector result rank");
7802 auto resultShape = resultType.getShape();
7803 auto resultScalableDims = resultType.getScalableDims();
7804 ArrayRef<int64_t> maskDimSizes = getMaskDimSizes();
7805 for (
const auto [index, maskDimSize] : llvm::enumerate(maskDimSizes)) {
7806 if (maskDimSize < 0 || maskDimSize > resultShape[index])
7808 "array attr of size out of bounds of vector result dimension size");
7809 if (resultScalableDims[index] && maskDimSize != 0 &&
7810 maskDimSize != resultShape[index])
7812 "only supports 'none set' or 'all set' scalable dimensions");
7816 bool anyZeros = llvm::is_contained(maskDimSizes, 0);
7817 bool allZeros = llvm::all_of(maskDimSizes, [](int64_t s) {
return s == 0; });
7818 if (anyZeros && !allZeros)
7819 return emitOpError(
"expected all mask dim sizes to be zeros, "
7820 "as a result of conjunction with zero mask dim");
7824bool ConstantMaskOp::isAllOnesMask() {
7827 if (resultType.getRank() == 0) {
7828 assert(getMaskDimSizes().size() == 1 &&
"invalid sizes for zero rank mask");
7829 return getMaskDimSizes()[0] == 1;
7831 for (
const auto [resultSize, maskDimSize] :
7832 llvm::zip_equal(resultType.getShape(), getMaskDimSizes())) {
7833 if (maskDimSize < resultSize)
7839OpFoldResult ConstantMaskOp::fold(FoldAdaptor adaptor) {
7840 ArrayRef<int64_t> bounds = getMaskDimSizes();
7843 auto createBoolSplat = [&](
bool x) {
7849 if (vectorSizes.empty()) {
7850 assert(bounds.size() == 1 &&
"invalid sizes for zero rank mask");
7851 return createBoolSplat(bounds[0] == 1);
7854 if (bounds == vectorSizes)
7855 return createBoolSplat(
true);
7856 if (llvm::all_of(bounds, [](int64_t x) {
return x == 0; }))
7857 return createBoolSplat(
false);
7858 return OpFoldResult();
7865void CreateMaskOp::build(OpBuilder &builder, OperationState &
result,
7867 ArrayRef<OpFoldResult> mixedOperands) {
7868 SmallVector<Value> operands =
7870 build(builder,
result, type, operands);
7873LogicalResult CreateMaskOp::verify() {
7874 auto vectorType = llvm::cast<VectorType>(getResult().
getType());
7876 if (vectorType.getRank() == 0) {
7879 "must specify exactly one operand for 0-D create_mask");
7881 llvm::cast<VectorType>(getResult().
getType()).getRank()) {
7883 "must specify an operand for each result vector dimension");
7913class CreateMaskFolder final :
public OpRewritePattern<CreateMaskOp> {
7917 LogicalResult matchAndRewrite(CreateMaskOp createMaskOp,
7918 PatternRewriter &rewriter)
const override {
7919 VectorType maskType = createMaskOp.getVectorType();
7920 ArrayRef<int64_t> maskTypeDimSizes = maskType.getShape();
7921 ArrayRef<bool> maskTypeDimScalableFlags = maskType.getScalableDims();
7924 constexpr std::array<int64_t, 1> rankZeroShape{1};
7925 constexpr std::array<bool, 1> rankZeroScalableDims{
false};
7926 if (maskType.getRank() == 0) {
7927 maskTypeDimSizes = rankZeroShape;
7928 maskTypeDimScalableFlags = rankZeroScalableDims;
7933 SmallVector<int64_t, 4> constantDims;
7934 for (
auto [i, dimSize] : llvm::enumerate(createMaskOp.getOperands())) {
7939 if (maskTypeDimScalableFlags[i] && intSize >= 0)
7941 constantDims.push_back(*intSize);
7945 if (vscaleMultiplier < maskTypeDimSizes[i])
7947 constantDims.push_back(*vscaleMultiplier);
7954 for (
auto [value, maskDimSize] : llvm::zip(constantDims, maskTypeDimSizes))
7955 value = std::clamp<int64_t>(value, 0, maskDimSize);
7958 if (llvm::is_contained(constantDims, 0))
7959 constantDims.assign(constantDims.size(), 0);
7970void CreateMaskOp::getCanonicalizationPatterns(RewritePatternSet &results,
7971 MLIRContext *context) {
7972 results.
add<CreateMaskFolder>(context);
7980 OpBuilder &builder, OperationState &
result, Value mask,
7981 Operation *maskableOp,
7982 function_ref<
void(OpBuilder &, Operation *)> maskRegionBuilder) {
7983 assert(maskRegionBuilder &&
7984 "builder callback for 'maskRegion' must be present");
7986 result.addOperands(mask);
7987 OpBuilder::InsertionGuard guard(builder);
7988 Region *maskRegion =
result.addRegion();
7990 maskRegionBuilder(builder, maskableOp);
7995 Value mask, Operation *maskableOp,
7996 function_ref<
void(OpBuilder &, Operation *)> maskRegionBuilder) {
7997 build(builder,
result, resultTypes, mask, Value(), maskableOp,
8003 Value mask, Value passthru, Operation *maskableOp,
8004 function_ref<
void(OpBuilder &, Operation *)> maskRegionBuilder) {
8005 build(builder,
result, mask, maskableOp, maskRegionBuilder);
8007 result.addOperands(passthru);
8008 result.addTypes(resultTypes);
8011ParseResult MaskOp::parse(OpAsmParser &parser, OperationState &
result) {
8013 result.regions.reserve(1);
8014 Region &maskRegion = *
result.addRegion();
8019 OpAsmParser::UnresolvedOperand mask;
8024 OpAsmParser::UnresolvedOperand passthru;
8026 if (parsePassthru.succeeded() && parser.
parseOperand(passthru))
8033 MaskOp::ensureTerminator(maskRegion, builder,
result.location);
8044 SmallVector<Type> resultTypes;
8047 result.types.append(resultTypes);
8053 if (parsePassthru.succeeded()) {
8054 if (resultTypes.empty())
8057 "expects a result if passthru operand is provided");
8066void mlir::vector::MaskOp::print(OpAsmPrinter &p) {
8067 p <<
" " << getMask();
8069 p <<
", " << getPassthru();
8073 Block *singleBlock = &getMaskRegion().getBlocks().front();
8079 getOperation()->getDiscardableAttrDictionary().getValue());
8081 p <<
" : " << getMask().getType();
8082 if (getNumResults() > 0)
8083 p <<
" -> " << getResultTypes();
8086void MaskOp::ensureTerminator(Region ®ion, Builder &builder, Location loc) {
8089 OpTrait::SingleBlockImplicitTerminator<vector::YieldOp>::Impl<
8090 MaskOp>::ensureTerminator(region, builder, loc);
8096 if (isa<vector::YieldOp>(block.
back()))
8104 OpTrait::SingleBlockImplicitTerminator<vector::YieldOp>::Impl<
8105 MaskOp>::ensureTerminator(region, builder, loc);
8111 Operation *maskedOp = &block.
front();
8112 opBuilder.setInsertionPointToEnd(&block);
8113 vector::YieldOp::create(opBuilder, loc, maskedOp->
getResults());
8116LogicalResult MaskOp::verify() {
8118 Block &block = getMaskRegion().getBlocks().
front();
8120 return emitOpError(
"expects a terminator within the mask region");
8123 if (numMaskRegionOps > 2)
8124 return emitOpError(
"expects only one operation to mask");
8127 auto terminator = dyn_cast<vector::YieldOp>(block.
back());
8129 return emitOpError(
"expects a terminator within the mask region");
8131 if (terminator->getNumOperands() != getNumResults())
8133 "expects number of results to match mask region yielded values");
8136 if (numMaskRegionOps == 1)
8139 auto maskableOp = dyn_cast<MaskableOpInterface>(block.
front());
8141 return emitOpError(
"expects a MaskableOpInterface within the mask region");
8145 return emitOpError(
"expects number of results to match maskable operation "
8146 "number of results");
8148 if (!llvm::equal(maskableOp->
getResults(), terminator.getOperands()))
8149 return emitOpError(
"expects all the results from the MaskableOpInterface "
8150 "to match all the values returned by the terminator");
8152 if (!llvm::equal(maskableOp->
getResultTypes(), getResultTypes()))
8154 "expects result type to match maskable operation result type");
8157 [](Type t) { return llvm::isa<VectorType>(t); }) > 1)
8158 return emitOpError(
"multiple vector results not supported");
8161 Type expectedMaskType = maskableOp.getExpectedMaskType();
8162 if (getMask().
getType() != expectedMaskType)
8163 return emitOpError(
"expects a ")
8164 << expectedMaskType <<
" mask for the maskable operation";
8167 Value passthru = getPassthru();
8169 if (!maskableOp.supportsPassthru())
8171 "doesn't expect a passthru argument for this maskable operation");
8174 return emitOpError(
"expects result when passthru argument is provided");
8177 return emitOpError(
"expects passthru type to match result type");
8197static LogicalResult foldEmptyMaskOp(MaskOp maskOp, MaskOp::FoldAdaptor adaptor,
8198 SmallVectorImpl<OpFoldResult> &results) {
8199 if (!maskOp.isEmpty() || maskOp.hasPassthru())
8202 Block *block = maskOp.getMaskBlock();
8203 auto terminator = cast<vector::YieldOp>(block->
front());
8204 if (terminator.getNumOperands() == 0)
8208 llvm::append_range(results, terminator.getOperands());
8212LogicalResult MaskOp::fold(FoldAdaptor adaptor,
8213 SmallVectorImpl<OpFoldResult> &results) {
8214 if (succeeded(foldEmptyMaskOp(*
this, adaptor, results)))
8224 Operation *maskableOp = getMaskableOp();
8230 llvm::append_range(results, maskableOp->
getResults());
8246class CanonializeEmptyMaskOp :
public OpRewritePattern<MaskOp> {
8249 LogicalResult matchAndRewrite(MaskOp maskOp,
8250 PatternRewriter &rewriter)
const override {
8251 if (!maskOp.isEmpty())
8254 if (!maskOp.hasPassthru())
8261 VectorType maskType = maskOp.getMask().getType();
8262 for (Type resultType : maskOp.getResultTypes()) {
8263 auto vecResultType = dyn_cast<VectorType>(resultType);
8264 if (!vecResultType || vecResultType.getShape() != maskType.getShape())
8268 Block *block = maskOp.getMaskBlock();
8269 auto terminator = cast<vector::YieldOp>(block->
front());
8270 assert(terminator.getNumOperands() == 1 &&
8271 "expected one result when passthru is provided");
8274 maskOp, maskOp.getResultTypes(), maskOp.getMask(),
8275 terminator.getOperand(0), maskOp.getPassthru());
8281void MaskOp::getCanonicalizationPatterns(RewritePatternSet &results,
8282 MLIRContext *context) {
8283 results.
add<CanonializeEmptyMaskOp>(context);
8289Operation *MaskOp::getMaskableOp() {
8290 Block *block = getMaskBlock();
8294 return &block->
front();
8298bool MaskOp::hasPassthru() {
return getPassthru() != Value(); }
8304LogicalResult ScanOp::verify() {
8305 VectorType srcType = getSourceType();
8306 VectorType initialType = getInitialValueType();
8308 int64_t srcRank = srcType.getRank();
8309 int64_t reductionDim = getReductionDim();
8310 if (reductionDim >= srcRank)
8311 return emitOpError(
"reduction dimension ")
8312 << reductionDim <<
" has to be less than " << srcRank;
8315 int64_t initialValueRank = initialType.getRank();
8316 if (initialValueRank != srcRank - 1)
8317 return emitOpError(
"initial value rank ")
8318 << initialValueRank <<
" has to be equal to " << srcRank - 1;
8321 ArrayRef<int64_t> srcShape = srcType.getShape();
8322 ArrayRef<int64_t> initialValueShapes = initialType.getShape();
8323 SmallVector<int64_t> expectedShape;
8324 for (
int i = 0; i < srcRank; i++) {
8325 if (i != reductionDim)
8326 expectedShape.push_back(srcShape[i]);
8328 if (!llvm::equal(initialValueShapes, expectedShape)) {
8329 return emitOpError(
"incompatible input/initial value shapes");
8333 Type eltType = getDestType().getElementType();
8335 return emitOpError(
"unsupported reduction type ")
8336 << eltType <<
" for kind '" << stringifyCombiningKind(getKind())
8343 RewritePatternSet &patterns, PatternBenefit benefit) {
8345 .
add<CreateMaskFolder, MaskedLoadFolder, MaskedStoreFolder, GatherFolder,
8346 ScatterFolder, ExpandLoadFolder, CompressStoreFolder,
8347 StridedSliceConstantMaskFolder, TransposeFolder>(
8352 CombiningKind kind, Value v1, Value acc,
8353 arith::FastMathFlagsAttr fastmath,
8360 case CombiningKind::ADD:
8362 result =
b.createOrFold<arith::AddIOp>(loc, v1, acc);
8363 else if (llvm::isa<FloatType>(t1) && llvm::isa<FloatType>(tAcc))
8364 result =
b.createOrFold<arith::AddFOp>(loc, v1, acc, fastmath);
8366 llvm_unreachable(
"invalid value types for ADD reduction");
8368 case CombiningKind::AND:
8370 result =
b.createOrFold<arith::AndIOp>(loc, v1, acc);
8372 case CombiningKind::MAXNUMF:
8373 assert(llvm::isa<FloatType>(t1) && llvm::isa<FloatType>(tAcc) &&
8374 "expected float values");
8375 result =
b.createOrFold<arith::MaxNumFOp>(loc, v1, acc, fastmath);
8377 case CombiningKind::MAXIMUMNUMF:
8378 assert(llvm::isa<FloatType>(t1) && llvm::isa<FloatType>(tAcc) &&
8379 "expected float values");
8380 result =
b.createOrFold<arith::MaximumNumFOp>(loc, v1, acc, fastmath);
8382 case CombiningKind::MAXIMUMF:
8383 assert(llvm::isa<FloatType>(t1) && llvm::isa<FloatType>(tAcc) &&
8384 "expected float values");
8385 result =
b.createOrFold<arith::MaximumFOp>(loc, v1, acc, fastmath);
8387 case CombiningKind::MINNUMF:
8388 assert(llvm::isa<FloatType>(t1) && llvm::isa<FloatType>(tAcc) &&
8389 "expected float values");
8390 result =
b.createOrFold<arith::MinNumFOp>(loc, v1, acc, fastmath);
8392 case CombiningKind::MINIMUMNUMF:
8393 assert(llvm::isa<FloatType>(t1) && llvm::isa<FloatType>(tAcc) &&
8394 "expected float values");
8395 result =
b.createOrFold<arith::MinimumNumFOp>(loc, v1, acc, fastmath);
8397 case CombiningKind::MINIMUMF:
8398 assert(llvm::isa<FloatType>(t1) && llvm::isa<FloatType>(tAcc) &&
8399 "expected float values");
8400 result =
b.createOrFold<arith::MinimumFOp>(loc, v1, acc, fastmath);
8402 case CombiningKind::MAXSI:
8404 result =
b.createOrFold<arith::MaxSIOp>(loc, v1, acc);
8406 case CombiningKind::MINSI:
8408 result =
b.createOrFold<arith::MinSIOp>(loc, v1, acc);
8410 case CombiningKind::MAXUI:
8412 result =
b.createOrFold<arith::MaxUIOp>(loc, v1, acc);
8414 case CombiningKind::MINUI:
8416 result =
b.createOrFold<arith::MinUIOp>(loc, v1, acc);
8418 case CombiningKind::MUL:
8420 result =
b.createOrFold<arith::MulIOp>(loc, v1, acc);
8421 else if (llvm::isa<FloatType>(t1) && llvm::isa<FloatType>(tAcc))
8422 result =
b.createOrFold<arith::MulFOp>(loc, v1, acc, fastmath);
8424 llvm_unreachable(
"invalid value types for MUL reduction");
8426 case CombiningKind::OR:
8428 result =
b.createOrFold<arith::OrIOp>(loc, v1, acc);
8430 case CombiningKind::XOR:
8432 result =
b.createOrFold<arith::XOrIOp>(loc, v1, acc);
8436 assert(
result &&
"unknown CombiningKind");
8444void StepOp::inferResultRanges(ArrayRef<ConstantIntRanges> argRanges,
8446 auto resultType = cast<VectorType>(
getType());
8447 if (resultType.isScalable()) {
8453 uint64_t maxIndex = resultType.getDimSize(0) - 1;
8454 APInt umin = APInt::getZero(bitwidth);
8455 APInt umax = APInt::getMaxValue(bitwidth).ugt(maxIndex)
8456 ? APInt(bitwidth, maxIndex)
8457 : APInt::getMaxValue(bitwidth);
8488struct StepCompareFolder :
public OpRewritePattern<StepOp> {
8491 LogicalResult matchAndRewrite(StepOp stepOp,
8492 PatternRewriter &rewriter)
const override {
8493 const int64_t stepSize = stepOp.getResult().getType().getNumElements();
8495 for (OpOperand &use : stepOp.getResult().getUses()) {
8496 auto cmpiOp = dyn_cast<arith::CmpIOp>(use.getOwner());
8501 const unsigned stepOperandNumber = use.getOperandNumber();
8502 if (stepOperandNumber != 0)
8506 unsigned constOperandNumber = 1;
8507 Value otherOperand = cmpiOp.getOperand(constOperandNumber);
8508 std::optional<int64_t> maybeConstValue =
8510 if (!maybeConstValue.has_value())
8513 int64_t constValue = maybeConstValue.value();
8514 arith::CmpIPredicate pred = cmpiOp.getPredicate();
8516 auto maybeSplat = [&]() -> std::optional<bool> {
8518 if ((pred == arith::CmpIPredicate::ult ||
8519 pred == arith::CmpIPredicate::uge) &&
8520 stepSize <= constValue)
8521 return pred == arith::CmpIPredicate::ult;
8524 if ((pred == arith::CmpIPredicate::ule ||
8525 pred == arith::CmpIPredicate::ugt) &&
8526 stepSize - 1 <= constValue) {
8527 return pred == arith::CmpIPredicate::ule;
8531 if ((pred == arith::CmpIPredicate::eq ||
8532 pred == arith::CmpIPredicate::ne) &&
8533 stepSize <= constValue)
8534 return pred == arith::CmpIPredicate::ne;
8536 return std::nullopt;
8539 if (!maybeSplat.has_value())
8544 auto type = dyn_cast<VectorType>(cmpiOp.getResult().getType());
8549 Value splat = mlir::arith::ConstantOp::create(rewriter, cmpiOp.getLoc(),
8561void StepOp::getCanonicalizationPatterns(RewritePatternSet &results,
8562 MLIRContext *context) {
8563 results.
add<StepCompareFolder>(context);
8573 Operation *maskableOp) {
8574 assert(maskableOp->
getBlock() &&
"MaskableOp must be inserted into a block");
8586 Operation *maskableOp, Value mask,
8591 return MaskOp::create(builder, maskableOp->
getLoc(),
8594 return MaskOp::create(builder, maskableOp->
getLoc(),
8607 Value newValue, Value passthru) {
8611 return arith::SelectOp::create(builder, newValue.
getLoc(), newValue.
getType(),
8612 mask, newValue, passthru);
8623struct InterleaveDeinterleaveFolder :
public OpRewritePattern<InterleaveOp> {
8626 LogicalResult matchAndRewrite(InterleaveOp interleaveOp,
8627 PatternRewriter &rewriter)
const override {
8628 auto lhsDefOp = interleaveOp.getLhs().getDefiningOp<DeinterleaveOp>();
8629 auto rhsDefOp = interleaveOp.getRhs().getDefiningOp<DeinterleaveOp>();
8630 if (!lhsDefOp || !rhsDefOp || lhsDefOp != rhsDefOp)
8632 for (
auto [idx, operand] : llvm::enumerate(interleaveOp.getOperands())) {
8633 if (cast<OpResult>(operand).getResultNumber() != idx)
8636 rewriter.
replaceOp(interleaveOp, lhsDefOp.getSource());
8642void InterleaveOp::getCanonicalizationPatterns(RewritePatternSet &results,
8643 MLIRContext *context) {
8644 results.
add<InterleaveDeinterleaveFolder>(context);
8647OpFoldResult InterleaveOp::fold(FoldAdaptor adaptor) {
8649 auto splat = dyn_cast_if_present<SplatElementsAttr>(adaptor.getLhs());
8650 if (!splat || adaptor.getLhs() != adaptor.getRhs())
8652 return SplatElementsAttr::get(getResultVectorType(),
8653 splat.getSplatValue<Attribute>());
8656std::optional<SmallVector<int64_t, 4>> InterleaveOp::getShapeForUnroll() {
8657 return llvm::to_vector<4>(getResultVectorType().
getShape());
8664std::optional<SmallVector<int64_t, 4>> DeinterleaveOp::getShapeForUnroll() {
8665 return llvm::to_vector<4>(getResultVectorType().
getShape());
8672#define GET_ATTRDEF_CLASSES
8673#include "mlir/Dialect/Vector/IR/VectorAttributes.cpp.inc"
8675#define GET_OP_CLASSES
8676#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 VectorType trimLeadingUnitDims(VectorType oldType)
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)