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"
80 if (
auto denseElts = llvm::dyn_cast<DenseIntElementsAttr>(c.getValue())) {
82 for (
bool b : denseElts.getValues<
bool>())
85 else if (!
b && val <= 0)
99 auto shape = m.getType().getShape();
101 bool allFalse =
true;
102 for (
auto [maskIdx, dimSize] : llvm::zip_equal(masks,
shape)) {
103 if (maskIdx < dimSize)
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 return llvm::isa<FloatType>(elementType);
185 VectorType vectorType) {
186 unsigned elementVectorRank = 0;
187 VectorType elementVectorType =
188 llvm::dyn_cast<VectorType>(shapedType.getElementType());
189 if (elementVectorType)
190 elementVectorRank += elementVectorType.getRank();
191 return vectorType.getRank() - elementVectorRank;
195 VectorType vectorType) {
198 if (shapedType.getRank() == 0 &&
204 shapedType.getRank(),
206 shapedType.getContext());
213 vector::TransferReadOp read) {
214 auto readMask = read.getMask();
215 auto writeMask = write.getMask();
221 bool couldBeSameSplat = readMask && (!writeMask || writeMask == readMask);
222 if (!couldBeSameSplat)
239 vector::TransferReadOp read) {
240 return !defWrite.hasOutOfBoundsDim() &&
241 defWrite.getIndices() == read.getIndices() &&
242 defWrite.getVectorType() == read.getVectorType() &&
243 defWrite.getPermutationMap() == read.getPermutationMap() &&
244 ((!defWrite.getMask() && !read.getMask()) ||
249 vector::TransferWriteOp priorWrite) {
250 return priorWrite.getIndices() == write.getIndices() &&
251 priorWrite.getMask() == write.getMask() &&
252 priorWrite.getVectorType() == write.getVectorType() &&
253 priorWrite.getPermutationMap() == write.getPermutationMap();
257 VectorTransferOpInterface transferA, VectorTransferOpInterface transferB,
258 bool testDynamicValueUsingBounds) {
260 if (transferA.getVectorType() != transferB.getVectorType())
262 unsigned rankOffset = transferA.getLeadingShapedRank();
263 for (
unsigned i = 0, e = transferA.getIndices().size(); i < e; i++) {
264 Value indexA = transferA.getIndices()[i];
265 Value indexB = transferB.getIndices()[i];
269 if (i < rankOffset) {
272 if (cstIndexA.has_value() && cstIndexB.has_value()) {
273 if (*cstIndexA != *cstIndexB)
277 if (testDynamicValueUsingBounds) {
280 FailureOr<uint64_t> delta =
282 if (succeeded(delta) && *delta != 0)
285 FailureOr<bool> testEqual =
287 if (succeeded(testEqual) && !testEqual.value())
293 int64_t vectorDim = transferA.getVectorType().getDimSize(i - rankOffset);
294 if (cstIndexA.has_value() && cstIndexB.has_value()) {
295 int64_t distance = std::abs(*cstIndexA - *cstIndexB);
296 if (distance >= vectorDim)
300 if (testDynamicValueUsingBounds) {
303 FailureOr<int64_t> delta =
305 if (succeeded(delta) && std::abs(*delta) >= vectorDim)
308 FailureOr<int64_t> computeDelta =
310 if (succeeded(computeDelta)) {
311 if (std::abs(computeDelta.value()) >= vectorDim)
321 VectorTransferOpInterface transferB,
322 bool testDynamicValueUsingBounds) {
323 if (transferA.getBase() != transferB.getBase())
326 testDynamicValueUsingBounds);
336 for (
auto [posInDim, dimSize, offsetInDim] :
337 llvm::reverse(llvm::zip_equal(position,
shape, offsets))) {
339 if (posInDim < dimSize + offsetInDim)
343 posInDim = offsetInDim;
353 llvm::transform(values, std::back_inserter(ints), [](
Value value) {
355 assert(constOp &&
"Unexpected non-constant index");
356 return constOp.value();
366 foldResults, std::back_inserter(ints), [](
OpFoldResult foldResult) {
367 assert(isa<Attribute>(foldResult) &&
"Unexpected non-constant index");
368 return cast<IntegerAttr>(cast<Attribute>(foldResult)).getInt();
378 llvm::transform(foldResults, std::back_inserter(values),
380 if (
auto attr = dyn_cast<Attribute>(foldResult))
382 builder, loc, cast<IntegerAttr>(attr).getInt())
385 return cast<Value>(foldResult);
398 if (
lhs.getDefiningOp<vector::VectorScaleOp>())
400 if (
rhs.getDefiningOp<vector::VectorScaleOp>())
410 if (
auto intAttr = dyn_cast<IntegerAttr>(attr)) {
411 if (
auto intType = dyn_cast<IntegerType>(expectedType)) {
412 if (intAttr.getType() != expectedType)
413 return IntegerAttr::get(expectedType, intAttr.getInt());
419 if (
auto floatAttr = dyn_cast<FloatAttr>(attr)) {
420 auto intType = dyn_cast<IntegerType>(expectedType);
424 APFloat floatVal = floatAttr.getValue();
425 APInt intVal = floatVal.bitcastToAPInt();
426 return IntegerAttr::get(expectedType, intVal);
435 Type srcType, VectorType dstVectorType,
436 std::pair<VectorDim, VectorDim> *mismatchingDims) {
438 if (isa<VectorElementTypeInterface>(srcType) && dstVectorType &&
442 VectorType srcVectorType = llvm::dyn_cast<VectorType>(srcType);
446 int64_t srcRank = srcVectorType.getRank();
447 int64_t dstRank = dstVectorType.getRank();
448 if (srcRank > dstRank)
452 int64_t lead = dstRank - srcRank;
453 for (
int64_t dimIdx = 0; dimIdx < srcRank; ++dimIdx) {
456 bool foundMismatchingDims =
false;
459 int64_t srcDim = srcVectorType.getDimSize(dimIdx);
460 int64_t dstDim = dstVectorType.getDimSize(lead + dimIdx);
461 if (srcDim != 1 && srcDim != dstDim)
462 foundMismatchingDims =
true;
465 bool srcDimScalableFlag = srcVectorType.getScalableDims()[dimIdx];
466 bool dstDimScalableFlag = dstVectorType.getScalableDims()[lead + dimIdx];
467 if ((srcDim == 1 && srcDimScalableFlag && dstDim != 1) ||
470 (srcDimScalableFlag != dstDimScalableFlag &&
471 (srcDim != 1 || srcDimScalableFlag)))
472 foundMismatchingDims =
true;
474 if (foundMismatchingDims) {
475 if (mismatchingDims !=
nullptr) {
476 mismatchingDims->first.dim = srcDim;
477 mismatchingDims->first.isScalable = srcDimScalableFlag;
479 mismatchingDims->second.dim = dstDim;
480 mismatchingDims->second.isScalable = dstDimScalableFlag;
522struct VectorInlinerInterface :
public DialectInlinerInterface {
523 using DialectInlinerInterface::DialectInlinerInterface;
532void VectorDialect::initialize() {
534#define GET_ATTRDEF_LIST
535#include "mlir/Dialect/Vector/IR/VectorAttributes.cpp.inc"
540#include "mlir/Dialect/Vector/IR/VectorOps.cpp.inc"
543 addInterfaces<VectorInlinerInterface>();
545 declarePromisedInterfaces<memref::IndexedAccessOpInterface, LoadOp, StoreOp,
546 MaskedLoadOp, MaskedStoreOp, ExpandLoadOp,
548 declarePromisedInterfaces<bufferization::BufferizableOpInterface,
549 TransferReadOp, TransferWriteOp, GatherOp, MaskOp,
551 declarePromisedInterfaces<SubsetOpInterface, TransferReadOp,
553 declarePromisedInterface<SubsetExtractionOpInterface, TransferReadOp>();
554 declarePromisedInterface<SubsetInsertionOpInterface, TransferWriteOp>();
555 declarePromisedInterface<ConvertToLLVMPatternInterface, VectorDialect>();
566 return arith::ConstantOp::materialize(builder, value, type, loc);
582void vector::MultiDimReductionOp::build(
OpBuilder &builder,
585 CombiningKind kind) {
587 for (
const auto &en : llvm::enumerate(reductionMask))
589 reductionDims.push_back(en.index());
590 build(builder,
result, kind, source,
acc, reductionDims);
593OpFoldResult MultiDimReductionOp::fold(FoldAdaptor adaptor) {
595 if (getReductionDims().empty())
600std::optional<SmallVector<int64_t, 4>>
601MultiDimReductionOp::getShapeForUnroll() {
602 return llvm::to_vector<4>(getSourceVectorType().
getShape());
605LogicalResult MultiDimReductionOp::verify() {
608 Type inferredReturnType;
609 auto sourceScalableDims = getSourceVectorType().getScalableDims();
610 for (
auto [dimIdx, dimSize] :
611 llvm::enumerate(getSourceVectorType().
getShape()))
612 if (!llvm::any_of(getReductionDims(),
613 [dimIdx = dimIdx](
int64_t reductionDimIdx) {
614 return reductionDimIdx ==
static_cast<int64_t>(dimIdx);
616 targetShape.push_back(dimSize);
617 scalableDims.push_back(sourceScalableDims[dimIdx]);
620 if (targetShape.empty())
621 inferredReturnType = getSourceVectorType().getElementType();
623 inferredReturnType = VectorType::get(
624 targetShape, getSourceVectorType().
getElementType(), scalableDims);
625 if (
getType() != inferredReturnType)
627 <<
" is incompatible with source type "
628 << getSourceVectorType();
634Type MultiDimReductionOp::getExpectedMaskType() {
635 auto vecType = getSourceVectorType();
636 return VectorType::get(vecType.getShape(),
637 IntegerType::get(vecType.getContext(), 1),
638 vecType.getScalableDims());
647struct ElideUnitDimsInMultiDimReduction
651 LogicalResult matchAndRewrite(MultiDimReductionOp reductionOp,
652 PatternRewriter &rewriter)
const override {
653 ArrayRef<int64_t> shape = reductionOp.getSourceVectorType().getShape();
654 for (
const auto &dim :
enumerate(shape)) {
655 if (reductionOp.isReducedDim(dim.index()) && dim.value() != 1)
660 OpBuilder::InsertionGuard guard(rewriter);
663 if (reductionOp.isMasked()) {
665 rootOp = reductionOp.getMaskingOp();
666 mask = reductionOp.getMaskingOp().getMask();
668 rootOp = reductionOp;
671 Location loc = reductionOp.getLoc();
672 Value acc = reductionOp.getAcc();
674 if (
auto dstVecType = dyn_cast<VectorType>(reductionOp.getDestType())) {
676 VectorType newMaskType =
677 VectorType::get(dstVecType.getShape(), rewriter.
getI1Type(),
678 dstVecType.getScalableDims());
679 mask = vector::ShapeCastOp::create(rewriter, loc, newMaskType, mask);
681 cast = vector::ShapeCastOp::create(
682 rewriter, loc, reductionOp.getDestType(), reductionOp.getSource());
687 mask = vector::ExtractOp::create(rewriter, loc, mask);
688 cast = vector::ExtractOp::create(rewriter, loc, reductionOp.getSource());
693 cast,
nullptr, mask);
700void MultiDimReductionOp::getCanonicalizationPatterns(
702 results.
add<ElideUnitDimsInMultiDimReduction>(context);
711 arith::FastMathFlags fastMathFlags) {
717 arith::FastMathFlags fastMathFlags) {
719 llvm::cast<VectorType>(
vector.getType()).getElementType(), kind,
vector,
723LogicalResult ReductionOp::verify() {
725 int64_t rank = getSourceVectorType().getRank();
727 return emitOpError(
"unsupported reduction rank: ") << rank;
730 Type eltType = getDest().getType();
733 << eltType <<
"' for kind '" << stringifyCombiningKind(getKind())
742Type ReductionOp::getExpectedMaskType() {
743 auto vecType = getSourceVectorType();
744 return VectorType::get(vecType.getShape(),
745 IntegerType::get(vecType.getContext(), 1),
746 vecType.getScalableDims());
753 case arith::AtomicRMWKind::addf:
754 case arith::AtomicRMWKind::addi:
755 return vector::ReductionOp::create(builder,
vector.getLoc(),
756 CombiningKind::ADD,
vector);
757 case arith::AtomicRMWKind::mulf:
758 case arith::AtomicRMWKind::muli:
759 return vector::ReductionOp::create(builder,
vector.getLoc(),
760 CombiningKind::MUL,
vector);
761 case arith::AtomicRMWKind::minimumf:
762 return vector::ReductionOp::create(builder,
vector.getLoc(),
763 CombiningKind::MINIMUMF,
vector);
764 case arith::AtomicRMWKind::mins:
765 return vector::ReductionOp::create(builder,
vector.getLoc(),
766 CombiningKind::MINSI,
vector);
767 case arith::AtomicRMWKind::minu:
768 return vector::ReductionOp::create(builder,
vector.getLoc(),
769 CombiningKind::MINUI,
vector);
770 case arith::AtomicRMWKind::maximumf:
771 return vector::ReductionOp::create(builder,
vector.getLoc(),
772 CombiningKind::MAXIMUMF,
vector);
773 case arith::AtomicRMWKind::maxs:
774 return vector::ReductionOp::create(builder,
vector.getLoc(),
775 CombiningKind::MAXSI,
vector);
776 case arith::AtomicRMWKind::maxu:
777 return vector::ReductionOp::create(builder,
vector.getLoc(),
778 CombiningKind::MAXUI,
vector);
779 case arith::AtomicRMWKind::andi:
780 return vector::ReductionOp::create(builder,
vector.getLoc(),
781 CombiningKind::AND,
vector);
782 case arith::AtomicRMWKind::ori:
783 return vector::ReductionOp::create(builder,
vector.getLoc(),
784 CombiningKind::OR,
vector);
785 case arith::AtomicRMWKind::minnumf:
786 return vector::ReductionOp::create(builder,
vector.getLoc(),
787 CombiningKind::MINNUMF,
vector);
788 case arith::AtomicRMWKind::maxnumf:
789 return vector::ReductionOp::create(builder,
vector.getLoc(),
790 CombiningKind::MAXNUMF,
vector);
791 case arith::AtomicRMWKind::xori:
792 return vector::ReductionOp::create(builder,
vector.getLoc(),
793 CombiningKind::XOR,
vector);
801std::optional<SmallVector<int64_t, 4>> ReductionOp::getShapeForUnroll() {
802 return llvm::to_vector<4>(getSourceVectorType().
getShape());
809 LogicalResult matchAndRewrite(ReductionOp reductionOp,
814 cast<vector::MaskableOpInterface>(reductionOp.getOperation());
817 if (maskableOp.isMasked()) {
819 rootOp = maskableOp.getMaskingOp();
820 mask = maskableOp.getMaskingOp().getMask();
822 rootOp = reductionOp;
825 auto vectorType = reductionOp.getSourceVectorType();
826 if (vectorType.getRank() != 0 && vectorType.getDimSize(0) != 1)
829 Location loc = reductionOp.getLoc();
831 mask = ExtractOp::create(rewriter, loc, mask);
832 Value
result = ExtractOp::create(rewriter, loc, reductionOp.getVector());
834 if (Value acc = reductionOp.getAcc())
837 reductionOp.getFastmathAttr(), mask);
847 results.
add<ElideSingleElementReduction>(context);
861 getIndexingMapsAttrName(
result.name),
865 getIteratorTypesAttrName(
result.name),
868 return IteratorTypeAttr::get(builder.getContext(), t);
877 ContractionOp::getDefaultKind());
883 ArrayAttr iteratorTypes, CombiningKind kind,
884 arith::FastMathFlags fastMathFlags) {
887 result.addAttribute(getIndexingMapsAttrName(
result.name), indexingMaps);
888 result.addAttribute(getIteratorTypesAttrName(
result.name), iteratorTypes);
890 CombiningKindAttr::get(builder.
getContext(), kind));
891 if (fastMathFlags != arith::FastMathFlags::none)
893 getFastmathAttrName(
result.name),
894 arith::FastMathFlagsAttr::get(builder.
getContext(), fastMathFlags));
905 DictionaryAttr dictAttr;
919 result.attributes.append(dictAttr.getValue().begin(),
920 dictAttr.getValue().end());
926 auto iteratorTypes = dyn_cast_or_null<ArrayAttr>(
927 result.attributes.get(getIteratorTypesAttrName(
result.name)));
928 if (!iteratorTypes) {
930 <<
"expected " << getIteratorTypesAttrName(
result.name)
931 <<
" array attribute";
936 for (StringRef s : iteratorTypes.getAsValueRange<StringAttr>()) {
937 auto maybeIteratorType = symbolizeIteratorType(s);
938 if (!maybeIteratorType.has_value())
939 return parser.
emitError(loc) <<
"unexpected iterator_type (" << s <<
")";
941 iteratorTypeAttrs.push_back(
942 IteratorTypeAttr::get(parser.
getContext(), maybeIteratorType.value()));
944 result.attributes.set(getIteratorTypesAttrName(
result.name),
947 if (!
result.attributes.get(getKindAttrName(
result.name))) {
949 getKindAttrName(
result.name),
950 CombiningKindAttr::get(
result.getContext(),
951 ContractionOp::getDefaultKind()));
953 if (masksInfo.empty())
955 if (masksInfo.size() != 2)
957 "expected zero or exactly 2 vector mask operands");
958 auto lhsType = llvm::cast<VectorType>(types[0]);
959 auto rhsType = llvm::cast<VectorType>(types[1]);
961 std::array<VectorType, 2> maskTypes = {
971 auto attrNames = getTraitAttrNames();
973 traitAttrsSet.insert_range(attrNames);
975 for (
auto attr : (*this)->getAttrs()) {
976 if (attr.getName() == getIteratorTypesAttrName()) {
978 llvm::cast<ArrayAttr>(attr.getValue())
979 .getAsValueRange<IteratorTypeAttr, IteratorType>();
985 llvm::map_to_vector(iteratorTypes, [&](IteratorType t) ->
Attribute {
986 return StringAttr::get(
getContext(), stringifyIteratorType(t));
989 attrs.emplace_back(getIteratorTypesAttrName(),
990 ArrayAttr::get(
getContext(), iteratorTypeNames));
991 }
else if (traitAttrsSet.count(attr.getName().strref()) > 0) {
993 if (attr.getName() == getFastmathAttrName() &&
994 llvm::cast<arith::FastMathFlagsAttr>(attr.getValue()).getValue() ==
995 arith::FastMathFlags::none)
997 attrs.push_back(attr);
1001 auto dictAttr = DictionaryAttr::get(
getContext(), attrs);
1002 p <<
" " << dictAttr <<
" " << getLhs() <<
", ";
1003 p << getRhs() <<
", " << getAcc();
1006 p <<
" : " << getLhs().getType() <<
", " << getRhs().getType() <<
" into "
1011 const std::vector<std::pair<int64_t, int64_t>> &map) {
1012 for (
auto &dimPair : map) {
1013 if (dimPair.first < 0 || dimPair.first >= lhsType.getRank() ||
1014 dimPair.second < 0 || dimPair.second >= rhsType.getRank() ||
1015 lhsType.getDimSize(dimPair.first) != rhsType.getDimSize(dimPair.second))
1022 ContractionOp op, VectorType lhsType, VectorType rhsType,
Type accType,
1024 const std::vector<std::pair<int64_t, int64_t>> &contractingDimMap,
1025 const std::vector<std::pair<int64_t, int64_t>> &batchDimMap) {
1028 for (
auto &dimPair : contractingDimMap) {
1029 lhsContractingDimSet.insert(dimPair.first);
1030 rhsContractingDimSet.insert(dimPair.second);
1033 llvm::make_second_range(batchDimMap));
1037 for (
int64_t i = 0, e = lhsType.getRank(); i < e; ++i) {
1038 if (lhsContractingDimSet.count(i) > 0)
1040 expectedResultDims.push_back(lhsType.getDimSize(i));
1044 for (
int64_t i = 0, e = rhsType.getRank(); i < e; ++i) {
1045 if (rhsContractingDimSet.count(i) > 0 || rhsBatchDimSet.count(i) > 0)
1047 expectedResultDims.push_back(rhsType.getDimSize(i));
1051 if (expectedResultDims.empty()) {
1053 if (llvm::isa<VectorType>(resType) || llvm::isa<VectorType>(accType))
1054 return op.emitOpError(
"invalid accumulator/result vector shape");
1057 auto resVectorType = llvm::dyn_cast<VectorType>(resType);
1058 auto accVectorType = llvm::dyn_cast<VectorType>(accType);
1059 if (!resVectorType || !accVectorType)
1060 return op.emitOpError(
"invalid accumulator/result vector shape");
1066 AffineMap lhsMap = op.getIndexingMapsArray()[0];
1067 AffineMap rhsMap = op.getIndexingMapsArray()[1];
1069 return op.emitOpError(
1070 "expected all dimensions to be either a LHS or a RHS dimension");
1073 {std::make_pair(lhsType, lhsMap), std::make_pair(rhsType, rhsMap)}) {
1074 VectorType v = pair.first;
1075 auto map = pair.second;
1076 for (
unsigned idx = 0, e = v.getRank(); idx < e; ++idx) {
1077 unsigned pos = map.getDimPosition(idx);
1082 if (!llvm::all_of(extents, [](
AffineExpr e) {
return e; }))
1083 return op.emitOpError(
"expected all dimensions to get an extent as "
1084 "either a LHS or a RHS dimension");
1086 AffineMap resMap = op.getIndexingMapsArray()[2];
1091 assert(llvm::all_of(expectedMap.
getResults(),
1092 llvm::IsaPred<AffineConstantExpr>) &&
1093 "expected constant extent along all dimensions.");
1095 auto expectedShape =
1097 return cast<AffineConstantExpr>(e).getValue();
1100 VectorType::get(expectedShape, resVectorType.getElementType(),
1101 resVectorType.getScalableDims());
1102 if (resVectorType != expected || accVectorType != expected)
1103 return op.emitOpError(
1104 "invalid accumulator/result vector shape, expected: ")
1110LogicalResult ContractionOp::verify() {
1111 VectorType lhsType = getLhsType();
1112 VectorType rhsType = getRhsType();
1113 Type accType = getAccType();
1114 Type resType = getResultType();
1116 if (llvm::isa<IntegerType>(lhsType.getElementType())) {
1117 if (!lhsType.getElementType().isSignlessInteger())
1118 return emitOpError(
"only supports signless integer types");
1122 if (getIndexingMapsArray().size() != 3)
1123 return emitOpError(
"expected an indexing map for each vector operand");
1128 unsigned numIterators = getIteratorTypes().getValue().size();
1129 for (
const auto &it : llvm::enumerate(getIndexingMapsArray())) {
1130 auto index = it.index();
1131 auto map = it.value();
1132 if (map.getNumSymbols() != 0)
1134 <<
index <<
" to have no symbols";
1135 auto vectorType = llvm::dyn_cast<VectorType>(getOperand(
index).
getType());
1136 unsigned rank = vectorType ? vectorType.getShape().size() : 0;
1139 if (map.getNumDims() != numIterators)
1141 <<
index <<
" to have " << numIterators <<
" number of inputs";
1142 if (map.getNumResults() != rank)
1144 <<
index <<
" to have " << rank <<
" number of outputs";
1145 if (!map.isProjectedPermutation())
1147 <<
index <<
" to be a projected permutation of its inputs";
1150 auto contractingDimMap = getContractingDimMap();
1151 auto batchDimMap = getBatchDimMap();
1154 if (contractingDimMap.empty())
1155 return emitOpError(
"expected at least one contracting dimension pair");
1158 if (!
verifyDimMap(lhsType, rhsType, contractingDimMap))
1159 return emitOpError(
"invalid contracting dimension map");
1163 return emitOpError(
"invalid batch dimension map");
1167 contractingDimMap, batchDimMap)))
1170 if (!getKindAttr()) {
1171 return emitOpError(
"expected 'kind' attribute of type CombiningKind (e.g. "
1172 "'vector.kind<add>')");
1176 auto vectorType = llvm::dyn_cast<VectorType>(resType);
1177 auto elementType = vectorType ? vectorType.getElementType() : resType;
1179 return emitOpError(
"unsupported contraction type");
1182 return cast<IndexingMapOpInterface>(this->getOperation()).verifyImpl();
1189Type ContractionOp::getExpectedMaskType() {
1190 auto indexingMaps = this->getIndexingMapsArray();
1193 VectorType lhsType = this->getLhsType();
1194 VectorType rhsType = this->getRhsType();
1196 unsigned numVecDims = lhsIdxMap.
getNumDims();
1202 for (
auto [dimIdx, dimSize] : llvm::enumerate(lhsType.getShape())) {
1205 lhsType.getScalableDims()[dimIdx];
1207 for (
auto [dimIdx, dimSize] : llvm::enumerate(rhsType.getShape())) {
1210 rhsType.getScalableDims()[dimIdx];
1213 assert(ShapedType::isStaticShape(maskShape) &&
1214 "Mask shape couldn't be computed");
1216 return VectorType::get(maskShape,
1217 IntegerType::get(lhsType.getContext(), 1),
1218 maskShapeScalableDims);
1223 getIteratorTypesAttrName(), getKindAttrName(),
1224 getFastmathAttrName()};
1234static std::vector<std::pair<int64_t, int64_t>>
1236 IteratorType targetIteratorType,
MLIRContext *context) {
1237 std::vector<std::pair<int64_t, int64_t>> dimMap;
1238 for (
const auto &it : llvm::enumerate(iteratorTypes)) {
1239 auto iteratorType = llvm::cast<IteratorTypeAttr>(it.value()).getValue();
1240 if (iteratorType != targetIteratorType)
1246 if (lhsDim >= 0 && rhsDim >= 0)
1247 dimMap.emplace_back(lhsDim, rhsDim);
1252void ContractionOp::getIterationBounds(
1254 auto lhsShape = getLhsType().getShape();
1255 auto resVectorType = llvm::dyn_cast<VectorType>(getResultType());
1257 for (
const auto &it : llvm::enumerate(getIteratorTypes())) {
1260 auto iteratorType = llvm::cast<IteratorTypeAttr>(it.value()).getValue();
1261 if (iteratorType == IteratorType::reduction) {
1264 assert(lhsDimIndex >= 0);
1265 iterationBounds.push_back(lhsShape[lhsDimIndex]);
1270 assert(resDimIndex >= 0);
1271 assert(resVectorType !=
nullptr);
1272 iterationBounds.push_back(resVectorType.getShape()[resDimIndex]);
1276void ContractionOp::getIterationIndexMap(
1278 unsigned numMaps = getIndexingMapsArray().size();
1279 iterationIndexMap.resize(numMaps);
1280 for (
const auto &it : llvm::enumerate(getIndexingMapsArray())) {
1281 auto index = it.index();
1282 auto map = it.value();
1283 for (
unsigned i = 0, e = map.getNumResults(); i < e; ++i) {
1284 auto dim = cast<AffineDimExpr>(map.getResult(i));
1285 iterationIndexMap[
index][dim.getPosition()] = i;
1290std::vector<std::pair<int64_t, int64_t>> ContractionOp::getContractingDimMap() {
1292 return getDimMap(indexingMaps, getIteratorTypes(), IteratorType::reduction,
1296std::vector<std::pair<int64_t, int64_t>> ContractionOp::getBatchDimMap() {
1298 return getDimMap(indexingMaps, getIteratorTypes(), IteratorType::parallel,
1302std::optional<SmallVector<int64_t, 4>> ContractionOp::getShapeForUnroll() {
1304 getIterationBounds(
shape);
1326template <
typename AddOpType>
1332 auto canonicalize = [&](
Value maybeContraction,
1333 Value otherOperand) -> vector::ContractionOp {
1334 vector::ContractionOp contractionOp =
1335 dyn_cast_or_null<vector::ContractionOp>(
1338 return vector::ContractionOp();
1339 if (
auto maybeZero = dyn_cast_or_null<arith::ConstantOp>(
1340 contractionOp.getAcc().getDefiningOp())) {
1341 if (maybeZero.getValue() ==
1342 rewriter.
getZeroAttr(contractionOp.getAcc().getType())) {
1344 bvm.
map(contractionOp.getAcc(), otherOperand);
1345 auto newContraction =
1346 cast<vector::ContractionOp>(rewriter.
clone(*contractionOp, bvm));
1347 rewriter.
replaceOp(addOp, newContraction.getResult());
1348 return newContraction;
1351 return vector::ContractionOp();
1354 Value a = addOp->getOperand(0),
b = addOp->getOperand(1);
1355 vector::ContractionOp
contract = canonicalize(a,
b);
1380 setResultRanges(getResult(), argRanges.front());
1385 auto vectorTy = cast<VectorType>(source.
getType());
1410 build(builder,
result, source, dynamicPos,
1415ExtractOp::inferReturnTypes(
MLIRContext *, std::optional<Location>,
1416 ExtractOp::Adaptor adaptor,
1418 auto vectorType = llvm::cast<VectorType>(adaptor.getSource().getType());
1419 if (
static_cast<int64_t>(adaptor.getStaticPosition().size()) ==
1420 vectorType.getRank()) {
1421 inferredReturnTypes.push_back(vectorType.getElementType());
1423 auto n = std::min<size_t>(adaptor.getStaticPosition().size(),
1424 vectorType.getRank());
1425 inferredReturnTypes.push_back(VectorType::get(
1426 vectorType.getShape().drop_front(n), vectorType.getElementType(),
1427 vectorType.getScalableDims().drop_front(n)));
1432LogicalResult vector::ExtractOp::verify() {
1433 if (
auto resTy = dyn_cast<VectorType>(getResult().
getType()))
1434 if (resTy.getRank() == 0)
1436 "expected a scalar instead of a 0-d vector as the result type");
1439 auto dynamicMarkersCount =
1440 llvm::count_if(getStaticPosition(), ShapedType::isDynamic);
1441 if (
static_cast<size_t>(dynamicMarkersCount) != getDynamicPosition().size())
1443 "mismatch between dynamic and static positions (kDynamic marker but no "
1444 "corresponding dynamic position) -- this can only happen due to an "
1445 "incorrect fold/rewrite");
1446 auto position = getMixedPosition();
1447 if (position.size() >
static_cast<unsigned>(getSourceVectorType().getRank()))
1449 "expected position attribute of rank no greater than vector rank");
1450 for (
auto [idx, pos] : llvm::enumerate(position)) {
1451 if (
auto attr = dyn_cast<Attribute>(pos)) {
1452 int64_t constIdx = cast<IntegerAttr>(attr).getInt();
1454 constIdx, kPoisonIndex, getSourceVectorType().getDimSize(idx))) {
1455 return emitOpError(
"expected position attribute #")
1457 <<
" to be a non-negative integer smaller than the "
1458 "corresponding vector dimension or poison (-1)";
1465template <
typename IntType>
1467 return llvm::map_to_vector<4>(
1468 arrayAttr.getAsRange<IntegerAttr>(),
1469 [](IntegerAttr attr) { return static_cast<IntType>(attr.getInt()); });
1475 if (!extractOp.getSource().getDefiningOp<ExtractOp>())
1479 if (extractOp.hasDynamicPosition())
1483 ExtractOp currentOp = extractOp;
1485 globalPosition.append(extrPos.rbegin(), extrPos.rend());
1486 while (ExtractOp nextOp = currentOp.getSource().getDefiningOp<ExtractOp>()) {
1489 if (currentOp.hasDynamicPosition())
1492 globalPosition.append(extrPos.rbegin(), extrPos.rend());
1494 extractOp.setOperand(0, currentOp.getSource());
1497 std::reverse(globalPosition.begin(), globalPosition.end());
1498 extractOp.setStaticPosition(globalPosition);
1510class ExtractFromInsertTransposeChainState {
1512 ExtractFromInsertTransposeChainState(ExtractOp e);
1521 template <
typename ContainerA,
typename ContainerB>
1522 bool isContainedWithin(
const ContainerA &a,
const ContainerB &
b) {
1523 return a.size() <=
b.size() &&
1524 std::equal(a.begin(), a.begin() + a.size(),
b.begin());
1531 template <
typename ContainerA,
typename ContainerB>
1532 bool intersectsWhereNonNegative(
const ContainerA &a,
const ContainerB &
b) {
1533 for (
auto [elemA, elemB] : llvm::zip(a,
b)) {
1534 if (elemA < 0 || elemB < 0)
1545 return (sentinels == ArrayRef(extractPosition).drop_front(extractedRank));
1549 void updateStateForNextIteration(Value v) {
1556 LogicalResult handleTransposeOp();
1559 LogicalResult handleInsertOpWithMatchingPos(Value &res);
1574 LogicalResult handleInsertOpWithPrefixPos(Value &res);
1579 Value tryToFoldExtractOpInPlace(Value source);
1581 ExtractOp extractOp;
1583 int64_t extractedRank;
1585 InsertOp nextInsertOp;
1586 TransposeOp nextTransposeOp;
1596 SmallVector<int64_t> sentinels;
1597 SmallVector<int64_t> extractPosition;
1601ExtractFromInsertTransposeChainState::ExtractFromInsertTransposeChainState(
1603 : extractOp(e), vectorRank(extractOp.getSourceVectorType().getRank()),
1604 extractedRank(extractOp.getNumIndices()) {
1605 assert(vectorRank >= extractedRank &&
"Extracted position overflow");
1606 sentinels.reserve(vectorRank - extractedRank);
1607 for (
int64_t i = 0, e = vectorRank - extractedRank; i < e; ++i)
1608 sentinels.push_back(-(i + 1));
1610 extractOp.getStaticPosition().end());
1616LogicalResult ExtractFromInsertTransposeChainState::handleTransposeOp() {
1618 if (extractOp.hasDynamicPosition())
1621 if (!nextTransposeOp)
1624 nextTransposeOp.getPermutation(), extractOp.getContext()));
1631ExtractFromInsertTransposeChainState::handleInsertOpWithMatchingPos(
1634 if (extractOp.hasDynamicPosition() || nextInsertOp.hasDynamicPosition())
1637 ArrayRef<int64_t> insertedPos = nextInsertOp.getStaticPosition();
1638 if (insertedPos != llvm::ArrayRef(
extractPosition).take_front(extractedRank))
1641 res = nextInsertOp.getValueToStore();
1650ExtractFromInsertTransposeChainState::handleInsertOpWithPrefixPos(Value &res) {
1652 if (extractOp.hasDynamicPosition() || nextInsertOp.hasDynamicPosition())
1655 ArrayRef<int64_t> insertedPos = nextInsertOp.getStaticPosition();
1665 res = nextInsertOp.getValueToStore();
1673Value ExtractFromInsertTransposeChainState::tryToFoldExtractOpInPlace(
1676 if (extractOp.hasDynamicPosition())
1680 bool nothingToFold = (source == extractOp.getSource());
1681 if (nothingToFold || !canFold())
1685 OpBuilder
b(extractOp.getContext());
1686 extractOp.setStaticPosition(
1688 extractOp.getSourceMutable().assign(source);
1689 return extractOp.getResult();
1693Value ExtractFromInsertTransposeChainState::fold() {
1695 if (extractOp.hasDynamicPosition())
1698 Value valueToExtractFrom = extractOp.getSource();
1699 updateStateForNextIteration(valueToExtractFrom);
1700 while (nextInsertOp || nextTransposeOp) {
1703 if (succeeded(handleTransposeOp())) {
1704 valueToExtractFrom = nextTransposeOp.getVector();
1705 updateStateForNextIteration(valueToExtractFrom);
1711 if (succeeded(handleInsertOpWithMatchingPos(
result)))
1716 if (succeeded(handleInsertOpWithPrefixPos(
result)))
1717 return tryToFoldExtractOpInPlace(
result);
1721 ArrayRef<int64_t> insertedPos = nextInsertOp.getStaticPosition();
1727 valueToExtractFrom = nextInsertOp.getDest();
1728 updateStateForNextIteration(valueToExtractFrom);
1731 return tryToFoldExtractOpInPlace(valueToExtractFrom);
1736 auto hasZeroDimVectorType = [](
Type type) ->
bool {
1737 auto vecType = dyn_cast<VectorType>(type);
1738 return vecType && vecType.getRank() == 0;
1748 if (isa<BroadcastOp>(op))
1751 auto shapeCast = dyn_cast<ShapeCastOp>(op);
1759 VectorType srcType = shapeCast.getSourceVectorType();
1761 uint64_t srcRank = srcType.getRank();
1763 return dstShape.size() >= srcRank && dstShape.take_back(srcRank) == srcShape;
1789 Operation *defOp = extractOp.getSource().getDefiningOp();
1796 if (extractOp.getType() == input.
getType())
1802 auto inputType = llvm::dyn_cast<VectorType>(input.
getType());
1803 auto extractType = llvm::dyn_cast<VectorType>(extractOp.getType());
1804 unsigned inputRank = inputType ? inputType.getRank() : 0;
1805 unsigned broadcastRank = extractOp.getSourceVectorType().getRank();
1806 unsigned extractRank = extractType ? extractType.getRank() : 0;
1809 if (extractRank > inputRank)
1813 assert(inputType &&
"input must be a vector type because of previous checks");
1822 extractType.getShape() != inputShape.take_back(extractRank))
1827 unsigned deltaOverall = inputRank - extractRank;
1828 unsigned deltaBroadcast = broadcastRank - inputRank;
1832 for (
auto [i, size] : llvm::enumerate(inputShape.take_front(deltaOverall))) {
1833 newPositions[i] = size == 1 ? zero : oldPositions[i + deltaBroadcast];
1836 extractOp->setOperands(
1837 llvm::to_vector(llvm::concat<Value>(
ValueRange(input), dynPos)));
1838 extractOp.setStaticPosition(staticPos);
1839 return extractOp.getResult();
1855 if (extractOp.hasDynamicPosition())
1858 auto shuffleOp = extractOp.getSource().getDefiningOp<ShuffleOp>();
1863 if (shuffleOp.getResultVectorType().getRank() != 1)
1866 int64_t inputVecSize = shuffleOp.getV1().getType().getShape()[0];
1867 auto shuffleMask = shuffleOp.getMask();
1868 int64_t extractIdx = extractOp.getStaticPosition()[0];
1869 int64_t shuffleIdx = shuffleMask[extractIdx];
1872 if (shuffleIdx < inputVecSize) {
1873 extractOp.setOperand(0, shuffleOp.getV1());
1874 extractOp.setStaticPosition({shuffleIdx});
1876 extractOp.setOperand(0, shuffleOp.getV2());
1877 extractOp.setStaticPosition({shuffleIdx - inputVecSize});
1880 return extractOp.getResult();
1886 if (extractOp.hasDynamicPosition())
1889 auto shapeCastOp = extractOp.getSource().getDefiningOp<vector::ShapeCastOp>();
1894 auto getDimReverse = [](VectorType type,
int64_t n) {
1895 return type.getShape().take_back(n + 1).front();
1898 llvm::isa<VectorType>(extractOp.getType())
1899 ? llvm::cast<VectorType>(extractOp.getType()).getRank()
1901 if (destinationRank > shapeCastOp.getSourceVectorType().getRank())
1903 if (destinationRank > 0) {
1904 auto destinationType =
1905 llvm::cast<VectorType>(extractOp.getResult().getType());
1906 for (
int64_t i = 0; i < destinationRank; i++) {
1910 if (getDimReverse(shapeCastOp.getSourceVectorType(), i) !=
1911 getDimReverse(destinationType, i))
1918 std::reverse(extractedPos.begin(), extractedPos.end());
1921 for (
int64_t i = 0, e = extractedPos.size(); i < e; i++) {
1922 strides.push_back(stride);
1924 getDimReverse(extractOp.getSourceVectorType(), i + destinationRank);
1932 shapeCastOp.getSourceVectorType().getRank() - destinationRank;
1934 for (
int64_t i = 0; i < numDimension; i++) {
1935 newStrides.push_back(stride);
1937 getDimReverse(shapeCastOp.getSourceVectorType(), i + destinationRank);
1939 std::reverse(newStrides.begin(), newStrides.end());
1943 extractOp.setStaticPosition(newPosition);
1944 extractOp.setOperand(0, shapeCastOp.getSource());
1945 return extractOp.getResult();
1951 if (extractOp.hasDynamicPosition())
1954 auto extractStridedSliceOp =
1955 extractOp.getSource().getDefiningOp<vector::ExtractStridedSliceOp>();
1956 if (!extractStridedSliceOp)
1965 if (extractStridedSliceOp.hasNonUnitStrides())
1971 while (!sliceOffsets.empty()) {
1972 size_t lastOffset = sliceOffsets.size() - 1;
1973 if (sliceOffsets.back() != 0 ||
1974 extractStridedSliceOp.getType().getDimSize(lastOffset) !=
1975 extractStridedSliceOp.getSourceVectorType().getDimSize(lastOffset))
1977 sliceOffsets.pop_back();
1979 unsigned destinationRank = 0;
1980 if (
auto vecType = llvm::dyn_cast<VectorType>(extractOp.getType()))
1981 destinationRank = vecType.getRank();
1984 if (destinationRank > extractStridedSliceOp.getSourceVectorType().getRank() -
1985 sliceOffsets.size())
1989 assert(extractedPos.size() >= sliceOffsets.size());
1990 for (
size_t i = 0, e = sliceOffsets.size(); i < e; i++)
1991 extractedPos[i] = extractedPos[i] + sliceOffsets[i];
1992 extractOp.getSourceMutable().assign(extractStridedSliceOp.getSource());
1996 extractOp.setStaticPosition(extractedPos);
1997 return extractOp.getResult();
2003 if (extractOp.hasDynamicPosition())
2007 llvm::isa<VectorType>(extractOp.getType())
2008 ? llvm::cast<VectorType>(extractOp.getType()).getRank()
2010 auto insertOp = extractOp.getSource().getDefiningOp<InsertStridedSliceOp>();
2020 int64_t insertRankDiff = insertOp.getDestVectorType().getRank() -
2021 insertOp.getSourceVectorType().getRank();
2022 if (destinationRank > insertOp.getSourceVectorType().getRank())
2027 if (llvm::any_of(insertOp.getStrides(), [](
Attribute attr) {
2028 return llvm::cast<IntegerAttr>(attr).getInt() != 1;
2031 bool disjoint =
false;
2033 for (
unsigned dim = 0, e = extractOffsets.size(); dim < e; ++dim) {
2034 int64_t start = insertOffsets[dim];
2036 (dim < insertRankDiff)
2038 : insertOp.getSourceVectorType().getDimSize(dim - insertRankDiff);
2040 int64_t offset = extractOffsets[dim];
2042 if (start <= offset && offset < end) {
2043 if (dim >= insertRankDiff)
2044 offsetDiffs.push_back(offset - start);
2055 insertOp.getSourceVectorType().getRank() - destinationRank;
2056 for (
int64_t i = 0; i < destinationRank; i++) {
2057 if (insertOp.getSourceVectorType().getDimSize(i + srcRankDiff) !=
2058 insertOp.getDestVectorType().getDimSize(i + srcRankDiff +
2062 extractOp.getSourceMutable().assign(insertOp.getValueToStore());
2065 extractOp.setStaticPosition(offsetDiffs);
2066 return extractOp.getResult();
2070 insertOp = insertOp.getDest().getDefiningOp<InsertStridedSliceOp>();
2083 if (extractOp.hasDynamicPosition())
2087 auto fromElementsOp = extractOp.getSource().
getDefiningOp<FromElementsOp>();
2088 if (!fromElementsOp)
2092 auto vecType = llvm::cast<VectorType>(fromElementsOp.getType());
2093 if (vecType.isScalable())
2097 int64_t rank = vecType.getRank();
2099 if (extractOp.getType() != vecType.getElementType())
2102 "unexpected number of indices");
2107 for (
int i = rank - 1; i >= 0; --i) {
2108 flatIndex +=
indices[i] * stride;
2109 stride *= vecType.getDimSize(i);
2111 return fromElementsOp.getElements()[flatIndex];
2116template <
typename OpType,
typename AdaptorType>
2119 std::vector<int64_t> staticPosition = op.getStaticPosition().vec();
2120 OperandRange dynamicPosition = op.getDynamicPosition();
2123 if constexpr (std::is_same_v<OpType, ExtractOp>)
2124 vectorShape = op.getSourceVectorType().getShape();
2129 if (!dynamicPosition.size())
2136 bool opChange =
false;
2137 for (
unsigned i = 0, e = staticPosition.size(); i < e; ++i) {
2138 if (ShapedType::isStatic(staticPosition[i]))
2142 if (
auto attr = mlir::dyn_cast_if_present<IntegerAttr>(positionAttr)) {
2143 int64_t value = attr.getInt();
2147 staticPosition[i] = attr.getInt();
2152 operands.push_back(position);
2156 op.setStaticPosition(staticPosition);
2157 op.getOperation()->setOperands(operands);
2159 return op.getResult();
2169 if (!is_contained(staticPos, poisonVal))
2172 return ub::PoisonAttr::get(context);
2186 auto denseAttr = dyn_cast_if_present<DenseElementsAttr>(srcAttr);
2191 if (denseAttr.isSplat()) {
2193 if (
auto vecDstType = dyn_cast<VectorType>(extractOp.getType()))
2198 auto vecTy = cast<VectorType>(extractOp.getSourceVectorType());
2199 if (vecTy.isScalable())
2202 if (extractOp.hasDynamicPosition()) {
2217 copy(extractOp.getStaticPosition(), completePositions.begin());
2220 auto denseValuesBegin = denseAttr.value_begin<TypedAttr>() + startPos;
2223 if (
auto resVecTy = dyn_cast<VectorType>(extractOp.getType())) {
2225 denseValuesBegin, denseValuesBegin + resVecTy.getNumElements());
2228 newAttr = *denseValuesBegin;
2234OpFoldResult ExtractOp::fold(FoldAdaptor adaptor) {
2238 if (getNumIndices() == 0 && getSource().
getType() == getResult().
getType())
2245 SmallVector<Value> operands = {getSource()};
2249 getContext(), adaptor.getStaticPosition(), kPoisonIndex))
2255 if (
auto res = ExtractFromInsertTransposeChainState(*this).fold())
2270 return inplaceFolded;
2276class ExtractOpFromBroadcast final :
public OpRewritePattern<ExtractOp> {
2280 LogicalResult matchAndRewrite(ExtractOp extractOp,
2281 PatternRewriter &rewriter)
const override {
2284 VectorType outType = dyn_cast<VectorType>(extractOp.getType());
2290 BroadcastableToResult::Success)
2299class ExtractOpFromCreateMask final :
public OpRewritePattern<ExtractOp> {
2303 LogicalResult matchAndRewrite(ExtractOp extractOp,
2304 PatternRewriter &rewriter)
const override {
2306 extractOp.getSource().getDefiningOp<vector::CreateMaskOp>();
2310 VectorType extractedMaskType =
2311 llvm::dyn_cast<VectorType>(extractOp.getResult().getType());
2313 if (!extractedMaskType)
2316 auto maskOperands = createMaskOp.getOperands();
2317 ArrayRef<int64_t> extractOpPos = extractOp.getStaticPosition();
2318 VectorType maskType = createMaskOp.getVectorType();
2320 bool containsUnknownDims =
false;
2323 for (
size_t dimIdx = 0; !allFalse && dimIdx < extractOpPos.size();
2325 int64_t pos = extractOpPos[dimIdx];
2326 Value operand = maskOperands[dimIdx];
2327 auto constantOp = operand.
getDefiningOp<arith::ConstantOp>();
2330 containsUnknownDims =
true;
2334 int64_t createMaskBound =
2335 llvm::cast<IntegerAttr>(constantOp.getValue()).getInt();
2337 if (pos != ShapedType::kDynamic) {
2340 allFalse |= pos >= createMaskBound;
2341 }
else if (createMaskBound < maskType.getDimSize(dimIdx)) {
2345 containsUnknownDims =
true;
2352 }
else if (!containsUnknownDims) {
2354 extractOp, extractedMaskType,
2355 maskOperands.drop_front(extractOpPos.size()));
2364class ExtractOpFromConstantMask final :
public OpRewritePattern<ExtractOp> {
2368 LogicalResult matchAndRewrite(ExtractOp extractOp,
2369 PatternRewriter &rewriter)
const override {
2370 auto constantMaskOp =
2371 extractOp.getSource().getDefiningOp<vector::ConstantMaskOp>();
2372 if (!constantMaskOp)
2375 Type resultType = extractOp.getResult().getType();
2376 auto extractedMaskType = dyn_cast<VectorType>(resultType);
2378 ArrayRef<int64_t> extractOpPos = extractOp.getStaticPosition();
2379 ArrayRef<int64_t> maskDimSizes = constantMaskOp.getMaskDimSizes();
2381 VectorType maskType = constantMaskOp.getVectorType();
2384 for (
size_t dimIdx = 0; dimIdx < extractOpPos.size(); dimIdx++) {
2385 int64_t pos = extractOpPos[dimIdx];
2386 if (pos == ShapedType::kDynamic) {
2389 if (maskDimSizes[dimIdx] == maskType.getDimSize(dimIdx))
2398 if (pos >= maskDimSizes[dimIdx]) {
2399 if (extractedMaskType) {
2411 if (extractedMaskType) {
2415 extractOp, extractedMaskType,
2416 maskDimSizes.drop_front(extractOpPos.size()));
2429LogicalResult foldExtractFromShapeCastToShapeCast(ExtractOp extractOp,
2430 PatternRewriter &rewriter) {
2431 auto castOp = extractOp.getSource().getDefiningOp<ShapeCastOp>();
2435 VectorType sourceType = castOp.getSourceVectorType();
2436 auto targetType = dyn_cast<VectorType>(extractOp.getResult().getType());
2440 if (sourceType.getNumElements() != targetType.getNumElements())
2444 castOp.getSource());
2454LogicalResult foldExtractFromFromElements(ExtractOp extractOp,
2455 PatternRewriter &rewriter) {
2457 if (extractOp.hasDynamicPosition())
2461 auto resultType = dyn_cast<VectorType>(extractOp.getType());
2466 auto fromElementsOp = extractOp.getSource().getDefiningOp<FromElementsOp>();
2467 if (!fromElementsOp)
2469 VectorType inputType = fromElementsOp.getType();
2472 if (resultType.isScalable() || inputType.isScalable())
2477 SmallVector<int64_t> firstElementPos =
2478 llvm::to_vector(extractOp.getStaticPosition());
2479 firstElementPos.append(resultType.getRank(), 0);
2482 for (int64_t i = inputType.getRank() - 1; i >= 0; --i) {
2483 flatIndex += firstElementPos[i] * stride;
2484 stride *= inputType.getDimSize(i);
2489 extractOp, resultType,
2490 fromElementsOp.getElements().slice(flatIndex,
2491 resultType.getNumElements()));
2503struct ExtractToShapeCast final : OpRewritePattern<vector::ExtractOp> {
2505 LogicalResult matchAndRewrite(vector::ExtractOp extractOp,
2506 PatternRewriter &rewriter)
const override {
2507 VectorType sourceType = extractOp.getSourceVectorType();
2508 VectorType outType = dyn_cast<VectorType>(extractOp.getType());
2512 if (sourceType.getNumElements() != outType.getNumElements())
2514 extractOp,
"extract to vector with fewer elements");
2518 if (llvm::any_of(extractOp.getMixedPosition(),
2519 [](OpFoldResult v) { return !isConstantIntValue(v, 0); }))
2521 "leaving for extract poison folder");
2524 extractOp.getSource());
2545struct FoldExtractFromInsertUnitDim final
2546 : OpRewritePattern<vector::ExtractOp> {
2549 LogicalResult matchAndRewrite(vector::ExtractOp extractOp,
2550 PatternRewriter &rewriter)
const override {
2551 if (extractOp.hasDynamicPosition())
2554 auto insertOp = extractOp.getSource().getDefiningOp<vector::InsertOp>();
2555 if (!insertOp || insertOp.hasDynamicPosition())
2558 ArrayRef<int64_t> extractPos = extractOp.getStaticPosition();
2559 ArrayRef<int64_t> insertPos = insertOp.getStaticPosition();
2562 if (extractPos.size() >= insertPos.size() ||
2563 extractPos != insertPos.take_front(extractPos.size()))
2569 auto srcVecType = extractOp.getSourceVectorType();
2570 for (int64_t i = extractPos.size(), e = srcVecType.getRank(); i < e; ++i)
2571 if (srcVecType.getDimSize(i) != 1)
2574 Value
inserted = insertOp.getValueToStore();
2575 Type extractedType = extractOp.getResult().getType();
2576 if (isa<VectorType>(
inserted.getType())) {
2583 extractOp, extractOp.getResult().
getType(),
2584 insertOp.getValueToStore());
2592void ExtractOp::getCanonicalizationPatterns(RewritePatternSet &results,
2593 MLIRContext *context) {
2594 results.
add<ExtractOpFromBroadcast, ExtractOpFromCreateMask,
2595 ExtractOpFromConstantMask, ExtractToShapeCast,
2596 FoldExtractFromInsertUnitDim>(context);
2597 results.
add(foldExtractFromShapeCastToShapeCast);
2598 results.
add(foldExtractFromFromElements);
2603 for (
auto attr : arrayAttr)
2604 results.push_back(llvm::cast<IntegerAttr>(attr).getInt());
2611std::optional<SmallVector<int64_t, 4>> FMAOp::getShapeForUnroll() {
2622 if (operands.empty())
2625 return llvm::all_of(operands, [&](
Value operand) {
2627 return currentDef == defOp;
2645 auto fromElementsOp =
2646 toElementsOp.getSource().getDefiningOp<FromElementsOp>();
2647 if (!fromElementsOp)
2650 llvm::append_range(results, fromElementsOp.getElements());
2667 auto bcastOp = toElementsOp.getSource().getDefiningOp<BroadcastOp>();
2671 if (isa<VectorType>(bcastOp.getSource().getType()))
2674 auto resultVecType = cast<VectorType>(toElementsOp.getSource().getType());
2676 Value scalar = bcastOp.getSource();
2677 results.assign(resultVecType.getNumElements(), scalar);
2681LogicalResult ToElementsOp::fold(FoldAdaptor adaptor,
2682 SmallVectorImpl<OpFoldResult> &results) {
2687 if (
auto shapeCast = getSource().getDefiningOp<ShapeCastOp>()) {
2688 setOperand(shapeCast.getSource());
2696ToElementsOp::inferReturnTypes(MLIRContext *ctx, std::optional<Location> loc,
2697 ToElementsOp::Adaptor adaptor,
2698 SmallVectorImpl<Type> &inferredReturnTypes) {
2699 auto vecType = cast<VectorType>(adaptor.getSource().getType());
2700 Type elType = vecType.getElementType();
2701 inferredReturnTypes.append(vecType.getNumElements(), elType);
2723 auto bcastOp = toElementsOp.getSource().getDefiningOp<BroadcastOp>();
2728 auto srcType = dyn_cast<VectorType>(bcastOp.getSource().getType());
2732 auto dstType = cast<VectorType>(toElementsOp.getSource().getType());
2737 int64_t dstRank = dstShape.size();
2738 int64_t srcRank = srcShape.size();
2741 auto srcElems = vector::ToElementsOp::create(
2742 rewriter, toElementsOp.getLoc(), bcastOp.getSource());
2744 int64_t dstCount = llvm::product_of(dstShape);
2747 replacements.reserve(dstCount);
2772 for (
int64_t lin = 0; lin < dstCount; ++lin) {
2775 for (
int64_t k = 0; k < srcRank; ++k)
2776 srcIdx[k] = (srcShape[k] == 1) ? 0 : dstIdx[dstRank - srcRank + k];
2779 replacements.push_back(srcElems.getResult(srcLin));
2782 rewriter.
replaceOp(toElementsOp, replacements);
2787void ToElementsOp::getCanonicalizationPatterns(RewritePatternSet &results,
2788 MLIRContext *context) {
2789 results.
add<ToElementsOfBroadcast>(context);
2809 OperandRange fromElemsOperands = fromElementsOp.getElements();
2810 if (fromElemsOperands.empty())
2813 auto toElementsOp = fromElemsOperands[0].getDefiningOp<ToElementsOp>();
2821 Value toElementsInput = toElementsOp.getSource();
2822 if (fromElementsOp.getType() == toElementsInput.
getType() &&
2823 llvm::equal(fromElemsOperands, toElementsOp.getResults())) {
2824 return toElementsInput;
2844 if (llvm::any_of(elements, [](
Attribute attr) {
2850 auto destVecType = fromElementsOp.getDest().getType();
2851 auto destEltType = destVecType.getElementType();
2852 if (!destEltType.isIntOrIndexOrFloat() && !isa<ComplexType>(destEltType))
2857 auto convertedElements = llvm::map_to_vector(elements, [&](
Attribute attr) {
2864OpFoldResult FromElementsOp::fold(FoldAdaptor adaptor) {
2881 if (!llvm::all_equal(fromElementsOp.getElements()))
2884 fromElementsOp, fromElementsOp.getType(),
2885 fromElementsOp.getElements().front());
2913 LogicalResult matchAndRewrite(FromElementsOp fromElements,
2917 if (fromElements.getType().getNumElements() == 1)
2928 for (
auto [insertIndex, element] :
2929 llvm::enumerate(fromElements.getElements())) {
2932 auto extractOp = element.getDefiningOp<vector::ExtractOp>();
2935 "element not from vector.extract");
2940 if (insertIndex == 0) {
2941 source = extractOp.getSource();
2942 }
else if (extractOp.getSource() != source) {
2944 "element from different vector");
2948 int64_t rank = position.size();
2949 assert(rank == source.getType().getRank() &&
2950 "scalar extract must have full rank position");
2961 if (insertIndex == 0) {
2962 const int64_t numElms = fromElements.getType().getNumElements();
2965 while (
index > 0 && position[
index - 1] == 0 &&
2966 numSuffixElms < numElms) {
2967 numSuffixElms *= source.getType().getDimSize(
index - 1);
2970 if (numSuffixElms != numElms) {
2972 fromElements,
"elements do not form a suffix of source");
2974 expectedPosition = llvm::to_vector(position);
2975 combinedPosition = position.drop_back(rank -
index);
2979 else if (expectedPosition != position) {
2981 fromElements,
"elements not in ascending order (static order)");
2983 increment(expectedPosition, source.getType().getShape());
2986 auto extracted = rewriter.
createOrFold<vector::ExtractOp>(
2987 fromElements.getLoc(), source, combinedPosition);
2990 fromElements, fromElements.getType(), extracted);
2998 for (
int dim : llvm::reverse(llvm::seq<int>(0,
indices.size()))) {
3017void BroadcastOp::inferResultRanges(ArrayRef<ConstantIntRanges> argRanges,
3019 setResultRanges(getResult(), argRanges.front());
3022std::optional<SmallVector<int64_t, 4>> BroadcastOp::getShapeForUnroll() {
3023 return llvm::to_vector<4>(getResultVectorType().
getShape());
3028static llvm::SetVector<int64_t>
3031 int64_t rankDiff = dstShape.size() - srcShape.size();
3034 for (
auto [s1, s2] :
3035 llvm::zip_equal(srcShape, dstShape.drop_front(rankDiff))) {
3037 assert(s1 == 1 &&
"expected \"dim-1\" broadcasting");
3045llvm::SetVector<int64_t> BroadcastOp::computeBroadcastedUnitDims() {
3047 auto srcVectorType = llvm::dyn_cast<VectorType>(getSourceType());
3050 return ::computeBroadcastedUnitDims(srcVectorType.getShape(),
3066Value BroadcastOp::createOrFoldBroadcastOp(
3067 OpBuilder &
b, Value value, ArrayRef<int64_t> dstShape,
3068 const llvm::SetVector<int64_t> &broadcastedDims) {
3069 assert(!dstShape.empty() &&
"unexpected empty dst shape");
3072 SmallVector<int64_t> checkShape;
3073 for (
int i = 0, e = dstShape.size(); i < e; ++i) {
3074 if (broadcastedDims.contains(i))
3076 checkShape.push_back(dstShape[i]);
3078 assert(broadcastedDims.size() == dstShape.size() - checkShape.size() &&
3079 "ill-formed broadcastedDims contains values not confined to "
3082 Location loc = value.
getLoc();
3084 VectorType srcVectorType = llvm::dyn_cast<VectorType>(value.
getType());
3085 VectorType dstVectorType = VectorType::get(dstShape, elementType);
3088 if (!srcVectorType) {
3089 assert(checkShape.empty() &&
3090 "ill-formed createOrFoldBroadcastOp arguments");
3091 return b.createOrFold<vector::BroadcastOp>(loc, dstVectorType, value);
3094 assert(srcVectorType.getShape().equals(checkShape) &&
3095 "ill-formed createOrFoldBroadcastOp arguments");
3105 SmallVector<int64_t> broadcastShape, permutation(dstShape.size(), -1);
3106 broadcastShape.reserve(dstShape.size());
3122 int64_t nextSrcShapeDim = broadcastedDims.size();
3123 for (int64_t i = 0, e = dstShape.size(); i < e; ++i) {
3124 if (broadcastedDims.contains(i)) {
3129 broadcastShape.push_back(dstShape[i]);
3130 permutation[i] = broadcastShape.size() - 1;
3136 permutation[i] = nextSrcShapeDim++;
3140 llvm::append_range(broadcastShape, srcVectorType.getShape());
3145 "unexpected \"dim-1\" broadcast");
3147 VectorType broadcastType = VectorType::get(broadcastShape, elementType);
3149 vector::BroadcastableToResult::Success &&
3150 "must be broadcastable");
3151 Value res =
b.createOrFold<vector::BroadcastOp>(loc, broadcastType, value);
3154 for (int64_t i = 0, e = permutation.size(); i < e; ++i)
3155 if (permutation[i] != i)
3156 return b.createOrFold<vector::TransposeOp>(loc, res, permutation);
3161LogicalResult BroadcastOp::verify() {
3162 std::pair<VectorDim, VectorDim> mismatchingDims;
3164 getSourceType(), getResultVectorType(), &mismatchingDims);
3165 if (res == BroadcastableToResult::Success)
3167 if (res == BroadcastableToResult::SourceRankHigher)
3168 return emitOpError(
"source rank higher than destination rank");
3169 if (res == BroadcastableToResult::DimensionMismatch) {
3171 << (mismatchingDims.first.isScalable ?
"[" :
"")
3172 << mismatchingDims.first.dim
3173 << (mismatchingDims.first.isScalable ?
"]" :
"") <<
" vs. "
3174 << (mismatchingDims.second.isScalable ?
"[" :
"")
3175 << mismatchingDims.second.dim
3176 << (mismatchingDims.second.isScalable ?
"]" :
"") <<
")";
3178 if (res == BroadcastableToResult::SourceTypeNotAVector)
3179 return emitOpError(
"source type is not a vector");
3180 llvm_unreachable(
"unexpected vector.broadcast op error");
3187 auto srcShapeCast = broadcastOp.getSource().getDefiningOp<ShapeCastOp>();
3191 VectorType srcType = srcShapeCast.getSourceVectorType();
3192 VectorType destType = broadcastOp.getResultVectorType();
3200 srcShapeCast.getResultVectorType().getShape();
3203 unsigned numTrailingDims = std::min(srcShape.size(), shapecastShape.size());
3204 if (!llvm::equal(srcShape.take_back(numTrailingDims),
3205 shapecastShape.take_back(numTrailingDims)))
3208 assert(all_of(srcShape.drop_back(numTrailingDims),
3209 [](
int64_t E) { return E == 1; }) &&
3210 all_of(shapecastShape.drop_back(numTrailingDims),
3211 [](
int64_t E) { return E == 1; }) &&
3212 "ill-formed shape_cast");
3214 broadcastOp.getSourceMutable().assign(srcShapeCast.getSource());
3218OpFoldResult BroadcastOp::fold(FoldAdaptor adaptor) {
3219 if (getSourceType() == getResultVectorType())
3224 if (!adaptor.getSource())
3226 auto vectorType = getResultVectorType();
3227 if (
auto attr = llvm::dyn_cast<IntegerAttr>(adaptor.getSource())) {
3228 if (vectorType.getElementType() != attr.getType())
3232 if (
auto attr = llvm::dyn_cast<FloatAttr>(adaptor.getSource())) {
3233 if (vectorType.getElementType() != attr.getType())
3237 if (
auto attr = llvm::dyn_cast<SplatElementsAttr>(adaptor.getSource()))
3247struct BroadcastFolder :
public OpRewritePattern<BroadcastOp> {
3250 LogicalResult matchAndRewrite(BroadcastOp broadcastOp,
3251 PatternRewriter &rewriter)
const override {
3252 auto srcBroadcast = broadcastOp.getSource().getDefiningOp<BroadcastOp>();
3256 broadcastOp.getResultVectorType(),
3257 srcBroadcast.getSource());
3270struct BroadcastToShapeCast final
3271 :
public OpRewritePattern<vector::BroadcastOp> {
3273 LogicalResult matchAndRewrite(vector::BroadcastOp
broadcast,
3274 PatternRewriter &rewriter)
const override {
3276 auto sourceType = dyn_cast<VectorType>(
broadcast.getSourceType());
3279 broadcast,
"source is a scalar, shape_cast doesn't support scalar");
3283 if (sourceType.getNumElements() != outType.getNumElements()) {
3285 broadcast,
"broadcast to a greater number of elements");
3295void BroadcastOp::getCanonicalizationPatterns(RewritePatternSet &results,
3296 MLIRContext *context) {
3297 results.
add<BroadcastFolder, BroadcastToShapeCast>(context);
3304LogicalResult ShuffleOp::verify() {
3305 VectorType resultType = getResultVectorType();
3306 VectorType v1Type = getV1VectorType();
3307 VectorType v2Type = getV2VectorType();
3309 int64_t resRank = resultType.getRank();
3310 int64_t v1Rank = v1Type.getRank();
3311 int64_t v2Rank = v2Type.getRank();
3312 bool wellFormed0DCase = v1Rank == 0 && v2Rank == 0 && resRank == 1;
3313 bool wellFormedNDCase = v1Rank == resRank && v2Rank == resRank;
3314 if (!wellFormed0DCase && !wellFormedNDCase)
3318 for (int64_t r = 1; r < v1Rank; ++r) {
3319 int64_t resDim = resultType.getDimSize(r);
3320 int64_t v1Dim = v1Type.getDimSize(r);
3321 int64_t v2Dim = v2Type.getDimSize(r);
3322 if (resDim != v1Dim || v1Dim != v2Dim)
3326 ArrayRef<int64_t> mask = getMask();
3327 int64_t maskLength = mask.size();
3328 if (maskLength <= 0)
3330 if (maskLength != resultType.getDimSize(0))
3333 int64_t indexSize = (v1Type.getRank() == 0 ? 1 : v1Type.getDimSize(0)) +
3334 (v2Type.getRank() == 0 ? 1 : v2Type.getDimSize(0));
3335 for (
auto [idx, maskPos] : llvm::enumerate(mask)) {
3337 return emitOpError(
"mask index #") << (idx + 1) <<
" out of range";
3343ShuffleOp::inferReturnTypes(MLIRContext *, std::optional<Location> loc,
3344 ShuffleOp::Adaptor adaptor,
3345 SmallVectorImpl<Type> &inferredReturnTypes) {
3346 auto v1Type = llvm::dyn_cast<VectorType>(adaptor.getV1().getType());
3350 auto v1Rank = v1Type.getRank();
3353 SmallVector<int64_t, 4> shape;
3354 shape.reserve(v1Rank);
3355 shape.push_back(std::max<size_t>(1, adaptor.getMask().size()));
3358 llvm::append_range(shape, v1Type.getShape().drop_front());
3359 inferredReturnTypes.push_back(
3360 VectorType::get(shape, v1Type.getElementType()));
3364template <
typename T>
3367 return idxArr.size() == width && llvm::all_of(idxArr, [&expected](T value) {
3368 return value == expected++;
3375 auto v1Type = op.getV1VectorType();
3376 auto v2Type = op.getV2VectorType();
3377 auto mask = op.getMask();
3390 if (!isV1Poison && !isV2Poison)
3393 int64_t v1Size = op.getV1VectorType().getDimSize(0);
3394 bool changed =
false;
3396 for (
int64_t &idx : newMask) {
3397 if (idx == ShuffleOp::kPoisonIndex)
3399 if ((isV1Poison && idx < v1Size) || (isV2Poison && idx >= v1Size)) {
3400 idx = ShuffleOp::kPoisonIndex;
3408 op.setMask(newMask);
3409 return op.getResult();
3418 return ub::PoisonAttr::get(context);
3425 auto v1Type = op.getV1VectorType();
3426 if (v1Type.getRank() != 1)
3438 auto v2DenseAttr = dyn_cast<DenseElementsAttr>(v2Attr);
3441 v2Elements = to_vector(v2DenseAttr.getValues<
Attribute>());
3442 poisonElement = v2Elements[0];
3445 auto v1DenseAttr = dyn_cast<DenseElementsAttr>(v1Attr);
3448 v1Elements = to_vector(v1DenseAttr.getValues<
Attribute>());
3449 poisonElement = v1Elements[0];
3454 int64_t v1Size = v1Type.getDimSize(0);
3455 for (
int64_t maskIdx : mask) {
3458 if (maskIdx == ShuffleOp::kPoisonIndex) {
3459 indexedElm = poisonElement;
3461 if (maskIdx < v1Size)
3462 indexedElm = isV1Poison ? poisonElement : v1Elements[maskIdx];
3464 indexedElm = isV2Poison ? poisonElement : v2Elements[maskIdx - v1Size];
3467 results.push_back(indexedElm);
3473OpFoldResult vector::ShuffleOp::fold(FoldAdaptor adaptor) {
3474 auto v1Type = getV1VectorType();
3476 assert(!v1Type.isScalable() && !getV2VectorType().isScalable() &&
3477 "Vector shuffle does not support scalable vectors");
3481 if (v1Type.getRank() == 0)
3489 Attribute v1Attr = adaptor.getV1(), v2Attr = adaptor.getV2();
3490 if (!v1Attr || !v2Attr)
3505struct Canonicalize0DShuffleOp :
public OpRewritePattern<ShuffleOp> {
3508 LogicalResult matchAndRewrite(ShuffleOp shuffleOp,
3509 PatternRewriter &rewriter)
const override {
3510 VectorType v1VectorType = shuffleOp.getV1VectorType();
3511 ArrayRef<int64_t> mask = shuffleOp.getMask();
3512 if (v1VectorType.getRank() > 0)
3514 if (mask.size() != 1)
3516 VectorType resType = VectorType::Builder(v1VectorType).setShape({1});
3534static Value getScalarSplatSource(Value value) {
3540 auto broadcast = dyn_cast<vector::BroadcastOp>(defOp);
3547 if (isa<VectorType>(
broadcast.getSourceType()))
3555class ShuffleSplat final :
public OpRewritePattern<ShuffleOp> {
3559 LogicalResult matchAndRewrite(ShuffleOp op,
3560 PatternRewriter &rewriter)
const override {
3561 Value splat = getScalarSplatSource(op.getV1());
3562 if (!splat || getScalarSplatSource(op.getV2()) != splat)
3572class ShuffleInterleave :
public OpRewritePattern<ShuffleOp> {
3576 LogicalResult matchAndRewrite(ShuffleOp op,
3577 PatternRewriter &rewriter)
const override {
3578 VectorType resultType = op.getResultVectorType();
3579 if (resultType.isScalable())
3581 op,
"ShuffleOp can't represent a scalable interleave");
3583 if (resultType.getRank() != 1)
3585 op,
"ShuffleOp can't represent an n-D interleave");
3587 VectorType sourceType = op.getV1VectorType();
3588 if (sourceType != op.getV2VectorType() ||
3589 sourceType.getNumElements() * 2 != resultType.getNumElements()) {
3591 op,
"ShuffleOp types don't match an interleave");
3594 ArrayRef<int64_t> shuffleMask = op.getMask();
3595 int64_t resultVectorSize = resultType.getNumElements();
3596 for (
int i = 0, e = resultVectorSize / 2; i < e; ++i) {
3597 int64_t maskValueA = shuffleMask[i * 2];
3598 int64_t maskValueB = shuffleMask[(i * 2) + 1];
3599 if (maskValueA != i || maskValueB != (resultVectorSize / 2) + i)
3601 "ShuffleOp mask not interleaving");
3617class FoldUnusedShuffleOperand final :
public OpRewritePattern<ShuffleOp> {
3621 LogicalResult matchAndRewrite(ShuffleOp op,
3622 PatternRewriter &rewriter)
const override {
3624 if (llvm::all_of(op.getMask(), [](int64_t mask) {
3625 return mask == ShuffleOp::kPoisonIndex;
3632 auto replaceOperandWithPoison = [&](OpOperand &operand) {
3635 Value poison = ub::PoisonOp::create(rewriter, op.getLoc(),
3644 int64_t leadingV1Size = op.getV1VectorType().getRank() > 0
3645 ? op.getV1VectorType().getDimSize(0)
3647 bool isV1Used = llvm::any_of(op.getMask(), [&](int64_t mask) {
3648 return mask != ShuffleOp::kPoisonIndex && mask < leadingV1Size;
3650 if (!isV1Used && succeeded(replaceOperandWithPoison(op.getV1Mutable())))
3654 bool isV2Used = llvm::any_of(op.getMask(), [&](int64_t mask) {
3655 return mask != ShuffleOp::kPoisonIndex && mask >= leadingV1Size;
3657 if (!isV2Used && succeeded(replaceOperandWithPoison(op.getV2Mutable())))
3665void ShuffleOp::getCanonicalizationPatterns(RewritePatternSet &results,
3666 MLIRContext *context) {
3667 results.
add<ShuffleSplat, ShuffleInterleave, Canonicalize0DShuffleOp,
3668 FoldUnusedShuffleOperand>(context);
3675void vector::InsertOp::inferResultRanges(ArrayRef<ConstantIntRanges> argRanges,
3677 setResultRanges(getResult(), argRanges[0].rangeUnion(argRanges[1]));
3680void vector::InsertOp::build(OpBuilder &builder, OperationState &
result,
3681 Value source, Value dest) {
3682 auto vectorTy = cast<VectorType>(dest.
getType());
3683 build(builder,
result, source, dest,
3684 SmallVector<int64_t>(vectorTy.getRank(), 0));
3687void vector::InsertOp::build(OpBuilder &builder, OperationState &
result,
3688 Value source, Value dest, int64_t position) {
3689 build(builder,
result, source, dest, ArrayRef<int64_t>{position});
3692void vector::InsertOp::build(OpBuilder &builder, OperationState &
result,
3693 Value source, Value dest, OpFoldResult position) {
3694 build(builder,
result, source, dest, ArrayRef<OpFoldResult>{position});
3697void vector::InsertOp::build(OpBuilder &builder, OperationState &
result,
3698 Value source, Value dest,
3699 ArrayRef<int64_t> position) {
3700 SmallVector<OpFoldResult> posVals;
3701 posVals.reserve(position.size());
3702 llvm::transform(position, std::back_inserter(posVals),
3704 build(builder,
result, source, dest, posVals);
3707void vector::InsertOp::build(OpBuilder &builder, OperationState &
result,
3708 Value source, Value dest,
3709 ArrayRef<OpFoldResult> position) {
3710 SmallVector<int64_t> staticPos;
3711 SmallVector<Value> dynamicPos;
3713 build(builder,
result, source, dest, dynamicPos,
3717LogicalResult InsertOp::verify() {
3718 if (
auto srcTy = dyn_cast<VectorType>(getValueToStoreType()))
3719 if (srcTy.getRank() == 0)
3721 "expected a scalar instead of a 0-d vector as the source operand");
3723 SmallVector<OpFoldResult> position = getMixedPosition();
3724 auto destVectorType = getDestVectorType();
3725 if (position.size() >
static_cast<unsigned>(destVectorType.getRank()))
3727 "expected position attribute of rank no greater than dest vector rank");
3728 auto srcVectorType = llvm::dyn_cast<VectorType>(getValueToStoreType());
3729 if (srcVectorType &&
3730 (
static_cast<unsigned>(srcVectorType.getRank()) + position.size() !=
3731 static_cast<unsigned>(destVectorType.getRank())))
3732 return emitOpError(
"expected position attribute rank + source rank to "
3733 "match dest vector rank");
3734 if (!srcVectorType &&
3735 (position.size() !=
static_cast<unsigned>(destVectorType.getRank())))
3737 "expected position attribute rank to match the dest vector rank");
3738 for (
auto [idx, pos] : llvm::enumerate(position)) {
3739 if (
auto attr = dyn_cast<Attribute>(pos)) {
3740 int64_t constIdx = cast<IntegerAttr>(attr).getInt();
3742 destVectorType.getDimSize(idx))) {
3743 return emitOpError(
"expected position attribute #")
3745 <<
" to be a non-negative integer smaller than the "
3747 "dest vector dimension";
3760 assert(positions.size() <= completePositions.size() &&
3761 "positions size must be less than or equal to destTy rank");
3762 copy(positions, completePositions.begin());
3770class InsertToBroadcast final :
public OpRewritePattern<InsertOp> {
3774 LogicalResult matchAndRewrite(InsertOp insertOp,
3775 PatternRewriter &rewriter)
const override {
3777 llvm::dyn_cast<VectorType>(insertOp.getValueToStoreType());
3778 if (!srcVecType || insertOp.getDestVectorType().getNumElements() !=
3779 srcVecType.getNumElements())
3782 insertOp, insertOp.getDestVectorType(), insertOp.getValueToStore());
3788class InsertSplatToSplat final :
public OpRewritePattern<InsertOp> {
3792 LogicalResult matchAndRewrite(InsertOp op,
3793 PatternRewriter &rewriter)
const override {
3795 Value splat = getScalarSplatSource(op.getValueToStore());
3796 if (!splat || getScalarSplatSource(op.getDest()) != splat)
3824class InsertChainFullyInitialized final :
public OpRewritePattern<InsertOp> {
3827 LogicalResult matchAndRewrite(InsertOp op,
3828 PatternRewriter &rewriter)
const override {
3830 VectorType destTy = op.getDestVectorType();
3831 if (destTy.isScalable())
3834 for (Operation *user : op.getResult().getUsers())
3835 if (
auto insertOp = dyn_cast<InsertOp>(user))
3836 if (insertOp.getDest() == op.getResult())
3839 InsertOp currentOp = op;
3840 SmallVector<InsertOp> chainInsertOps;
3843 if (currentOp.hasDynamicPosition())
3846 chainInsertOps.push_back(currentOp);
3847 currentOp = currentOp.getDest().getDefiningOp<InsertOp>();
3850 if (currentOp && !currentOp->hasOneUse())
3854 int64_t vectorSize = destTy.getNumElements();
3855 int64_t initializedCount = 0;
3856 SmallVector<bool> initializedDestIdxs(vectorSize,
false);
3857 SmallVector<int64_t> pendingInsertPos;
3858 SmallVector<int64_t> pendingInsertSize;
3859 SmallVector<Value> pendingInsertValues;
3861 for (
auto insertOp : chainInsertOps) {
3863 if (is_contained(insertOp.getStaticPosition(), InsertOp::kPoisonIndex))
3867 int64_t insertBeginPosition =
3872 int64_t insertSize = 1;
3873 if (
auto srcVectorType =
3874 llvm::dyn_cast<VectorType>(insertOp.getValueToStoreType()))
3875 insertSize = srcVectorType.getNumElements();
3877 assert(insertBeginPosition + insertSize <= vectorSize &&
3878 "insert would overflow the vector");
3880 for (
auto index : llvm::seq<int64_t>(insertBeginPosition,
3881 insertBeginPosition + insertSize)) {
3882 if (initializedDestIdxs[index])
3884 initializedDestIdxs[index] =
true;
3890 pendingInsertPos.push_back(insertBeginPosition);
3891 pendingInsertSize.push_back(insertSize);
3892 pendingInsertValues.push_back(insertOp.getValueToStore());
3894 if (initializedCount == vectorSize)
3899 if (initializedCount != vectorSize)
3902 SmallVector<Value> elements(vectorSize);
3903 for (
auto [insertBeginPosition, insertSize, valueToStore] :
3904 llvm::reverse(llvm::zip(pendingInsertPos, pendingInsertSize,
3905 pendingInsertValues))) {
3906 auto srcVectorType = llvm::dyn_cast<VectorType>(valueToStore.getType());
3908 if (!srcVectorType) {
3909 elements[insertBeginPosition] = valueToStore;
3913 Repeated<Type> elementToInsertTypes(insertSize,
3914 srcVectorType.getElementType());
3916 auto elementsToInsert = vector::ToElementsOp::create(
3917 rewriter, op.getLoc(), elementToInsertTypes, valueToStore);
3918 for (int64_t linearIdx = 0; linearIdx < insertSize; linearIdx++) {
3919 elements[insertBeginPosition + linearIdx] =
3920 elementsToInsert.getResult(linearIdx);
3934 int64_t maxVectorSizeFoldThreshold) {
3935 if (insertOp.hasDynamicPosition())
3938 auto denseDst = llvm::dyn_cast_if_present<DenseElementsAttr>(dstAttr);
3946 VectorType destTy = insertOp.getDestVectorType();
3947 if (destTy.isScalable())
3951 if (destTy.getNumElements() > maxVectorSizeFoldThreshold &&
3952 !insertOp->hasOneUse())
3957 if (is_contained(insertOp.getStaticPosition(), InsertOp::kPoisonIndex))
3964 Type destEltType = destTy.getElementType();
3968 if (
auto denseSource = llvm::dyn_cast<DenseElementsAttr>(srcAttr)) {
3969 for (
auto value : denseSource.getValues<
Attribute>())
3975 auto allValues = llvm::to_vector(denseDst.getValues<
Attribute>());
3976 copy(insertedValues, allValues.begin() + insertBeginPosition);
3985 auto destInsert = insertOp.getDest().
getDefiningOp<InsertOp>();
3989 if (insertOp.getMixedPosition() != destInsert.getMixedPosition())
3992 insertOp.
setOperand(1, destInsert.getDest());
3993 return insertOp.getResult();
3996void InsertOp::getCanonicalizationPatterns(RewritePatternSet &results,
3997 MLIRContext *context) {
3998 results.
add<InsertToBroadcast, BroadcastFolder, InsertSplatToSplat,
3999 InsertChainFullyInitialized>(context);
4002OpFoldResult InsertOp::fold(FoldAdaptor adaptor) {
4005 constexpr int64_t vectorSizeFoldThreshold = 256;
4009 if (getNumIndices() == 0 && getValueToStoreType() ==
getType())
4010 return getValueToStore();
4014 SmallVector<Value> operands = {getValueToStore(), getDest()};
4020 getContext(), adaptor.getStaticPosition(), kPoisonIndex))
4023 *
this, adaptor.getValueToStore(), adaptor.getDest(),
4024 vectorSizeFoldThreshold)) {
4028 return inplaceFolded;
4035void InsertStridedSliceOp::build(OpBuilder &builder, OperationState &
result,
4036 Value source, Value dest,
4037 ArrayRef<int64_t> offsets,
4038 ArrayRef<int64_t> strides) {
4039 result.addOperands({source, dest});
4043 result.addAttribute(InsertStridedSliceOp::getOffsetsAttrName(
result.name),
4045 result.addAttribute(InsertStridedSliceOp::getStridesAttrName(
result.name),
4050template <
typename OpType>
4054 StringRef attrName) {
4055 if (arrayAttr.size() >
shape.size())
4056 return op.emitOpError(
"expected ")
4057 << attrName <<
" attribute of rank no greater than vector rank";
4064template <
typename OpType>
4068 bool halfOpen =
true) {
4069 for (
auto attr : arrayAttr) {
4070 auto val = llvm::cast<IntegerAttr>(attr).getInt();
4074 if (val < min || val >= upper)
4075 return op.emitOpError(
"expected ") << attrName <<
" to be confined to ["
4076 <<
min <<
", " << upper <<
")";
4084template <
typename OpType>
4089 for (
auto [
index, attrDimPair] :
4090 llvm::enumerate(llvm::zip_first(arrayAttr,
shape))) {
4091 int64_t val = llvm::cast<IntegerAttr>(std::get<0>(attrDimPair)).getInt();
4095 if (val < min || val >=
max)
4096 return op.emitOpError(
"expected ")
4097 << attrName <<
" dimension " <<
index <<
" to be confined to ["
4098 <<
min <<
", " <<
max <<
")";
4108template <
typename OpType>
4113 assert(arrayAttr1.size() <=
shape.size());
4114 assert(arrayAttr2.size() <=
shape.size());
4115 for (
auto [
index, it] :
4116 llvm::enumerate(llvm::zip(arrayAttr1, arrayAttr2,
shape))) {
4117 auto val1 = llvm::cast<IntegerAttr>(std::get<0>(it)).getInt();
4118 auto val2 = llvm::cast<IntegerAttr>(std::get<1>(it)).getInt();
4122 if (val1 + val2 < 0 || val1 + val2 >=
max)
4123 return op.emitOpError(
"expected sum(")
4124 << attrName1 <<
", " << attrName2 <<
") dimension " <<
index
4125 <<
" to be confined to [" <<
min <<
", " <<
max <<
")";
4133 return IntegerAttr::get(IntegerType::get(context, 64), APInt(64, v));
4135 return ArrayAttr::get(context, llvm::to_vector<8>(attrs));
4138LogicalResult InsertStridedSliceOp::verify() {
4139 auto sourceVectorType = getSourceVectorType();
4140 auto destVectorType = getDestVectorType();
4141 auto offsets = getOffsetsAttr();
4142 auto strides = getStridesAttr();
4143 if (offsets.size() !=
static_cast<unsigned>(destVectorType.getRank()))
4145 "expected offsets of same size as destination vector rank");
4146 if (strides.size() !=
static_cast<unsigned>(sourceVectorType.getRank()))
4147 return emitOpError(
"expected strides of same size as source vector rank");
4148 if (sourceVectorType.getRank() > destVectorType.getRank())
4150 "expected source rank to be no greater than destination rank");
4152 auto sourceShape = sourceVectorType.getShape();
4153 auto destShape = destVectorType.getShape();
4154 SmallVector<int64_t, 4> sourceShapeAsDestShape(
4155 destShape.size() - sourceShape.size(), 0);
4156 sourceShapeAsDestShape.append(sourceShape.begin(), sourceShape.end());
4157 auto offName = InsertStridedSliceOp::getOffsetsAttrName();
4158 auto stridesName = InsertStridedSliceOp::getStridesAttrName();
4167 offName,
"source vector shape",
4171 unsigned rankDiff = destShape.size() - sourceShape.size();
4172 for (
unsigned idx = 0; idx < sourceShape.size(); ++idx) {
4173 if (sourceVectorType.getScalableDims()[idx] !=
4174 destVectorType.getScalableDims()[idx + rankDiff]) {
4175 return emitOpError(
"mismatching scalable flags (at source vector idx=")
4178 if (sourceVectorType.getScalableDims()[idx]) {
4179 auto sourceSize = sourceShape[idx];
4180 auto destSize = destShape[idx + rankDiff];
4181 if (sourceSize != destSize) {
4184 << (
" to match the corresponding base size from the input "
4186 << sourceSize << (
" vs ") << destSize << (
")");
4196class FoldInsertStridedSliceSplat final
4197 :
public OpRewritePattern<InsertStridedSliceOp> {
4201 LogicalResult matchAndRewrite(InsertStridedSliceOp insertStridedSliceOp,
4202 PatternRewriter &rewriter)
const override {
4204 auto dst = insertStridedSliceOp.getDest();
4205 auto splat = getScalarSplatSource(insertStridedSliceOp.getValueToStore());
4206 if (!splat || getScalarSplatSource(dst) != splat)
4209 rewriter.
replaceOp(insertStridedSliceOp, dst);
4216class FoldInsertStridedSliceOfExtract final
4217 :
public OpRewritePattern<InsertStridedSliceOp> {
4221 LogicalResult matchAndRewrite(InsertStridedSliceOp insertStridedSliceOp,
4222 PatternRewriter &rewriter)
const override {
4223 auto extractStridedSliceOp =
4224 insertStridedSliceOp.getValueToStore()
4225 .getDefiningOp<vector::ExtractStridedSliceOp>();
4227 if (!extractStridedSliceOp)
4230 if (extractStridedSliceOp.getOperand() != insertStridedSliceOp.getDest())
4234 if (extractStridedSliceOp.getStrides() !=
4235 insertStridedSliceOp.getStrides() ||
4236 extractStridedSliceOp.getOffsets() != insertStridedSliceOp.getOffsets())
4239 rewriter.
replaceOp(insertStridedSliceOp, insertStridedSliceOp.getDest());
4246class InsertStridedSliceConstantFolder final
4247 :
public OpRewritePattern<InsertStridedSliceOp> {
4253 static constexpr int64_t vectorSizeFoldThreshold = 256;
4255 LogicalResult matchAndRewrite(InsertStridedSliceOp op,
4256 PatternRewriter &rewriter)
const override {
4260 Attribute vectorDestCst;
4264 VectorType destTy = destVector.getType();
4265 if (destTy.isScalable())
4269 if (destTy.getNumElements() > vectorSizeFoldThreshold &&
4270 !destVector.hasOneUse())
4274 Attribute sourceCst;
4284 if (op.hasNonUnitStrides())
4287 VectorType sliceVecTy = sourceValue.getType();
4288 ArrayRef<int64_t> sliceShape = sliceVecTy.getShape();
4289 int64_t rankDifference = destTy.getRank() - sliceVecTy.getRank();
4290 SmallVector<int64_t, 4> offsets =
getI64SubArray(op.getOffsets());
4291 SmallVector<int64_t, 4> destStrides =
computeStrides(destTy.getShape());
4299 auto denseDest = llvm::cast<DenseElementsAttr>(vectorDestCst);
4300 auto denseSlice = llvm::cast<DenseElementsAttr>(sourceCst);
4301 auto sliceValuesIt = denseSlice.value_begin<Attribute>();
4302 auto newValues = llvm::to_vector(denseDest.getValues<Attribute>());
4303 SmallVector<int64_t> currDestPosition(offsets.begin(), offsets.end());
4304 MutableArrayRef<int64_t> currSlicePosition(
4305 currDestPosition.begin() + rankDifference, currDestPosition.end());
4306 ArrayRef<int64_t> sliceOffsets(offsets.begin() + rankDifference,
4309 int64_t linearizedPosition =
linearize(currDestPosition, destStrides);
4310 assert(linearizedPosition < destTy.getNumElements() &&
"Invalid index");
4311 assert(sliceValuesIt != denseSlice.value_end<Attribute>() &&
4312 "Invalid slice element");
4313 newValues[linearizedPosition] = *sliceValuesIt;
4326void vector::InsertStridedSliceOp::getCanonicalizationPatterns(
4327 RewritePatternSet &results, MLIRContext *context) {
4328 results.
add<FoldInsertStridedSliceSplat, FoldInsertStridedSliceOfExtract,
4329 InsertStridedSliceConstantFolder>(context);
4332OpFoldResult InsertStridedSliceOp::fold(FoldAdaptor adaptor) {
4333 if (getSourceVectorType() == getDestVectorType())
4334 return getValueToStore();
4343void OuterProductOp::build(OpBuilder &builder, OperationState &
result,
4344 Value
lhs, Value
rhs, Value acc) {
4349void OuterProductOp::print(OpAsmPrinter &p) {
4350 p <<
" " << getLhs() <<
", " << getRhs();
4352 p <<
", " << getAcc();
4355 p <<
" : " << getLhs().getType() <<
", " << getRhs().getType();
4358ParseResult OuterProductOp::parse(OpAsmParser &parser, OperationState &
result) {
4359 SmallVector<OpAsmParser::UnresolvedOperand, 3> operandsInfo;
4366 if (operandsInfo.size() < 2)
4368 "expected at least 2 operands");
4369 VectorType vLHS = llvm::dyn_cast<VectorType>(tLHS);
4370 VectorType vRHS = llvm::dyn_cast<VectorType>(tRHS);
4373 "expected vector type for operand #1");
4377 SmallVector<bool> scalableDimsRes{vLHS.getScalableDims()[0],
4378 vRHS.getScalableDims()[0]};
4379 resType = VectorType::get({vLHS.getDimSize(0), vRHS.getDimSize(0)},
4380 vLHS.getElementType(), scalableDimsRes);
4383 SmallVector<bool> scalableDimsRes{vLHS.getScalableDims()[0]};
4384 resType = VectorType::get({vLHS.getDimSize(0)}, vLHS.getElementType(),
4388 if (!
result.attributes.get(OuterProductOp::getKindAttrName(
result.name))) {
4389 result.attributes.append(
4390 OuterProductOp::getKindAttrName(
result.name),
4391 CombiningKindAttr::get(
result.getContext(),
4392 OuterProductOp::getDefaultKind()));
4398 (operandsInfo.size() > 2 &&
4403LogicalResult OuterProductOp::verify() {
4404 Type tRHS = getOperandTypeRHS();
4405 VectorType vLHS = getOperandVectorTypeLHS(),
4406 vRHS = llvm::dyn_cast<VectorType>(tRHS),
4407 vACC = getOperandVectorTypeACC(), vRES = getResultVectorType();
4409 if (vLHS.getRank() != 1)
4410 return emitOpError(
"expected 1-d vector for operand #1");
4414 if (vRHS.getRank() != 1)
4415 return emitOpError(
"expected 1-d vector for operand #2");
4416 if (vRES.getRank() != 2)
4418 if (vLHS.getDimSize(0) != vRES.getDimSize(0))
4419 return emitOpError(
"expected #1 operand dim to match result dim #1");
4420 if (vRHS.getDimSize(0) != vRES.getDimSize(1))
4421 return emitOpError(
"expected #2 operand dim to match result dim #2");
4422 if (vLHS.isScalable() && !vRHS.isScalable()) {
4426 "expected either both or only #2 operand dim to be scalable");
4430 if (vRES.getRank() != 1)
4432 if (vLHS.getDimSize(0) != vRES.getDimSize(0))
4433 return emitOpError(
"expected #1 operand dim to match result dim #1");
4436 if (vACC && vACC != vRES)
4437 return emitOpError(
"expected operand #3 of same type as result type");
4439 if (!getKindAttr()) {
4440 return emitOpError(
"expected 'kind' attribute of type CombiningKind (e.g. "
4441 "'vector.kind<add>')");
4446 return emitOpError(
"unsupported outerproduct type");
4455Type OuterProductOp::getExpectedMaskType() {
4456 auto vecType = this->getResultVectorType();
4457 return VectorType::get(vecType.getShape(),
4458 IntegerType::get(vecType.getContext(), 1),
4459 vecType.getScalableDims());
4473 assert(offsets.size() == sizes.size() && offsets.size() == strides.size());
4475 shape.reserve(vectorType.getRank());
4477 for (
unsigned e = offsets.size(); idx < e; ++idx)
4478 shape.push_back(llvm::cast<IntegerAttr>(sizes[idx]).getInt());
4479 for (
unsigned e = vectorType.getShape().size(); idx < e; ++idx)
4480 shape.push_back(vectorType.getShape()[idx]);
4482 return VectorType::get(
shape, vectorType.getElementType(),
4483 vectorType.getScalableDims());
4486void ExtractStridedSliceOp::build(OpBuilder &builder, OperationState &
result,
4487 Value source, ArrayRef<int64_t> offsets,
4488 ArrayRef<int64_t> sizes,
4489 ArrayRef<int64_t> strides) {
4490 result.addOperands(source);
4496 offsetsAttr, sizesAttr, stridesAttr));
4497 result.addAttribute(ExtractStridedSliceOp::getOffsetsAttrName(
result.name),
4499 result.addAttribute(ExtractStridedSliceOp::getSizesAttrName(
result.name),
4501 result.addAttribute(ExtractStridedSliceOp::getStridesAttrName(
result.name),
4505LogicalResult ExtractStridedSliceOp::verify() {
4506 auto type = getSourceVectorType();
4507 auto offsets = getOffsetsAttr();
4508 auto sizes = getSizesAttr();
4509 auto strides = getStridesAttr();
4510 if (offsets.size() != sizes.size() || offsets.size() != strides.size())
4512 "expected offsets, sizes and strides attributes of same size");
4514 auto shape = type.getShape();
4515 auto offName = getOffsetsAttrName();
4516 auto sizesName = getSizesAttrName();
4517 auto stridesName = getStridesAttrName();
4533 shape, offName, sizesName,
4538 offsets, sizes, strides);
4539 if (getResult().
getType() != resultType)
4540 return emitOpError(
"expected result type to be ") << resultType;
4542 for (
unsigned idx = 0; idx < sizes.size(); ++idx) {
4543 if (type.getScalableDims()[idx]) {
4544 auto inputDim = type.getShape()[idx];
4545 auto inputSize = llvm::cast<IntegerAttr>(sizes[idx]).getInt();
4546 if (inputDim != inputSize)
4549 << (
" to match the corresponding base size from the input "
4551 << inputSize << (
" vs ") << inputDim << (
")");
4564 auto getElement = [](
ArrayAttr array,
int idx) {
4565 return llvm::cast<IntegerAttr>(array[idx]).getInt();
4567 ArrayAttr extractOffsets = op.getOffsets();
4570 auto insertOp = op.getSource().getDefiningOp<InsertStridedSliceOp>();
4572 if (op.getSourceVectorType().getRank() !=
4573 insertOp.getSourceVectorType().getRank())
4575 ArrayAttr insertOffsets = insertOp.getOffsets();
4576 ArrayAttr insertStrides = insertOp.getStrides();
4579 if (extractOffsets.size() > insertOffsets.size())
4581 bool patialoverlap =
false;
4582 bool disjoint =
false;
4584 for (
unsigned dim = 0, e = extractOffsets.size(); dim < e; ++dim) {
4585 if (getElement(
extractStrides, dim) != getElement(insertStrides, dim))
4587 int64_t start = getElement(insertOffsets, dim);
4588 int64_t end = start + insertOp.getSourceVectorType().getDimSize(dim);
4589 int64_t offset = getElement(extractOffsets, dim);
4590 int64_t size = getElement(extractSizes, dim);
4592 if (start <= offset && offset < end) {
4595 if (offset + size > end)
4596 patialoverlap =
true;
4597 offsetDiffs.push_back(offset - start);
4604 if (!disjoint && !patialoverlap) {
4605 op.setOperand(insertOp.getValueToStore());
4608 op.setOffsetsAttr(
b.getI64ArrayAttr(offsetDiffs));
4614 insertOp = insertOp.getDest().getDefiningOp<InsertStridedSliceOp>();
4629 auto dense = llvm::dyn_cast_if_present<DenseElementsAttr>(foldInput);
4634 if (op.hasNonUnitStrides())
4637 VectorType sourceVecTy = op.getSourceVectorType();
4641 VectorType sliceVecTy = op.getType();
4643 int64_t rank = sliceVecTy.getRank();
4655 const auto denseValuesBegin = dense.value_begin<
Attribute>();
4657 sliceValues.reserve(sliceVecTy.getNumElements());
4661 assert(linearizedPosition < sourceVecTy.getNumElements() &&
4663 sliceValues.push_back(*(denseValuesBegin + linearizedPosition));
4664 }
while (succeeded(
incSlicePosition(currSlicePosition, sliceShape, offsets)));
4666 assert(
static_cast<int64_t>(sliceValues.size()) ==
4667 sliceVecTy.getNumElements() &&
4668 "Invalid number of slice elements");
4672OpFoldResult ExtractStridedSliceOp::fold(FoldAdaptor adaptor) {
4673 if (getSourceVectorType() == getResult().
getType())
4680 llvm::dyn_cast_if_present<SplatElementsAttr>(adaptor.getSource()))
4687void ExtractStridedSliceOp::getOffsets(SmallVectorImpl<int64_t> &results) {
4709class StridedSliceFolder final
4710 :
public OpRewritePattern<ExtractStridedSliceOp> {
4712 using OpRewritePattern<ExtractStridedSliceOp>::OpRewritePattern;
4714 LogicalResult matchAndRewrite(ExtractStridedSliceOp secondOp,
4715 PatternRewriter &rewriter)
const override {
4716 auto firstOp = secondOp.getSource().getDefiningOp<ExtractStridedSliceOp>();
4720 if (secondOp.hasNonUnitStrides() || firstOp.hasNonUnitStrides())
4723 SmallVector<int64_t> firstOffsets =
getI64SubArray(firstOp.getOffsets());
4724 SmallVector<int64_t> firstSizes =
getI64SubArray(firstOp.getSizes());
4725 SmallVector<int64_t> secondOffsets =
getI64SubArray(secondOp.getOffsets());
4726 SmallVector<int64_t> secondSizes =
getI64SubArray(secondOp.getSizes());
4728 unsigned newRank = std::max(firstOffsets.size(), secondOffsets.size());
4729 SmallVector<int64_t> combinedOffsets(newRank, 0);
4730 SmallVector<int64_t> combinedSizes(newRank);
4731 ArrayRef<int64_t> firstSourceShape =
4732 firstOp.getSourceVectorType().getShape();
4733 for (
unsigned i = 0; i < newRank; ++i) {
4734 int64_t off1 = (i < firstOffsets.size()) ? firstOffsets[i] : 0;
4735 int64_t off2 = (i < secondOffsets.size()) ? secondOffsets[i] : 0;
4736 combinedOffsets[i] = off1 + off2;
4738 if (i < secondSizes.size()) {
4739 combinedSizes[i] = secondSizes[i];
4740 }
else if (i < firstSizes.size()) {
4741 combinedSizes[i] = firstSizes[i];
4743 combinedSizes[i] = firstSourceShape[i];
4747 SmallVector<int64_t> combinedStrides(newRank, 1);
4749 secondOp, firstOp.getSource(), combinedOffsets, combinedSizes,
4767class StridedSliceCreateMaskFolder final
4768 :
public OpRewritePattern<ExtractStridedSliceOp> {
4772 LogicalResult matchAndRewrite(ExtractStridedSliceOp extractStridedSliceOp,
4773 PatternRewriter &rewriter)
const override {
4774 Location loc = extractStridedSliceOp.getLoc();
4778 extractStridedSliceOp.getSource().getDefiningOp<CreateMaskOp>();
4782 if (extractStridedSliceOp.hasNonUnitStrides())
4785 SmallVector<Value> maskDimSizes(createMaskOp.getOperands());
4787 SmallVector<int64_t> sliceOffsets;
4790 SmallVector<int64_t> sliceSizes;
4794 SmallVector<Value> sliceMaskDimSizes;
4795 sliceMaskDimSizes.reserve(maskDimSizes.size());
4799 for (
auto [maskDimSize, sliceOffset, sliceSize] :
4800 llvm::zip(maskDimSizes, sliceOffsets, sliceSizes)) {
4804 IntegerAttr offsetAttr =
4806 Value offset = arith::ConstantOp::create(rewriter, loc, offsetAttr);
4807 Value sliceMaskDimSize =
4808 arith::SubIOp::create(rewriter, loc, maskDimSize, offset);
4809 sliceMaskDimSizes.push_back(sliceMaskDimSize);
4814 llvm::drop_begin(maskDimSizes, sliceMaskDimSizes.size()));
4818 extractStridedSliceOp, extractStridedSliceOp.getResult().
getType(),
4826class StridedSliceConstantMaskFolder final
4827 :
public OpRewritePattern<ExtractStridedSliceOp> {
4831 LogicalResult matchAndRewrite(ExtractStridedSliceOp extractStridedSliceOp,
4832 PatternRewriter &rewriter)
const override {
4835 auto *defOp = extractStridedSliceOp.getSource().getDefiningOp();
4836 auto constantMaskOp = dyn_cast_or_null<ConstantMaskOp>(defOp);
4837 if (!constantMaskOp)
4840 if (extractStridedSliceOp.hasNonUnitStrides())
4843 ArrayRef<int64_t> maskDimSizes = constantMaskOp.getMaskDimSizes();
4845 SmallVector<int64_t> sliceOffsets;
4848 SmallVector<int64_t> sliceSizes;
4852 SmallVector<int64_t> sliceMaskDimSizes;
4853 sliceMaskDimSizes.reserve(maskDimSizes.size());
4854 for (
auto [maskDimSize, sliceOffset, sliceSize] :
4855 llvm::zip(maskDimSizes, sliceOffsets, sliceSizes)) {
4856 int64_t sliceMaskDimSize = std::max(
4857 static_cast<int64_t
>(0),
4858 std::min(sliceOffset + sliceSize, maskDimSize) - sliceOffset);
4859 sliceMaskDimSizes.push_back(sliceMaskDimSize);
4862 if (sliceMaskDimSizes.size() < maskDimSizes.size())
4863 for (
size_t i = sliceMaskDimSizes.size(); i < maskDimSizes.size(); ++i)
4864 sliceMaskDimSizes.push_back(maskDimSizes[i]);
4867 if (llvm::is_contained(sliceMaskDimSizes, 0))
4868 sliceMaskDimSizes.assign(maskDimSizes.size(), 0);
4873 extractStridedSliceOp, extractStridedSliceOp.getResult().
getType(),
4881class StridedSliceBroadcast final
4882 :
public OpRewritePattern<ExtractStridedSliceOp> {
4886 LogicalResult matchAndRewrite(ExtractStridedSliceOp op,
4887 PatternRewriter &rewriter)
const override {
4893 unsigned srcRank = srcVecType ? srcVecType.getRank() : 0;
4894 auto dstVecType = llvm::cast<VectorType>(op.getType());
4895 unsigned dstRank = dstVecType.getRank();
4896 unsigned rankDiff = dstRank - srcRank;
4900 bool needsSlice =
false;
4901 for (
unsigned i = 0; i < srcRank; i++) {
4902 if (srcVecType.getDimSize(i) != 1 &&
4903 srcVecType.getDimSize(i) != dstVecType.getDimSize(i + rankDiff)) {
4910 SmallVector<int64_t> offsets =
4912 SmallVector<int64_t> sizes =
4914 for (
unsigned i = 0; i < srcRank; i++) {
4915 if (srcVecType.getDimSize(i) == 1) {
4923 source = ExtractStridedSliceOp::create(
4924 rewriter, op->getLoc(), source, offsets, sizes,
4933class StridedSliceSplat final :
public OpRewritePattern<ExtractStridedSliceOp> {
4937 LogicalResult matchAndRewrite(ExtractStridedSliceOp op,
4938 PatternRewriter &rewriter)
const override {
4940 Value splat = getScalarSplatSource(op.getSource());
4964class ContiguousExtractStridedSliceToExtract final
4965 :
public OpRewritePattern<ExtractStridedSliceOp> {
4969 LogicalResult matchAndRewrite(ExtractStridedSliceOp op,
4970 PatternRewriter &rewriter)
const override {
4971 if (op.hasNonUnitStrides())
4973 Value source = op.getOperand();
4974 auto sourceType = cast<VectorType>(source.
getType());
4975 if (sourceType.isScalable() || sourceType.getRank() == 0)
4984 for (numOffsets = sizes.size(); numOffsets > 0; --numOffsets) {
4985 if (sizes[numOffsets - 1] != sourceType.getDimSize(numOffsets - 1))
4992 if (numOffsets == 0)
4997 if (numOffsets == sourceType.getRank() &&
4998 static_cast<int>(sizes.size()) == sourceType.getRank())
5002 for (
int i = 0; i < numOffsets; ++i) {
5010 while (numOffsets <
static_cast<int>(sizes.size()) - 1 &&
5011 sizes[numOffsets] == 1) {
5016 auto extractOffsets = ArrayRef(offsets).take_front(numOffsets);
5017 Value extract = vector::ExtractOp::create(rewriter, op->getLoc(), source,
5026void ExtractStridedSliceOp::getCanonicalizationPatterns(
5027 RewritePatternSet &results, MLIRContext *context) {
5030 results.
add<StridedSliceFolder, StridedSliceCreateMaskFolder,
5031 StridedSliceConstantMaskFolder, StridedSliceBroadcast,
5032 StridedSliceSplat, ContiguousExtractStridedSliceToExtract>(
5042void TransferReadOp::build(OpBuilder &builder, OperationState &
result,
5043 VectorType vectorType, Value source,
5045 AffineMapAttr permutationMapAttr,
5048 Type elemType = llvm::cast<ShapedType>(source.
getType()).getElementType();
5050 padding = ub::PoisonOp::create(builder,
result.location, elemType);
5053 build(builder,
result, vectorType, source,
indices, permutationMapAttr,
5054 *padding, Value(), inBoundsAttr);
5062void TransferReadOp::build(OpBuilder &builder, OperationState &
result,
5063 VectorType vectorType, Value source,
5065 AffineMap permutationMap,
5066 std::optional<ArrayRef<bool>> inBounds) {
5067 if (!permutationMap)
5069 llvm::cast<ShapedType>(source.
getType()), vectorType);
5070 auto permutationMapAttr = AffineMapAttr::get(permutationMap);
5071 auto inBoundsAttr = (inBounds && !inBounds.value().empty())
5074 SmallVector<bool>(vectorType.getRank(),
false));
5076 build(builder,
result, vectorType, source,
indices, padding,
5077 permutationMapAttr, inBoundsAttr);
5083void TransferReadOp::build(OpBuilder &builder, OperationState &
result,
5084 VectorType vectorType, Value source,
5086 std::optional<ArrayRef<bool>> inBounds) {
5088 build(builder,
result, vectorType, source,
indices, padding,
5089 AffineMap(), inBounds);
5092template <
typename EmitFun>
5096 for (
auto expr : permutationMap.
getResults()) {
5097 auto dim = dyn_cast<AffineDimExpr>(expr);
5098 auto zero = dyn_cast<AffineConstantExpr>(expr);
5100 if (zero.getValue() != 0) {
5102 "requires a projected permutation_map (at most one dim or the zero "
5103 "constant can appear in each result)");
5108 return emitOpError(
"requires a projected permutation_map (at most one "
5109 "dim or the zero constant can appear in each result)");
5111 if (seen[dim.getPosition()]) {
5113 "requires a permutation_map that is a permutation (found one dim "
5114 "used more than once)");
5116 seen[dim.getPosition()] =
true;
5123 VectorType vectorType, VectorType maskType,
5124 VectorType inferredMaskType,
AffineMap permutationMap,
5126 if (op->hasAttr(
"masked")) {
5127 return op->emitOpError(
"masked attribute has been removed. "
5128 "Use in_bounds instead.");
5131 if (!llvm::isa<MemRefType, RankedTensorType>(shapedType))
5132 return op->emitOpError(
5133 "requires source to be a memref or ranked tensor type");
5135 auto elementType = shapedType.getElementType();
5137 if (
auto vectorElementType = llvm::dyn_cast<VectorType>(elementType)) {
5139 unsigned sourceVecSize =
5141 vectorElementType.getShape().back();
5142 unsigned resultVecSize =
5144 vectorType.getShape().back();
5145 if (resultVecSize % sourceVecSize != 0)
5146 return op->emitOpError(
5147 "requires the bitwidth of the minor 1-D vector to be an integral "
5148 "multiple of the bitwidth of the minor 1-D vector of the source");
5150 unsigned sourceVecEltRank = vectorElementType.getRank();
5151 unsigned resultVecRank = vectorType.getRank();
5152 if (sourceVecEltRank > resultVecRank)
5153 return op->emitOpError(
5154 "requires source vector element and vector result ranks to match.");
5155 unsigned rankOffset = resultVecRank - sourceVecEltRank;
5158 return op->emitOpError(
"requires a permutation_map with result dims of "
5159 "the same rank as the vector type");
5162 return op->emitOpError(
"does not support masks with vector element type");
5165 unsigned minorSize =
5166 vectorType.getRank() == 0 ? 1 : vectorType.getShape().back();
5167 unsigned resultVecSize =
5170 return op->emitOpError(
5171 "requires the bitwidth of the minor 1-D vector to be an integral "
5172 "multiple of the bitwidth of the source element type");
5176 return op->emitOpError(
"requires a permutation_map with result dims of "
5177 "the same rank as the vector type");
5181 return op->emitOpError(
"requires permutation_map without symbols");
5183 if (permutationMap.
getNumInputs() != shapedType.getRank())
5184 return op->emitOpError(
"requires a permutation_map with input dims of the "
5185 "same rank as the source type");
5187 if (maskType && maskType != inferredMaskType)
5188 return op->emitOpError(
"inferred mask type (")
5189 << inferredMaskType <<
") and mask operand type (" << maskType
5193 return op->emitOpError(
"expects the in_bounds attr of same rank "
5194 "as permutation_map results: ")
5195 << AffineMapAttr::get(permutationMap)
5196 <<
" vs inBounds of size: " << inBounds.size();
5203 elidedAttrs.push_back(TransferReadOp::getOperandSegmentSizeAttr());
5204 if (op.getPermutationMap().isMinorIdentity())
5205 elidedAttrs.push_back(op.getPermutationMapAttrName());
5207 if (llvm::none_of(op.getInBoundsValues(), [](
bool b) { return b; }))
5208 elidedAttrs.push_back(op.getInBoundsAttrName());
5212void TransferReadOp::print(OpAsmPrinter &p) {
5215 p <<
", " << getMask();
5222 auto i1Type = IntegerType::get(permMap.
getContext(), 1);
5224 assert(invPermMap &&
"Inversed permutation map couldn't be computed");
5229 if (maskShape.empty())
5230 maskShape.push_back(1);
5235 return VectorType::get(maskShape, i1Type, scalableDims);
5252 if (hasMask.succeeded()) {
5259 if (types.size() != 2)
5260 return parser.
emitError(typesLoc,
"requires two types");
5262 auto shapedType = llvm::dyn_cast<ShapedType>(types[0]);
5263 if (!shapedType || !llvm::isa<MemRefType, RankedTensorType>(shapedType))
5264 return parser.
emitError(typesLoc,
"requires memref or ranked tensor type");
5265 VectorType vectorType = llvm::dyn_cast<VectorType>(types[1]);
5267 return parser.
emitError(typesLoc,
"requires vector type");
5268 auto permMapAttrName = TransferReadOp::getPermutationMapAttrName(
result.name);
5272 if (shapedType.getRank() <
5275 "expected a custom permutation_map when "
5276 "rank(source) != rank(destination)");
5278 result.attributes.set(permMapAttrName, AffineMapAttr::get(permMap));
5280 permMap = llvm::cast<AffineMapAttr>(permMapAttr).getValue();
5282 auto inBoundsAttrName = TransferReadOp::getInBoundsAttrName(
result.name);
5283 Attribute inBoundsAttr =
result.attributes.get(inBoundsAttrName);
5284 if (!inBoundsAttr) {
5285 result.addAttribute(inBoundsAttrName,
5294 if (hasMask.succeeded()) {
5295 if (llvm::dyn_cast<VectorType>(shapedType.getElementType()))
5297 maskInfo.
location,
"does not support masks with vector element type");
5300 "expected the same rank for the vector and the "
5301 "results of the permutation map");
5309 result.addAttribute(TransferReadOp::getOperandSegmentSizeAttr(),
5311 {1, static_cast<int32_t>(indexInfo.size()), 1,
5312 static_cast<int32_t>(hasMask.succeeded())}));
5316LogicalResult TransferReadOp::verify() {
5318 ShapedType shapedType = getShapedType();
5320 VectorType maskType = getMaskType();
5321 auto paddingType = getPadding().getType();
5322 auto permutationMap = getPermutationMap();
5323 VectorType inferredMaskType =
5326 auto sourceElementType = shapedType.getElementType();
5328 if (
static_cast<int64_t
>(
getIndices().size()) != shapedType.getRank())
5329 return emitOpError(
"requires ") << shapedType.getRank() <<
" indices";
5332 shapedType, vectorType, maskType,
5333 inferredMaskType, permutationMap, getInBounds())))
5336 if (
auto sourceVectorElementType =
5337 llvm::dyn_cast<VectorType>(sourceElementType)) {
5340 if (sourceVectorElementType != paddingType)
5342 "requires source element type and padding type to match.");
5346 if (!VectorType::isValidElementType(paddingType))
5347 return emitOpError(
"requires valid padding vector elemental type");
5350 if (paddingType != sourceElementType)
5352 "requires formal padding and source of the same elemental type");
5363Type TransferReadOp::getExpectedMaskType() {
5370VectorType TransferReadOp::getVectorType() {
5371 return cast<VectorType>(getVector().
getType());
5374template <
typename TransferOp>
5378 if (op.getShapedType().isDynamicDim(indicesIdx))
5382 if (!cstOp.has_value())
5385 int64_t sourceSize = op.getShapedType().getDimSize(indicesIdx);
5386 int64_t vectorSize = op.getVectorType().getDimSize(resultIdx);
5388 return cstOp.value() + vectorSize <= sourceSize;
5391template <
typename TransferOp>
5395 if (op.getTransferRank() == 0)
5398 bool changed =
false;
5400 newInBounds.reserve(op.getTransferRank());
5405 for (
unsigned i = 0; i < op.getTransferRank(); ++i) {
5407 if (op.isDimInBounds(i)) {
5408 newInBounds.push_back(
true);
5413 bool inBounds =
false;
5414 auto dimExpr = dyn_cast<AffineDimExpr>(permutationMap.
getResult(i));
5417 dimExpr.getPosition());
5418 nonBcastDims.push_back(i);
5421 newInBounds.push_back(inBounds);
5423 changed |= inBounds;
5429 bool allNonBcastDimsInBounds = llvm::all_of(
5430 nonBcastDims, [&newInBounds](
unsigned idx) {
return newInBounds[idx]; });
5431 if (allNonBcastDimsInBounds) {
5433 changed |= !newInBounds[idx];
5434 newInBounds[idx] =
true;
5442 op.setInBoundsAttr(
b.getBoolArrayAttr(newInBounds));
5446template <
typename TransferOp>
5448 auto mask = op.getMask();
5455 op.getMaskMutable().clear();
5463template <
typename TransferOp>
5465 VectorType vecType = op.getVectorType();
5466 if (vecType.getRank() != 1 || vecType.getShape()[0] != 1 ||
5467 vecType.isScalable())
5474 int64_t srcRank = op.getShapedType().getRank();
5480 op.setPermutationMapAttr(AffineMapAttr::get(minorIdentity));
5494static Value foldRAW(TransferReadOp readOp) {
5495 if (!llvm::isa<RankedTensorType>(readOp.getShapedType()))
5497 auto defWrite = readOp.getBase().getDefiningOp<vector::TransferWriteOp>();
5500 return defWrite.getVector();
5502 cast<VectorTransferOpInterface>(defWrite.getOperation()),
5503 cast<VectorTransferOpInterface>(readOp.getOperation())))
5505 defWrite = defWrite.getBase().getDefiningOp<vector::TransferWriteOp>();
5510OpFoldResult TransferReadOp::fold(FoldAdaptor) {
5511 if (Value vec = foldRAW(*
this))
5524 return OpFoldResult();
5527std::optional<SmallVector<int64_t, 4>> TransferReadOp::getShapeForUnroll() {
5531void TransferReadOp::getEffects(
5532 SmallVectorImpl<SideEffects::EffectInstance<MemoryEffects::Effect>>
5534 if (llvm::isa<MemRefType>(getShapedType()))
5535 effects.emplace_back(MemoryEffects::Read::get(), &getBaseMutable(),
5536 SideEffects::DefaultResource::get());
5540 if (hasPureTensorSemantics())
5547static AffineMap inverseWithUnusedDims(AffineMap map) {
5549 "expected a projected permutation map");
5554 int64_t pos = cast<AffineDimExpr>(
result).getPosition();
5584struct TransferReadAfterWriteToBroadcast
5585 :
public OpRewritePattern<TransferReadOp> {
5588 LogicalResult matchAndRewrite(TransferReadOp readOp,
5589 PatternRewriter &rewriter)
const override {
5590 auto defWrite = readOp.getBase().getDefiningOp<vector::TransferWriteOp>();
5594 if (!readOp.hasPureTensorSemantics() || !defWrite.hasPureTensorSemantics())
5598 if (readOp.getMask() || defWrite.getMask())
5601 if (readOp.getIndices() != defWrite.getIndices())
5604 if (readOp.hasOutOfBoundsDim() || defWrite.hasOutOfBoundsDim())
5608 if (readOp.getTransferChunkAccessed() !=
5609 defWrite.getTransferChunkAccessed())
5616 AffineMap readMap = readOp.getPermutationMap();
5617 AffineMap writeMap = defWrite.getPermutationMap();
5618 AffineMap invWriteMap = inverseWithUnusedDims(writeMap);
5619 AffineMap composedMap = readMap.
compose(invWriteMap);
5633 int64_t numBroadcastedDims = broadcastedDims.size();
5634 auto invPerm = llvm::to_vector_of<int64_t>(broadcastedDims);
5636 for (
auto [idx, expr] : llvm::enumerate(composedMap.
getResults())) {
5637 if (
auto dim = dyn_cast<AffineDimExpr>(expr)) {
5638 int64_t effectiveDim = dim.getPosition() + numBroadcastedDims;
5639 invPerm[effectiveDim] = idx;
5644 VectorType readVecTy = readOp.getVectorType();
5646 auto broadcastedVecTy =
5648 readVecTy.getElementType(),
5651 Value vec = defWrite.getVector();
5652 Location loc = readOp.getLoc();
5653 vec = vector::BroadcastOp::create(rewriter, loc, broadcastedVecTy, vec);
5660void TransferReadOp::getCanonicalizationPatterns(RewritePatternSet &results,
5661 MLIRContext *context) {
5662 results.
add<TransferReadAfterWriteToBroadcast>(context);
5665FailureOr<std::optional<SmallVector<Value>>>
5666TransferReadOp::bubbleDownCasts(OpBuilder &builder) {
5667 if (!hasPureBufferSemantics())
5678void TransferWriteOp::build(OpBuilder &builder, OperationState &
result,
5680 AffineMapAttr permutationMapAttr,
5683 Type resultType = llvm::dyn_cast<RankedTensorType>(dest.
getType());
5684 build(builder,
result, resultType, vector, dest,
indices, permutationMapAttr,
5685 mask, inBoundsAttr);
5689void TransferWriteOp::build(OpBuilder &builder, OperationState &
result,
5691 AffineMapAttr permutationMapAttr,
5693 build(builder,
result, vector, dest,
indices, permutationMapAttr,
5694 Value(), inBoundsAttr);
5699void TransferWriteOp::build(OpBuilder &builder, OperationState &
result,
5701 AffineMap permutationMap,
5702 std::optional<ArrayRef<bool>> inBounds) {
5703 if (!permutationMap)
5706 llvm::cast<VectorType>(vector.
getType()));
5707 auto permutationMapAttr = AffineMapAttr::get(permutationMap);
5709 (inBounds && !inBounds.value().empty())
5712 llvm::cast<VectorType>(vector.
getType()).getRank(),
false));
5713 build(builder,
result, vector, dest,
indices, permutationMapAttr,
5714 Value(), inBoundsAttr);
5719void TransferWriteOp::build(OpBuilder &builder, OperationState &
result,
5721 std::optional<ArrayRef<bool>> inBounds) {
5726ParseResult TransferWriteOp::parse(OpAsmParser &parser,
5727 OperationState &
result) {
5730 OpAsmParser::UnresolvedOperand vectorInfo, sourceInfo;
5731 SmallVector<OpAsmParser::UnresolvedOperand, 8> indexInfo;
5732 SmallVector<Type, 2> types;
5733 OpAsmParser::UnresolvedOperand maskInfo;
5739 if (hasMask.succeeded() && parser.
parseOperand(maskInfo))
5744 if (types.size() != 2)
5745 return parser.
emitError(typesLoc,
"requires two types");
5747 VectorType vectorType = llvm::dyn_cast<VectorType>(types[0]);
5749 return parser.
emitError(typesLoc,
"requires vector type");
5750 ShapedType shapedType = llvm::dyn_cast<ShapedType>(types[1]);
5751 if (!shapedType || !llvm::isa<MemRefType, RankedTensorType>(shapedType))
5752 return parser.
emitError(typesLoc,
"requires memref or ranked tensor type");
5753 auto permMapAttrName =
5754 TransferWriteOp::getPermutationMapAttrName(
result.name);
5755 auto permMapAttr =
result.attributes.get(permMapAttrName);
5758 if (shapedType.getRank() <
5761 "expected a custom permutation_map when "
5762 "rank(source) != rank(destination)");
5764 result.attributes.set(permMapAttrName, AffineMapAttr::get(permMap));
5766 permMap = llvm::cast<AffineMapAttr>(permMapAttr).getValue();
5768 auto inBoundsAttrName = TransferWriteOp::getInBoundsAttrName(
result.name);
5769 Attribute inBoundsAttr =
result.attributes.get(inBoundsAttrName);
5770 if (!inBoundsAttr) {
5771 result.addAttribute(inBoundsAttrName,
5779 if (hasMask.succeeded()) {
5780 if (llvm::dyn_cast<VectorType>(shapedType.getElementType()))
5782 maskInfo.
location,
"does not support masks with vector element type");
5785 "expected the same rank for the vector and the "
5786 "results of the permutation map");
5792 result.addAttribute(TransferWriteOp::getOperandSegmentSizeAttr(),
5794 {1, 1, static_cast<int32_t>(indexInfo.size()),
5795 static_cast<int32_t>(hasMask.succeeded())}));
5796 return failure(llvm::isa<RankedTensorType>(shapedType) &&
5800void TransferWriteOp::print(OpAsmPrinter &p) {
5803 p <<
", " << getMask();
5808LogicalResult TransferWriteOp::verify() {
5810 ShapedType shapedType = getShapedType();
5812 VectorType maskType = getMaskType();
5813 auto permutationMap = getPermutationMap();
5814 VectorType inferredMaskType =
5818 if (llvm::size(
getIndices()) != shapedType.getRank())
5819 return emitOpError(
"requires ") << shapedType.getRank() <<
" indices";
5823 if (hasBroadcastDim())
5824 return emitOpError(
"should not have broadcast dimensions");
5827 shapedType, vectorType, maskType,
5828 inferredMaskType, permutationMap, getInBounds())))
5841Type TransferWriteOp::getExpectedMaskType() {
5848Value TransferWriteOp::getVector() {
return getOperand(0); }
5849VectorType TransferWriteOp::getVectorType() {
5850 return cast<VectorType>(getValueToStore().
getType());
5873static LogicalResult foldReadInitWrite(TransferWriteOp write,
5874 ArrayRef<Attribute>,
5875 SmallVectorImpl<OpFoldResult> &results) {
5877 if (write.getTransferRank() == 0)
5879 auto rankedTensorType =
5880 llvm::dyn_cast<RankedTensorType>(write.getBase().getType());
5882 if (!rankedTensorType)
5885 auto read = write.getVector().getDefiningOp<vector::TransferReadOp>();
5889 if (read.getTransferRank() == 0)
5892 if (!read.getPermutationMap().isMinorIdentity() ||
5893 !write.getPermutationMap().isMinorIdentity())
5896 if (read.getTransferRank() != write.getTransferRank())
5899 if (read.hasOutOfBoundsDim() || write.hasOutOfBoundsDim())
5902 if (read.getMask() || write.getMask())
5905 if (read.getBase().getType() != rankedTensorType)
5908 if (read.getVectorType() != write.getVectorType())
5911 if (read.getVectorType().getShape() != rankedTensorType.getShape())
5914 auto isNotConstantZero = [](Value v) {
5916 return !cstOp.has_value() || cstOp.value() != 0;
5918 if (llvm::any_of(read.getIndices(), isNotConstantZero) ||
5919 llvm::any_of(write.getIndices(), isNotConstantZero))
5922 results.push_back(read.getBase());
5926static bool checkSameValueWAR(vector::TransferReadOp read,
5927 vector::TransferWriteOp write) {
5928 return read.getBase() == write.getBase() &&
5929 read.getIndices() == write.getIndices() &&
5930 read.getPermutationMap() == write.getPermutationMap() &&
5931 read.getVectorType() == write.getVectorType() && !read.getMask() &&
5948static LogicalResult foldWAR(TransferWriteOp write,
5949 SmallVectorImpl<OpFoldResult> &results) {
5950 if (!llvm::isa<RankedTensorType>(write.getBase().getType()))
5952 auto read = write.getVector().getDefiningOp<vector::TransferReadOp>();
5956 if (!checkSameValueWAR(read, write))
5958 results.push_back(read.getBase());
5962LogicalResult TransferWriteOp::fold(FoldAdaptor adaptor,
5963 SmallVectorImpl<OpFoldResult> &results) {
5964 if (succeeded(foldReadInitWrite(*
this, adaptor.getOperands(), results)))
5966 if (succeeded(foldWAR(*
this, results)))
5980std::optional<SmallVector<int64_t, 4>> TransferWriteOp::getShapeForUnroll() {
5984void TransferWriteOp::getEffects(
5985 SmallVectorImpl<SideEffects::EffectInstance<MemoryEffects::Effect>>
5987 if (llvm::isa<MemRefType>(getShapedType()))
5988 effects.emplace_back(MemoryEffects::Write::get(), &getBaseMutable(),
5989 SideEffects::DefaultResource::get());
5993 if (hasPureTensorSemantics())
6023class FoldWaw final :
public OpRewritePattern<TransferWriteOp> {
6026 LogicalResult matchAndRewrite(TransferWriteOp writeOp,
6027 PatternRewriter &rewriter)
const override {
6028 if (!llvm::isa<RankedTensorType>(writeOp.getShapedType()))
6030 vector::TransferWriteOp writeToModify = writeOp;
6032 auto defWrite = writeOp.getBase().getDefiningOp<vector::TransferWriteOp>();
6036 writeToModify.getBaseMutable().assign(defWrite.getBase());
6041 cast<VectorTransferOpInterface>(defWrite.getOperation()),
6042 cast<VectorTransferOpInterface>(writeOp.getOperation())))
6046 if (!defWrite->hasOneUse())
6048 writeToModify = defWrite;
6049 defWrite = defWrite.getBase().getDefiningOp<vector::TransferWriteOp>();
6078struct SwapExtractSliceOfTransferWrite
6079 :
public OpRewritePattern<tensor::InsertSliceOp> {
6083 LogicalResult matchAndRewrite(tensor::InsertSliceOp insertOp,
6084 PatternRewriter &rewriter)
const override {
6085 if (!insertOp.hasUnitStride())
6088 insertOp.getSource().getDefiningOp<tensor::ExtractSliceOp>();
6089 if (!extractOp || !extractOp.hasUnitStride() || !extractOp->hasOneUse())
6091 auto transferOp = extractOp.getSource().getDefiningOp<TransferWriteOp>();
6092 if (!transferOp || !transferOp->hasOneUse())
6097 if (insertOp.getSourceType().getRank() != transferOp.getTransferRank()) {
6099 "use-def chain is rank-reducing");
6103 if (!extractOp.hasZeroOffset()) {
6105 "ExtractSliceOp has non-zero offset");
6109 if (!llvm::all_of(transferOp.getIndices(), [](Value value) {
6110 return getConstantIntValue(value) == static_cast<int64_t>(0);
6113 "TranferWriteOp has non-zero offset");
6117 if (insertOp.getMixedSizes().size() != extractOp.getMixedSizes().size()) {
6119 insertOp,
"InsertSliceOp and ExtractSliceOp ranks differ");
6122 for (
auto [insertSize, extractSize] :
6123 llvm::zip_equal(insertOp.getMixedSizes(), extractOp.getMixedSizes())) {
6126 insertOp,
"InsertSliceOp and ExtractSliceOp sizes differ");
6131 assert(transferOp.getVectorType().hasStaticShape() &&
6132 "expected vector to have a static shape");
6133 ArrayRef<int64_t>
vectorShape = transferOp.getVectorType().getShape();
6135 transferOp.getPermutationMap(), transferOp.getShapedType().getShape());
6136 if (transferOp.getMask() || !
vectorShape.equals(resultShape)) {
6138 insertOp,
"TransferWriteOp may not write the full tensor.");
6143 SmallVector<bool> newInBounds(
vectorShape.size(),
false);
6144 auto newExtractOp = tensor::ExtractSliceOp::create(
6145 rewriter, extractOp.getLoc(), insertOp.getSourceType(),
6146 insertOp.getDest(), insertOp.getMixedOffsets(),
6147 insertOp.getMixedSizes(), insertOp.getMixedStrides());
6148 auto newTransferWriteOp = TransferWriteOp::create(
6149 rewriter, transferOp.getLoc(), transferOp.getVector(),
6150 newExtractOp.getResult(), transferOp.getIndices(),
6151 transferOp.getPermutationMapAttr(),
6154 insertOp.getSourceMutable().assign(newTransferWriteOp.getResult());
6162void TransferWriteOp::getCanonicalizationPatterns(RewritePatternSet &results,
6163 MLIRContext *context) {
6164 results.
add<FoldWaw, SwapExtractSliceOfTransferWrite>(context);
6167FailureOr<std::optional<SmallVector<Value>>>
6168TransferWriteOp::bubbleDownCasts(OpBuilder &builder) {
6169 if (!hasPureBufferSemantics())
6179static LogicalResult verifyLoadStoreMemRefLayout(Operation *op,
6181 MemRefType memRefTy) {
6184 if (!vecTy.isScalable() &&
6185 (vecTy.getRank() == 0 || vecTy.getNumElements() == 1))
6188 if (!memRefTy.isLastDimUnitStride())
6189 return op->
emitOpError(
"most minor memref dim must have unit stride");
6193LogicalResult vector::LoadOp::verify() {
6197 if (
failed(verifyLoadStoreMemRefLayout(*
this, resVecTy, memRefTy)))
6204 return emitOpError(
"memref strides must be non-negative");
6206 if (memRefTy.getRank() < resVecTy.getRank())
6208 "destination memref has lower rank than the result vector");
6211 Type memElemTy = memRefTy.getElementType();
6212 if (
auto memVecTy = llvm::dyn_cast<VectorType>(memElemTy)) {
6213 if (memVecTy != resVecTy)
6214 return emitOpError(
"base memref and result vector types should match");
6215 memElemTy = memVecTy.getElementType();
6218 if (resVecTy.getElementType() != memElemTy)
6219 return emitOpError(
"base and result element types should match");
6220 if (llvm::size(
getIndices()) != memRefTy.getRank())
6221 return emitOpError(
"requires ") << memRefTy.getRank() <<
" indices";
6225OpFoldResult LoadOp::fold(FoldAdaptor) {
6228 return OpFoldResult();
6231std::optional<SmallVector<int64_t, 4>> LoadOp::getShapeForUnroll() {
6235FailureOr<std::optional<SmallVector<Value>>>
6236LoadOp::bubbleDownCasts(OpBuilder &builder) {
6245LogicalResult vector::StoreOp::verify() {
6249 if (
failed(verifyLoadStoreMemRefLayout(*
this, valueVecTy, memRefTy)))
6256 return emitOpError(
"memref strides must be non-negative");
6258 if (memRefTy.getRank() < valueVecTy.getRank())
6259 return emitOpError(
"source memref has lower rank than the vector to store");
6262 Type memElemTy = memRefTy.getElementType();
6263 if (
auto memVecTy = llvm::dyn_cast<VectorType>(memElemTy)) {
6264 if (memVecTy != valueVecTy)
6266 "base memref and valueToStore vector types should match");
6267 memElemTy = memVecTy.getElementType();
6270 if (valueVecTy.getElementType() != memElemTy)
6271 return emitOpError(
"base and valueToStore element type should match");
6272 if (llvm::size(
getIndices()) != memRefTy.getRank())
6273 return emitOpError(
"requires ") << memRefTy.getRank() <<
" indices";
6277LogicalResult StoreOp::fold(FoldAdaptor adaptor,
6278 SmallVectorImpl<OpFoldResult> &results) {
6282std::optional<SmallVector<int64_t, 4>> StoreOp::getShapeForUnroll() {
6286FailureOr<std::optional<SmallVector<Value>>>
6287StoreOp::bubbleDownCasts(OpBuilder &builder) {
6296LogicalResult MaskedLoadOp::verify() {
6297 VectorType maskVType = getMaskVectorType();
6298 VectorType passVType = getPassThruVectorType();
6306 return emitOpError(
"memref strides must be non-negative");
6311 if (llvm::size(
getIndices()) != memType.getRank())
6312 return emitOpError(
"requires ") << memType.getRank() <<
" indices";
6313 if (resVType.getShape() != maskVType.getShape())
6314 return emitOpError(
"expected result shape to match mask shape");
6315 if (resVType != passVType)
6316 return emitOpError(
"expected pass_thru of same type as result type");
6321class MaskedLoadFolder final :
public OpRewritePattern<MaskedLoadOp> {
6324 LogicalResult matchAndRewrite(MaskedLoadOp
load,
6325 PatternRewriter &rewriter)
const override {
6337 llvm_unreachable(
"Unexpected 1DMaskFormat on MaskedLoad");
6342void MaskedLoadOp::getCanonicalizationPatterns(RewritePatternSet &results,
6343 MLIRContext *context) {
6344 results.
add<MaskedLoadFolder>(context);
6347OpFoldResult MaskedLoadOp::fold(FoldAdaptor) {
6350 return OpFoldResult();
6353FailureOr<std::optional<SmallVector<Value>>>
6354MaskedLoadOp::bubbleDownCasts(OpBuilder &builder) {
6363LogicalResult MaskedStoreOp::verify() {
6364 VectorType maskVType = getMaskVectorType();
6372 return emitOpError(
"memref strides must be non-negative");
6377 if (llvm::size(
getIndices()) != memType.getRank())
6378 return emitOpError(
"requires ") << memType.getRank() <<
" indices";
6379 if (valueVType.getShape() != maskVType.getShape())
6380 return emitOpError(
"expected valueToStore shape to match mask shape");
6385class MaskedStoreFolder final :
public OpRewritePattern<MaskedStoreOp> {
6388 LogicalResult matchAndRewrite(MaskedStoreOp store,
6389 PatternRewriter &rewriter)
const override {
6393 store, store.getValueToStore(), store.getBase(), store.getIndices());
6401 llvm_unreachable(
"Unexpected 1DMaskFormat on MaskedStore");
6406void MaskedStoreOp::getCanonicalizationPatterns(RewritePatternSet &results,
6407 MLIRContext *context) {
6408 results.
add<MaskedStoreFolder>(context);
6411LogicalResult MaskedStoreOp::fold(FoldAdaptor adaptor,
6412 SmallVectorImpl<OpFoldResult> &results) {
6416FailureOr<std::optional<SmallVector<Value>>>
6417MaskedStoreOp::bubbleDownCasts(OpBuilder &builder) {
6426LogicalResult GatherOp::verify() {
6427 VectorType indVType = getIndexVectorType();
6428 VectorType maskVType = getMaskVectorType();
6430 ShapedType baseType = getBaseType();
6432 if (!llvm::isa<MemRefType, RankedTensorType>(baseType))
6433 return emitOpError(
"requires base to be a memref or ranked tensor type");
6438 if (
auto memRefType = dyn_cast<MemRefType>(baseType))
6440 return emitOpError(
"memref strides must be non-negative");
6445 if (llvm::size(getOffsets()) != baseType.getRank())
6446 return emitOpError(
"requires ") << baseType.getRank() <<
" indices";
6447 if (resVType.getShape() != indVType.getShape())
6448 return emitOpError(
"expected result dim to match indices dim");
6449 if (resVType.getShape() != maskVType.getShape())
6450 return emitOpError(
"expected result dim to match mask dim");
6451 if (resVType != getPassThruVectorType())
6452 return emitOpError(
"expected pass_thru of same type as result type");
6453 if (getAlignmentAttr() && !isa<MemRefType>(baseType)) {
6455 "alignment is only supported for memref bases, not tensor bases");
6464Type GatherOp::getExpectedMaskType() {
6465 auto vecType = this->getIndexVectorType();
6466 return VectorType::get(vecType.getShape(),
6467 IntegerType::get(vecType.getContext(), 1),
6468 vecType.getScalableDims());
6471std::optional<SmallVector<int64_t, 4>> GatherOp::getShapeForUnroll() {
6476static LogicalResult isZeroBasedContiguousSeq(Value indexVec) {
6477 auto vecType = dyn_cast<VectorType>(indexVec.
getType());
6478 if (!vecType || vecType.getRank() != 1 || vecType.isScalable())
6484 DenseIntElementsAttr elements;
6489 llvm::equal(elements, llvm::seq<int64_t>(0, vecType.getNumElements())));
6493class GatherFolder final :
public OpRewritePattern<GatherOp> {
6496 LogicalResult matchAndRewrite(GatherOp gather,
6497 PatternRewriter &rewriter)
const override {
6502 rewriter.
replaceOp(gather, gather.getPassThru());
6507 llvm_unreachable(
"Unexpected 1DMaskFormat on GatherFolder");
6513class FoldContiguousGather final :
public OpRewritePattern<GatherOp> {
6516 LogicalResult matchAndRewrite(GatherOp op,
6517 PatternRewriter &rewriter)
const override {
6518 if (!isa<MemRefType>(op.getBase().getType()))
6521 if (
failed(isZeroBasedContiguousSeq(op.getIndices())))
6525 op.getOffsets(), op.getMask(),
6532void GatherOp::getCanonicalizationPatterns(RewritePatternSet &results,
6533 MLIRContext *context) {
6534 results.
add<GatherFolder, FoldContiguousGather>(context);
6537FailureOr<std::optional<SmallVector<Value>>>
6538GatherOp::bubbleDownCasts(OpBuilder &builder) {
6547LogicalResult ScatterOp::verify() {
6548 VectorType indVType = getIndexVectorType();
6549 VectorType maskVType = getMaskVectorType();
6551 ShapedType baseType = getBaseType();
6553 if (!llvm::isa<MemRefType, RankedTensorType>(baseType))
6554 return emitOpError(
"requires base to be a memref or ranked tensor type");
6559 if (
auto memRefType = dyn_cast<MemRefType>(baseType))
6561 return emitOpError(
"memref strides must be non-negative");
6566 if (llvm::size(getOffsets()) != baseType.getRank())
6567 return emitOpError(
"requires ") << baseType.getRank() <<
" indices";
6568 if (valueVType.getShape() != indVType.getShape())
6569 return emitOpError(
"expected valueToStore dim to match indices dim");
6570 if (valueVType.getShape() != maskVType.getShape())
6571 return emitOpError(
"expected valueToStore dim to match mask dim");
6572 if (getAlignmentAttr() && !isa<MemRefType>(baseType)) {
6574 "alignment is only supported for memref bases, not tensor bases");
6579class ScatterFolder final :
public OpRewritePattern<ScatterOp> {
6582 LogicalResult matchAndRewrite(ScatterOp scatter,
6583 PatternRewriter &rewriter)
const override {
6584 ShapedType baseType = scatter.getBaseType();
6585 bool isMemRef = isa<MemRefType>(baseType);
6586 if (!isMemRef && !isa<RankedTensorType>(baseType))
6599 rewriter.
replaceOp(scatter, scatter.getBase());
6604 llvm_unreachable(
"Unexpected 1DMaskFormat on ScatterFolder");
6610class FoldContiguousScatter final :
public OpRewritePattern<ScatterOp> {
6613 LogicalResult matchAndRewrite(ScatterOp op,
6614 PatternRewriter &rewriter)
const override {
6617 if (!isa<MemRefType>(op.getBase().getType()))
6620 if (
failed(isZeroBasedContiguousSeq(op.getIndices())))
6624 op, op.getBase(), op.getOffsets(), op.getMask(), op.getValueToStore());
6630void ScatterOp::getCanonicalizationPatterns(RewritePatternSet &results,
6631 MLIRContext *context) {
6632 results.
add<ScatterFolder, FoldContiguousScatter>(context);
6635FailureOr<std::optional<SmallVector<Value>>>
6636ScatterOp::bubbleDownCasts(OpBuilder &builder) {
6645LogicalResult ExpandLoadOp::verify() {
6646 VectorType maskVType = getMaskVectorType();
6647 VectorType passVType = getPassThruVectorType();
6654 if (llvm::size(
getIndices()) != memType.getRank())
6655 return emitOpError(
"requires ") << memType.getRank() <<
" indices";
6656 if (resVType.getShape() != maskVType.getShape())
6657 return emitOpError(
"expected result shape to match mask shape");
6658 if (resVType != passVType)
6659 return emitOpError(
"expected pass_thru of same type as result type");
6664class ExpandLoadFolder final :
public OpRewritePattern<ExpandLoadOp> {
6667 LogicalResult matchAndRewrite(ExpandLoadOp expand,
6668 PatternRewriter &rewriter)
const override {
6672 expand, expand.getType(), expand.getBase(), expand.getIndices());
6675 rewriter.
replaceOp(expand, expand.getPassThru());
6680 llvm_unreachable(
"Unexpected 1DMaskFormat on ExpandLoadFolder");
6685void ExpandLoadOp::getCanonicalizationPatterns(RewritePatternSet &results,
6686 MLIRContext *context) {
6687 results.
add<ExpandLoadFolder>(context);
6690FailureOr<std::optional<SmallVector<Value>>>
6691ExpandLoadOp::bubbleDownCasts(OpBuilder &builder) {
6700LogicalResult CompressStoreOp::verify() {
6701 VectorType maskVType = getMaskVectorType();
6708 if (llvm::size(
getIndices()) != memType.getRank())
6709 return emitOpError(
"requires ") << memType.getRank() <<
" indices";
6710 if (valueVType.getShape() != maskVType.getShape())
6711 return emitOpError(
"expected valueToStore shape to match mask shape");
6716class CompressStoreFolder final :
public OpRewritePattern<CompressStoreOp> {
6719 LogicalResult matchAndRewrite(CompressStoreOp compress,
6720 PatternRewriter &rewriter)
const override {
6724 compress, compress.getValueToStore(), compress.getBase(),
6725 compress.getIndices());
6733 llvm_unreachable(
"Unexpected 1DMaskFormat on CompressStoreFolder");
6738void CompressStoreOp::getCanonicalizationPatterns(RewritePatternSet &results,
6739 MLIRContext *context) {
6740 results.
add<CompressStoreFolder>(context);
6743FailureOr<std::optional<SmallVector<Value>>>
6744CompressStoreOp::bubbleDownCasts(OpBuilder &builder) {
6753void ShapeCastOp::inferResultRanges(ArrayRef<ConstantIntRanges> argRanges,
6755 setResultRanges(getResult(), argRanges.front());
6758std::optional<SmallVector<int64_t, 4>> ShapeCastOp::getShapeForUnroll() {
6759 return llvm::to_vector<4>(getResultVectorType().
getShape());
6762LogicalResult ShapeCastOp::verify() {
6764 VectorType sourceType = getSourceVectorType();
6765 VectorType resultType = getResultVectorType();
6773 int64_t sourceNElms = sourceType.getNumElements();
6774 int64_t resultNElms = resultType.getNumElements();
6775 if (sourceNElms != resultNElms) {
6776 return emitOpError() <<
"has different number of elements at source ("
6777 << sourceNElms <<
") and result (" << resultNElms
6782 int64_t sourceNScalableDims = sourceType.getNumScalableDims();
6783 int64_t resultNScalableDims = resultType.getNumScalableDims();
6784 if (sourceNScalableDims != resultNScalableDims)
6785 return emitOpError() <<
"has different number of scalable dims at source ("
6786 << sourceNScalableDims <<
") and result ("
6787 << resultNScalableDims <<
")";
6798bool ShapeCastOp::isBroadcastLike() {
6799 auto srcType = getSourceVectorType();
6800 auto resType = getResultVectorType();
6803 std::pair<VectorDim, VectorDim> mismatchingDims;
6805 BroadcastableToResult::Success)
6812 size_t rankDiff = resType.getRank() - srcType.getRank();
6817 if (!llvm::all_of(resType.getShape().take_front(rankDiff),
6818 [](int64_t dim) { return dim == 1; }))
6822 return resType.getShape().take_back(srcType.getRank()) == srcType.getShape();
6829static bool isOrderPreserving(TransposeOp transpose) {
6830 ArrayRef<int64_t> permutation = transpose.getPermutation();
6831 VectorType sourceType = transpose.getSourceVectorType();
6832 ArrayRef<int64_t> inShape = sourceType.getShape();
6833 ArrayRef<bool> inDimIsScalable = sourceType.getScalableDims();
6834 auto isNonScalableUnitDim = [&](int64_t dim) {
6835 return inShape[dim] == 1 && !inDimIsScalable[dim];
6837 int64_t current = 0;
6838 for (
auto p : permutation) {
6839 if (!isNonScalableUnitDim(p)) {
6849OpFoldResult ShapeCastOp::fold(FoldAdaptor adaptor) {
6851 VectorType resultType =
getType();
6854 if (getSource().
getType() == resultType)
6858 if (
auto precedingShapeCast = getSource().getDefiningOp<ShapeCastOp>()) {
6859 setOperand(precedingShapeCast.getSource());
6864 if (
auto transpose = getSource().getDefiningOp<TransposeOp>()) {
6865 if (isOrderPreserving(transpose)) {
6866 setOperand(transpose.getVector());
6874 if (
auto bcastOp = getSource().getDefiningOp<BroadcastOp>()) {
6875 if (bcastOp.getSourceType() == resultType)
6876 return bcastOp.getSource();
6880 if (
auto denseAttr =
6881 dyn_cast_if_present<DenseElementsAttr>(adaptor.getSource()))
6882 return denseAttr.reshape(
getType());
6898static VectorType trimTrailingOneDims(VectorType oldType) {
6899 ArrayRef<int64_t> oldShape = oldType.getShape();
6900 ArrayRef<int64_t> newShape = oldShape;
6902 ArrayRef<bool> oldScalableDims = oldType.getScalableDims();
6903 ArrayRef<bool> newScalableDims = oldScalableDims;
6905 while (!newShape.empty() && newShape.back() == 1 && !newScalableDims.back()) {
6906 newShape = newShape.drop_back(1);
6907 newScalableDims = newScalableDims.drop_back(1);
6912 if (newShape.empty()) {
6913 newShape = oldShape.take_back();
6914 newScalableDims = oldScalableDims.take_back();
6917 return VectorType::get(newShape, oldType.getElementType(), newScalableDims);
6932class ShapeCastCreateMaskFolderTrailingOneDim final
6933 :
public OpRewritePattern<ShapeCastOp> {
6937 LogicalResult matchAndRewrite(ShapeCastOp shapeOp,
6938 PatternRewriter &rewriter)
const override {
6939 Value shapeOpSrc = shapeOp->getOperand(0);
6940 auto createMaskOp = shapeOpSrc.
getDefiningOp<vector::CreateMaskOp>();
6941 auto constantMaskOp = shapeOpSrc.
getDefiningOp<vector::ConstantMaskOp>();
6942 if (!createMaskOp && !constantMaskOp)
6945 VectorType shapeOpResTy = shapeOp.getResultVectorType();
6946 VectorType shapeOpSrcTy = shapeOp.getSourceVectorType();
6948 VectorType newVecType = trimTrailingOneDims(shapeOpSrcTy);
6949 if (newVecType != shapeOpResTy)
6952 auto numDimsToDrop =
6953 shapeOpSrcTy.getShape().size() - shapeOpResTy.getShape().size();
6960 auto maskOperands = createMaskOp.getOperands();
6961 auto numMaskOperands = maskOperands.size();
6964 for (
size_t i = numMaskOperands - 1; i >= numMaskOperands - numDimsToDrop;
6966 auto constant = maskOperands[i].getDefiningOp<arith::ConstantIndexOp>();
6967 if (!constant || (constant.value() != 1))
6970 SmallVector<Value> newMaskOperands =
6971 maskOperands.drop_back(numDimsToDrop);
6978 if (constantMaskOp) {
6979 auto maskDimSizes = constantMaskOp.getMaskDimSizes();
6980 auto numMaskOperands = maskDimSizes.size();
6983 for (
size_t i = numMaskOperands - 1; i >= numMaskOperands - numDimsToDrop;
6985 if (maskDimSizes[i] != 1)
6989 auto newMaskOperands = maskDimSizes.drop_back(numDimsToDrop);
7002int64_t getBroadcastStretchingFactor(ArrayRef<int64_t> srcShape,
7003 ArrayRef<int64_t> dstShape) {
7004 int stretchingFactor = 1;
7005 int numLeadingDims = dstShape.size() - srcShape.size();
7006 for (
int i = 0, e = srcShape.size(); i < e; i++) {
7007 int64_t dstDim = dstShape[numLeadingDims + i];
7008 if (srcShape[i] == 1 && dstDim != 1) {
7009 stretchingFactor *= dstDim;
7012 return stretchingFactor;
7016class ShapeCastBroadcastFolder final :
public OpRewritePattern<ShapeCastOp> {
7020 LogicalResult matchAndRewrite(ShapeCastOp shapeCastOp,
7021 PatternRewriter &rewriter)
const override {
7023 shapeCastOp.getSource().getDefiningOp<vector::BroadcastOp>();
7027 auto srcVectorType = dyn_cast<VectorType>(broadcastOp.getSourceType());
7028 bool srcIsScalar = !srcVectorType;
7036 VectorType dstVectorType = shapeCastOp.getResultVectorType();
7037 ArrayRef<int64_t> dstShape = dstVectorType.getShape();
7038 ArrayRef<int64_t> srcShape =
7039 srcIsScalar ? ArrayRef<int64_t>{} : srcVectorType.getShape();
7040 ArrayRef<int64_t> broadcastShape =
7041 broadcastOp.getResultVectorType().getShape();
7045 BroadcastableToResult::Success) {
7053 if (srcVectorType.getNumElements() != 1) {
7054 if (getBroadcastStretchingFactor(srcShape, dstShape) !=
7055 getBroadcastStretchingFactor(srcShape, broadcastShape)) {
7062 broadcastOp.getSource());
7081class FoldShapeCastOfFromElements final :
public OpRewritePattern<ShapeCastOp> {
7085 LogicalResult matchAndRewrite(ShapeCastOp shapeCastOp,
7086 PatternRewriter &rewriter)
const override {
7087 auto fromElements = shapeCastOp.getSource().getDefiningOp<FromElementsOp>();
7092 shapeCastOp, shapeCastOp.getResultVectorType(),
7093 fromElements.getElements());
7100void ShapeCastOp::getCanonicalizationPatterns(RewritePatternSet &results,
7101 MLIRContext *context) {
7102 results.
add<ShapeCastCreateMaskFolderTrailingOneDim, ShapeCastBroadcastFolder,
7103 FoldShapeCastOfFromElements>(context);
7110LogicalResult BitCastOp::verify() {
7111 auto sourceVectorType = getSourceVectorType();
7112 auto resultVectorType = getResultVectorType();
7114 for (int64_t i = 0, e = sourceVectorType.getRank() - 1; i < e; i++) {
7115 if (sourceVectorType.getDimSize(i) != resultVectorType.getDimSize(i))
7116 return emitOpError(
"dimension size mismatch at: ") << i;
7119 DataLayout dataLayout = DataLayout::closest(*
this);
7120 auto sourceElementBits =
7122 auto resultElementBits =
7125 if (sourceVectorType.getRank() == 0) {
7126 if (sourceElementBits != resultElementBits)
7127 return emitOpError(
"source/result bitwidth of the 0-D vector element "
7128 "types must be equal");
7129 }
else if (sourceElementBits * sourceVectorType.getShape().back() !=
7130 resultElementBits * resultVectorType.getShape().back()) {
7132 "source/result bitwidth of the minor 1-D vectors must be equal");
7138OpFoldResult BitCastOp::fold(FoldAdaptor adaptor) {
7144 if (
auto otherOp = getSource().getDefiningOp<BitCastOp>()) {
7145 if (getResult().
getType() == otherOp.getSource().getType())
7146 return otherOp.getSource();
7148 setOperand(otherOp.getSource());
7152 Attribute sourceConstant = adaptor.getSource();
7153 if (!sourceConstant)
7156 Type srcElemType = getSourceVectorType().getElementType();
7157 Type dstElemType = getResultVectorType().getElementType();
7159 if (
auto floatPack = llvm::dyn_cast<DenseFPElementsAttr>(sourceConstant)) {
7160 if (floatPack.isSplat()) {
7161 auto splat = floatPack.getSplatValue<FloatAttr>();
7164 if (srcElemType.
isF16() && dstElemType.
isF32()) {
7165 uint32_t bits =
static_cast<uint32_t
>(
7166 splat.getValue().bitcastToAPInt().getZExtValue());
7168 bits = (bits << 16) | (bits & 0xffff);
7169 APInt intBits(32, bits);
7170 APFloat floatBits(llvm::APFloat::IEEEsingle(), intBits);
7176 if (
auto intPack = llvm::dyn_cast<DenseIntElementsAttr>(sourceConstant)) {
7177 if (intPack.isSplat()) {
7178 auto splat = intPack.getSplatValue<IntegerAttr>();
7180 if (llvm::isa<IntegerType>(dstElemType) && srcElemType.
isIntOrFloat()) {
7185 if (dstBitWidth > srcBitWidth && dstBitWidth % srcBitWidth == 0) {
7186 APInt intBits = splat.getValue().zext(dstBitWidth);
7189 for (uint64_t i = 0; i < dstBitWidth / srcBitWidth - 1; i++)
7190 intBits = (intBits << srcBitWidth) | intBits;
7200std::optional<SmallVector<int64_t, 4>> BitCastOp::getShapeForUnroll() {
7201 return llvm::to_vector<4>(getResultVectorType().
getShape());
7208static SmallVector<int64_t, 8> extractShape(MemRefType memRefType) {
7209 auto vectorType = llvm::dyn_cast<VectorType>(memRefType.getElementType());
7210 SmallVector<int64_t, 8> res(memRefType.getShape());
7212 res.append(vectorType.getShape().begin(), vectorType.getShape().end());
7218void TypeCastOp::build(OpBuilder &builder, OperationState &
result,
7220 result.addOperands(source);
7221 MemRefType memRefType = llvm::cast<MemRefType>(source.
getType());
7222 VectorType vectorType =
7223 VectorType::get(extractShape(memRefType),
7225 result.addTypes(MemRefType::get({}, vectorType, MemRefLayoutAttrInterface(),
7226 memRefType.getMemorySpace()));
7229LogicalResult TypeCastOp::verify() {
7230 MemRefType canonicalType =
getMemRefType().canonicalizeStridedLayout();
7231 if (!canonicalType.getLayout().isIdentity())
7232 return emitOpError(
"expects operand to be a memref with identity layout");
7233 if (!getResultMemRefType().getLayout().isIdentity())
7234 return emitOpError(
"expects result to be a memref with identity layout");
7235 if (getResultMemRefType().getMemorySpace() !=
7237 return emitOpError(
"expects result in same memory space");
7240 auto resultType = getResultMemRefType();
7244 "expects result and operand with same underlying scalar type: ")
7246 if (extractShape(sourceType) != extractShape(resultType))
7248 "expects concatenated result and operand shapes to be equal: ")
7257void vector::TransposeOp::build(OpBuilder &builder, OperationState &
result,
7258 Value vector, ArrayRef<int64_t> permutation) {
7259 VectorType vt = llvm::cast<VectorType>(vector.
getType());
7260 SmallVector<int64_t, 4> transposedShape(vt.getRank());
7261 SmallVector<bool, 4> transposedScalableDims(vt.getRank());
7262 for (
unsigned i = 0; i < permutation.size(); ++i) {
7263 transposedShape[i] = vt.getShape()[permutation[i]];
7264 transposedScalableDims[i] = vt.getScalableDims()[permutation[i]];
7267 result.addOperands(vector);
7268 result.addTypes(VectorType::get(transposedShape, vt.getElementType(),
7269 transposedScalableDims));
7270 result.addAttribute(TransposeOp::getPermutationAttrName(
result.name),
7274OpFoldResult vector::TransposeOp::fold(FoldAdaptor adaptor) {
7277 llvm::dyn_cast_if_present<SplatElementsAttr>(adaptor.getVector()))
7278 return splat.reshape(getResultVectorType());
7295 if (getSourceVectorType() == getResultVectorType() &&
7296 isOrderPreserving(*
this))
7302LogicalResult vector::TransposeOp::verify() {
7303 VectorType vectorType = getSourceVectorType();
7304 VectorType resultType = getResultVectorType();
7305 int64_t rank = resultType.getRank();
7306 if (vectorType.getRank() != rank)
7307 return emitOpError(
"vector result rank mismatch: ") << rank;
7309 ArrayRef<int64_t> perm = getPermutation();
7310 int64_t size = perm.size();
7312 return emitOpError(
"transposition length mismatch: ") << size;
7313 SmallVector<bool, 8> seen(rank,
false);
7314 for (
const auto &ta : llvm::enumerate(perm)) {
7315 if (ta.value() < 0 || ta.value() >= rank)
7316 return emitOpError(
"transposition index out of range: ") << ta.value();
7317 if (seen[ta.value()])
7318 return emitOpError(
"duplicate position index: ") << ta.value();
7319 seen[ta.value()] =
true;
7320 if (resultType.getDimSize(ta.index()) != vectorType.getDimSize(ta.value()))
7321 return emitOpError(
"dimension size mismatch at: ") << ta.value();
7326std::optional<SmallVector<int64_t, 4>> TransposeOp::getShapeForUnroll() {
7327 return llvm::to_vector<4>(getResultVectorType().
getShape());
7330void TransposeOp::inferResultRanges(ArrayRef<ConstantIntRanges> argRanges,
7332 setResultRanges(getResult(), argRanges.front());
7338class TransposeFolder final :
public OpRewritePattern<vector::TransposeOp> {
7342 LogicalResult matchAndRewrite(vector::TransposeOp transposeOp,
7343 PatternRewriter &rewriter)
const override {
7345 auto composePermutations = [](ArrayRef<int64_t> permutation1,
7346 ArrayRef<int64_t> permutation2) {
7347 SmallVector<int64_t, 4>
result;
7348 for (
auto index : permutation2)
7349 result.push_back(permutation1[index]);
7354 vector::TransposeOp parentTransposeOp =
7355 transposeOp.getVector().getDefiningOp<vector::TransposeOp>();
7356 if (!parentTransposeOp)
7359 SmallVector<int64_t, 4> permutation = composePermutations(
7360 parentTransposeOp.getPermutation(), transposeOp.getPermutation());
7363 transposeOp, transposeOp.getResult().
getType(),
7364 parentTransposeOp.getVector(), permutation);
7370class FoldTransposeSplat final :
public OpRewritePattern<TransposeOp> {
7374 LogicalResult matchAndRewrite(TransposeOp transposeOp,
7375 PatternRewriter &rewriter)
const override {
7376 Value splat = getScalarSplatSource(transposeOp.getVector());
7381 transposeOp, transposeOp.getResultVectorType(), splat);
7387class FoldTransposeCreateMask final :
public OpRewritePattern<TransposeOp> {
7391 LogicalResult matchAndRewrite(TransposeOp transpOp,
7392 PatternRewriter &rewriter)
const override {
7393 Value transposeSrc = transpOp.getVector();
7394 auto createMaskOp = transposeSrc.
getDefiningOp<vector::CreateMaskOp>();
7395 auto constantMaskOp = transposeSrc.
getDefiningOp<vector::ConstantMaskOp>();
7396 if (!createMaskOp && !constantMaskOp)
7401 ArrayRef<int64_t> permutation = transpOp.getPermutation();
7404 auto maskOperands = createMaskOp.getOperands();
7405 SmallVector<Value> newOperands(maskOperands.begin(), maskOperands.end());
7409 transpOp, transpOp.getResultVectorType(), newOperands);
7414 auto maskDimSizes = constantMaskOp.getMaskDimSizes();
7418 transpOp, transpOp.getResultVectorType(), newMaskDimSizes);
7424class FoldTransposeShapeCast final :
public OpRewritePattern<TransposeOp> {
7428 LogicalResult matchAndRewrite(TransposeOp transposeOp,
7429 PatternRewriter &rewriter)
const override {
7431 transposeOp.getVector().getDefiningOp<vector::ShapeCastOp>();
7434 if (!isOrderPreserving(transposeOp))
7437 VectorType resultType = transposeOp.getType();
7444 shapeCastOp.getSource());
7463class FoldTransposeFromElements final :
public OpRewritePattern<TransposeOp> {
7466 LogicalResult matchAndRewrite(vector::TransposeOp transposeOp,
7467 PatternRewriter &rewriter)
const override {
7468 auto fromElementsOp =
7469 transposeOp.getVector().getDefiningOp<vector::FromElementsOp>();
7470 if (!fromElementsOp)
7473 VectorType srcTy = fromElementsOp.getDest().getType();
7474 VectorType dstTy = transposeOp.getType();
7476 ArrayRef<int64_t> permutation = transposeOp.getPermutation();
7477 int64_t rank = srcTy.getRank();
7480 SmallVector<int64_t> inversePerm(rank, 0);
7481 for (int64_t i = 0; i < rank; ++i)
7482 inversePerm[permutation[i]] = i;
7484 ArrayRef<int64_t> srcShape = srcTy.getShape();
7485 ArrayRef<int64_t> dstShape = dstTy.getShape();
7486 SmallVector<int64_t> srcIdx(rank, 0);
7487 SmallVector<int64_t> dstIdx(rank, 0);
7491 auto elementsOld = fromElementsOp.getElements();
7492 SmallVector<Value> elementsNew;
7493 int64_t dstNumElements = dstTy.getNumElements();
7494 elementsNew.reserve(dstNumElements);
7498 for (int64_t linearIdx = 0; linearIdx < dstNumElements; ++linearIdx) {
7502 for (int64_t j = 0; j < rank; ++j)
7503 srcIdx[j] = dstIdx[inversePerm[j]];
7505 int64_t srcLin =
linearize(srcIdx, srcStrides);
7507 elementsNew.push_back(elementsOld[srcLin]);
7541class FoldTransposeBroadcast :
public OpRewritePattern<vector::TransposeOp> {
7544 FoldTransposeBroadcast(MLIRContext *context, PatternBenefit benefit = 1)
7545 : OpRewritePattern<vector::TransposeOp>(context, benefit) {}
7547 LogicalResult matchAndRewrite(vector::TransposeOp transpose,
7548 PatternRewriter &rewriter)
const override {
7554 "not preceded by a broadcast");
7557 auto inputType = dyn_cast<VectorType>(
broadcast.getSourceType());
7558 VectorType outputType = transpose.getResultVectorType();
7561 bool inputIsScalar = !inputType;
7562 if (inputIsScalar) {
7568 ArrayRef<int64_t> permutation = transpose.getPermutation();
7569 ArrayRef<int64_t> inputShape = inputType.getShape();
7570 int64_t inputRank = inputType.getRank();
7571 int64_t outputRank = transpose.getType().getRank();
7572 int64_t deltaRank = outputRank - inputRank;
7575 for (
int inputIndex = 0; inputIndex < inputRank; ++inputIndex) {
7576 bool notOne = inputShape[inputIndex] != 1;
7577 bool prevNotOne = (inputIndex != 0 && inputShape[inputIndex - 1] != 1);
7578 bool groupEndFound = notOne || prevNotOne;
7579 if (groupEndFound) {
7580 int high = inputIndex + deltaRank;
7584 for (
int i = low; i < high; ++i) {
7585 if (permutation[i] < low || permutation[i] >= high) {
7587 transpose,
"permutation not local to group");
7601 vector::BroadcastableToResult::Success &&
7602 "not broadcastable directly to transpose output");
7613void vector::TransposeOp::getCanonicalizationPatterns(
7614 RewritePatternSet &results, MLIRContext *context) {
7615 results.
add<FoldTransposeCreateMask, FoldTransposeShapeCast, TransposeFolder,
7616 FoldTransposeSplat, FoldTransposeFromElements,
7617 FoldTransposeBroadcast>(context);
7624void ConstantMaskOp::build(OpBuilder &builder, OperationState &
result,
7626 assert(kind == ConstantMaskKind::AllTrue ||
7627 kind == ConstantMaskKind::AllFalse);
7628 build(builder,
result, type,
7629 kind == ConstantMaskKind::AllTrue
7631 : SmallVector<int64_t>(type.getRank(), 0));
7634LogicalResult ConstantMaskOp::verify() {
7635 auto resultType = llvm::cast<VectorType>(getResult().
getType());
7637 if (resultType.getRank() == 0) {
7638 if (getMaskDimSizes().size() != 1)
7639 return emitError(
"array attr must have length 1 for 0-D vectors");
7640 auto dim = getMaskDimSizes()[0];
7641 if (dim != 0 && dim != 1)
7642 return emitError(
"mask dim size must be either 0 or 1 for 0-D vectors");
7647 if (
static_cast<int64_t
>(getMaskDimSizes().size()) != resultType.getRank())
7649 "must specify array attr of size equal vector result rank");
7652 auto resultShape = resultType.getShape();
7653 auto resultScalableDims = resultType.getScalableDims();
7654 ArrayRef<int64_t> maskDimSizes = getMaskDimSizes();
7655 for (
const auto [index, maskDimSize] : llvm::enumerate(maskDimSizes)) {
7656 if (maskDimSize < 0 || maskDimSize > resultShape[index])
7658 "array attr of size out of bounds of vector result dimension size");
7659 if (resultScalableDims[index] && maskDimSize != 0 &&
7660 maskDimSize != resultShape[index])
7662 "only supports 'none set' or 'all set' scalable dimensions");
7666 bool anyZeros = llvm::is_contained(maskDimSizes, 0);
7667 bool allZeros = llvm::all_of(maskDimSizes, [](int64_t s) {
return s == 0; });
7668 if (anyZeros && !allZeros)
7669 return emitOpError(
"expected all mask dim sizes to be zeros, "
7670 "as a result of conjunction with zero mask dim");
7674bool ConstantMaskOp::isAllOnesMask() {
7677 if (resultType.getRank() == 0) {
7678 assert(getMaskDimSizes().size() == 1 &&
"invalid sizes for zero rank mask");
7679 return getMaskDimSizes()[0] == 1;
7681 for (
const auto [resultSize, maskDimSize] :
7682 llvm::zip_equal(resultType.getShape(), getMaskDimSizes())) {
7683 if (maskDimSize < resultSize)
7689OpFoldResult ConstantMaskOp::fold(FoldAdaptor adaptor) {
7690 ArrayRef<int64_t> bounds = getMaskDimSizes();
7693 auto createBoolSplat = [&](
bool x) {
7699 if (vectorSizes.empty()) {
7700 assert(bounds.size() == 1 &&
"invalid sizes for zero rank mask");
7701 return createBoolSplat(bounds[0] == 1);
7704 if (bounds == vectorSizes)
7705 return createBoolSplat(
true);
7706 if (llvm::all_of(bounds, [](int64_t x) {
return x == 0; }))
7707 return createBoolSplat(
false);
7708 return OpFoldResult();
7715void CreateMaskOp::build(OpBuilder &builder, OperationState &
result,
7717 ArrayRef<OpFoldResult> mixedOperands) {
7718 SmallVector<Value> operands =
7720 build(builder,
result, type, operands);
7723LogicalResult CreateMaskOp::verify() {
7724 auto vectorType = llvm::cast<VectorType>(getResult().
getType());
7726 if (vectorType.getRank() == 0) {
7727 if (getNumOperands() != 1)
7729 "must specify exactly one operand for 0-D create_mask");
7730 }
else if (getNumOperands() !=
7731 llvm::cast<VectorType>(getResult().
getType()).getRank()) {
7733 "must specify an operand for each result vector dimension");
7763class CreateMaskFolder final :
public OpRewritePattern<CreateMaskOp> {
7767 LogicalResult matchAndRewrite(CreateMaskOp createMaskOp,
7768 PatternRewriter &rewriter)
const override {
7769 VectorType maskType = createMaskOp.getVectorType();
7770 ArrayRef<int64_t> maskTypeDimSizes = maskType.getShape();
7771 ArrayRef<bool> maskTypeDimScalableFlags = maskType.getScalableDims();
7774 constexpr std::array<int64_t, 1> rankZeroShape{1};
7775 constexpr std::array<bool, 1> rankZeroScalableDims{
false};
7776 if (maskType.getRank() == 0) {
7777 maskTypeDimSizes = rankZeroShape;
7778 maskTypeDimScalableFlags = rankZeroScalableDims;
7783 SmallVector<int64_t, 4> constantDims;
7784 for (
auto [i, dimSize] : llvm::enumerate(createMaskOp.getOperands())) {
7789 if (maskTypeDimScalableFlags[i] && intSize >= 0)
7791 constantDims.push_back(*intSize);
7795 if (vscaleMultiplier < maskTypeDimSizes[i])
7797 constantDims.push_back(*vscaleMultiplier);
7804 for (
auto [value, maskDimSize] : llvm::zip(constantDims, maskTypeDimSizes))
7805 value = std::clamp<int64_t>(value, 0, maskDimSize);
7808 if (llvm::is_contained(constantDims, 0))
7809 constantDims.assign(constantDims.size(), 0);
7820void CreateMaskOp::getCanonicalizationPatterns(RewritePatternSet &results,
7821 MLIRContext *context) {
7822 results.
add<CreateMaskFolder>(context);
7830 OpBuilder &builder, OperationState &
result, Value mask,
7831 Operation *maskableOp,
7832 function_ref<
void(OpBuilder &, Operation *)> maskRegionBuilder) {
7833 assert(maskRegionBuilder &&
7834 "builder callback for 'maskRegion' must be present");
7836 result.addOperands(mask);
7837 OpBuilder::InsertionGuard guard(builder);
7838 Region *maskRegion =
result.addRegion();
7840 maskRegionBuilder(builder, maskableOp);
7845 Value mask, Operation *maskableOp,
7846 function_ref<
void(OpBuilder &, Operation *)> maskRegionBuilder) {
7847 build(builder,
result, resultTypes, mask, Value(), maskableOp,
7853 Value mask, Value passthru, Operation *maskableOp,
7854 function_ref<
void(OpBuilder &, Operation *)> maskRegionBuilder) {
7855 build(builder,
result, mask, maskableOp, maskRegionBuilder);
7857 result.addOperands(passthru);
7858 result.addTypes(resultTypes);
7861ParseResult MaskOp::parse(OpAsmParser &parser, OperationState &
result) {
7863 result.regions.reserve(1);
7864 Region &maskRegion = *
result.addRegion();
7869 OpAsmParser::UnresolvedOperand mask;
7874 OpAsmParser::UnresolvedOperand passthru;
7876 if (parsePassthru.succeeded() && parser.
parseOperand(passthru))
7883 MaskOp::ensureTerminator(maskRegion, builder,
result.location);
7894 SmallVector<Type> resultTypes;
7897 result.types.append(resultTypes);
7903 if (parsePassthru.succeeded()) {
7904 if (resultTypes.empty())
7907 "expects a result if passthru operand is provided");
7916void mlir::vector::MaskOp::print(OpAsmPrinter &p) {
7917 p <<
" " << getMask();
7919 p <<
", " << getPassthru();
7923 Block *singleBlock = &getMaskRegion().getBlocks().front();
7930 p <<
" : " << getMask().getType();
7931 if (getNumResults() > 0)
7932 p <<
" -> " << getResultTypes();
7935void MaskOp::ensureTerminator(Region ®ion, Builder &builder, Location loc) {
7938 OpTrait::SingleBlockImplicitTerminator<vector::YieldOp>::Impl<
7939 MaskOp>::ensureTerminator(region, builder, loc);
7945 if (isa<vector::YieldOp>(block.
back()))
7953 OpTrait::SingleBlockImplicitTerminator<vector::YieldOp>::Impl<
7954 MaskOp>::ensureTerminator(region, builder, loc);
7960 Operation *maskedOp = &block.
front();
7961 opBuilder.setInsertionPointToEnd(&block);
7962 vector::YieldOp::create(opBuilder, loc, maskedOp->
getResults());
7965LogicalResult MaskOp::verify() {
7967 Block &block = getMaskRegion().getBlocks().
front();
7969 return emitOpError(
"expects a terminator within the mask region");
7972 if (numMaskRegionOps > 2)
7973 return emitOpError(
"expects only one operation to mask");
7976 auto terminator = dyn_cast<vector::YieldOp>(block.
back());
7978 return emitOpError(
"expects a terminator within the mask region");
7980 if (terminator->getNumOperands() != getNumResults())
7982 "expects number of results to match mask region yielded values");
7985 if (numMaskRegionOps == 1)
7988 auto maskableOp = dyn_cast<MaskableOpInterface>(block.
front());
7990 return emitOpError(
"expects a MaskableOpInterface within the mask region");
7994 return emitOpError(
"expects number of results to match maskable operation "
7995 "number of results");
7997 if (!llvm::equal(maskableOp->
getResults(), terminator.getOperands()))
7998 return emitOpError(
"expects all the results from the MaskableOpInterface "
7999 "to match all the values returned by the terminator");
8001 if (!llvm::equal(maskableOp->
getResultTypes(), getResultTypes()))
8003 "expects result type to match maskable operation result type");
8006 [](Type t) { return llvm::isa<VectorType>(t); }) > 1)
8007 return emitOpError(
"multiple vector results not supported");
8010 Type expectedMaskType = maskableOp.getExpectedMaskType();
8011 if (getMask().
getType() != expectedMaskType)
8013 << expectedMaskType <<
" mask for the maskable operation";
8016 Value passthru = getPassthru();
8018 if (!maskableOp.supportsPassthru())
8020 "doesn't expect a passthru argument for this maskable operation");
8023 return emitOpError(
"expects result when passthru argument is provided");
8026 return emitOpError(
"expects passthru type to match result type");
8046static LogicalResult foldEmptyMaskOp(MaskOp maskOp, MaskOp::FoldAdaptor adaptor,
8047 SmallVectorImpl<OpFoldResult> &results) {
8048 if (!maskOp.isEmpty() || maskOp.hasPassthru())
8051 Block *block = maskOp.getMaskBlock();
8052 auto terminator = cast<vector::YieldOp>(block->
front());
8053 if (terminator.getNumOperands() == 0)
8057 llvm::append_range(results, terminator.getOperands());
8061LogicalResult MaskOp::fold(FoldAdaptor adaptor,
8062 SmallVectorImpl<OpFoldResult> &results) {
8063 if (succeeded(foldEmptyMaskOp(*
this, adaptor, results)))
8073 Operation *maskableOp = getMaskableOp();
8079 llvm::append_range(results, maskableOp->
getResults());
8095class CanonializeEmptyMaskOp :
public OpRewritePattern<MaskOp> {
8098 LogicalResult matchAndRewrite(MaskOp maskOp,
8099 PatternRewriter &rewriter)
const override {
8100 if (!maskOp.isEmpty())
8103 if (!maskOp.hasPassthru())
8110 VectorType maskType = maskOp.getMask().getType();
8111 for (Type resultType : maskOp.getResultTypes()) {
8112 auto vecResultType = dyn_cast<VectorType>(resultType);
8113 if (!vecResultType || vecResultType.getShape() != maskType.getShape())
8117 Block *block = maskOp.getMaskBlock();
8118 auto terminator = cast<vector::YieldOp>(block->
front());
8119 assert(terminator.getNumOperands() == 1 &&
8120 "expected one result when passthru is provided");
8123 maskOp, maskOp.getResultTypes(), maskOp.getMask(),
8124 terminator.getOperand(0), maskOp.getPassthru());
8130void MaskOp::getCanonicalizationPatterns(RewritePatternSet &results,
8131 MLIRContext *context) {
8132 results.
add<CanonializeEmptyMaskOp>(context);
8138Operation *MaskOp::getMaskableOp() {
8139 Block *block = getMaskBlock();
8143 return &block->
front();
8147bool MaskOp::hasPassthru() {
return getPassthru() != Value(); }
8153LogicalResult ScanOp::verify() {
8154 VectorType srcType = getSourceType();
8155 VectorType initialType = getInitialValueType();
8157 int64_t srcRank = srcType.getRank();
8158 int64_t reductionDim = getReductionDim();
8159 if (reductionDim >= srcRank)
8161 << reductionDim <<
" has to be less than " << srcRank;
8164 int64_t initialValueRank = initialType.getRank();
8165 if (initialValueRank != srcRank - 1)
8167 << initialValueRank <<
" has to be equal to " << srcRank - 1;
8170 ArrayRef<int64_t> srcShape = srcType.getShape();
8171 ArrayRef<int64_t> initialValueShapes = initialType.getShape();
8172 SmallVector<int64_t> expectedShape;
8173 for (
int i = 0; i < srcRank; i++) {
8174 if (i != reductionDim)
8175 expectedShape.push_back(srcShape[i]);
8177 if (!llvm::equal(initialValueShapes, expectedShape)) {
8178 return emitOpError(
"incompatible input/initial value shapes");
8182 Type eltType = getDestType().getElementType();
8185 << eltType <<
" for kind '" << stringifyCombiningKind(getKind())
8192 RewritePatternSet &patterns, PatternBenefit benefit) {
8194 .
add<CreateMaskFolder, MaskedLoadFolder, MaskedStoreFolder, GatherFolder,
8195 ScatterFolder, ExpandLoadFolder, CompressStoreFolder,
8196 StridedSliceConstantMaskFolder, TransposeFolder>(
8201 CombiningKind kind, Value v1, Value acc,
8202 arith::FastMathFlagsAttr fastmath,
8209 case CombiningKind::ADD:
8211 result =
b.createOrFold<arith::AddIOp>(loc, v1, acc);
8212 else if (llvm::isa<FloatType>(t1) && llvm::isa<FloatType>(tAcc))
8213 result =
b.createOrFold<arith::AddFOp>(loc, v1, acc, fastmath);
8215 llvm_unreachable(
"invalid value types for ADD reduction");
8217 case CombiningKind::AND:
8219 result =
b.createOrFold<arith::AndIOp>(loc, v1, acc);
8221 case CombiningKind::MAXNUMF:
8222 assert(llvm::isa<FloatType>(t1) && llvm::isa<FloatType>(tAcc) &&
8223 "expected float values");
8224 result =
b.createOrFold<arith::MaxNumFOp>(loc, v1, acc, fastmath);
8226 case CombiningKind::MAXIMUMF:
8227 assert(llvm::isa<FloatType>(t1) && llvm::isa<FloatType>(tAcc) &&
8228 "expected float values");
8229 result =
b.createOrFold<arith::MaximumFOp>(loc, v1, acc, fastmath);
8231 case CombiningKind::MINNUMF:
8232 assert(llvm::isa<FloatType>(t1) && llvm::isa<FloatType>(tAcc) &&
8233 "expected float values");
8234 result =
b.createOrFold<arith::MinNumFOp>(loc, v1, acc, fastmath);
8236 case CombiningKind::MINIMUMF:
8237 assert(llvm::isa<FloatType>(t1) && llvm::isa<FloatType>(tAcc) &&
8238 "expected float values");
8239 result =
b.createOrFold<arith::MinimumFOp>(loc, v1, acc, fastmath);
8241 case CombiningKind::MAXSI:
8243 result =
b.createOrFold<arith::MaxSIOp>(loc, v1, acc);
8245 case CombiningKind::MINSI:
8247 result =
b.createOrFold<arith::MinSIOp>(loc, v1, acc);
8249 case CombiningKind::MAXUI:
8251 result =
b.createOrFold<arith::MaxUIOp>(loc, v1, acc);
8253 case CombiningKind::MINUI:
8255 result =
b.createOrFold<arith::MinUIOp>(loc, v1, acc);
8257 case CombiningKind::MUL:
8259 result =
b.createOrFold<arith::MulIOp>(loc, v1, acc);
8260 else if (llvm::isa<FloatType>(t1) && llvm::isa<FloatType>(tAcc))
8261 result =
b.createOrFold<arith::MulFOp>(loc, v1, acc, fastmath);
8263 llvm_unreachable(
"invalid value types for MUL reduction");
8265 case CombiningKind::OR:
8267 result =
b.createOrFold<arith::OrIOp>(loc, v1, acc);
8269 case CombiningKind::XOR:
8271 result =
b.createOrFold<arith::XOrIOp>(loc, v1, acc);
8275 assert(
result &&
"unknown CombiningKind");
8283void StepOp::inferResultRanges(ArrayRef<ConstantIntRanges> argRanges,
8285 auto resultType = cast<VectorType>(
getType());
8286 if (resultType.isScalable()) {
8292 uint64_t maxIndex = resultType.getDimSize(0) - 1;
8293 APInt umin = APInt::getZero(bitwidth);
8294 APInt umax = APInt::getMaxValue(bitwidth).ugt(maxIndex)
8295 ? APInt(bitwidth, maxIndex)
8296 : APInt::getMaxValue(bitwidth);
8327struct StepCompareFolder :
public OpRewritePattern<StepOp> {
8330 LogicalResult matchAndRewrite(StepOp stepOp,
8331 PatternRewriter &rewriter)
const override {
8332 const int64_t stepSize = stepOp.getResult().getType().getNumElements();
8334 for (OpOperand &use : stepOp.getResult().getUses()) {
8335 auto cmpiOp = dyn_cast<arith::CmpIOp>(use.getOwner());
8340 const unsigned stepOperandNumber = use.getOperandNumber();
8341 if (stepOperandNumber != 0)
8345 unsigned constOperandNumber = 1;
8346 Value otherOperand = cmpiOp.getOperand(constOperandNumber);
8347 std::optional<int64_t> maybeConstValue =
8349 if (!maybeConstValue.has_value())
8352 int64_t constValue = maybeConstValue.value();
8353 arith::CmpIPredicate pred = cmpiOp.getPredicate();
8355 auto maybeSplat = [&]() -> std::optional<bool> {
8357 if ((pred == arith::CmpIPredicate::ult ||
8358 pred == arith::CmpIPredicate::uge) &&
8359 stepSize <= constValue)
8360 return pred == arith::CmpIPredicate::ult;
8363 if ((pred == arith::CmpIPredicate::ule ||
8364 pred == arith::CmpIPredicate::ugt) &&
8365 stepSize - 1 <= constValue) {
8366 return pred == arith::CmpIPredicate::ule;
8370 if ((pred == arith::CmpIPredicate::eq ||
8371 pred == arith::CmpIPredicate::ne) &&
8372 stepSize <= constValue)
8373 return pred == arith::CmpIPredicate::ne;
8375 return std::nullopt;
8378 if (!maybeSplat.has_value())
8383 auto type = dyn_cast<VectorType>(cmpiOp.getResult().getType());
8388 Value splat = mlir::arith::ConstantOp::create(rewriter, cmpiOp.getLoc(),
8400void StepOp::getCanonicalizationPatterns(RewritePatternSet &results,
8401 MLIRContext *context) {
8402 results.
add<StepCompareFolder>(context);
8412 Operation *maskableOp) {
8413 assert(maskableOp->
getBlock() &&
"MaskableOp must be inserted into a block");
8425 Operation *maskableOp, Value mask,
8430 return MaskOp::create(builder, maskableOp->
getLoc(),
8433 return MaskOp::create(builder, maskableOp->
getLoc(),
8446 Value newValue, Value passthru) {
8450 return arith::SelectOp::create(builder, newValue.
getLoc(), newValue.
getType(),
8451 mask, newValue, passthru);
8462struct InterleaveDeinterleaveFolder :
public OpRewritePattern<InterleaveOp> {
8465 LogicalResult matchAndRewrite(InterleaveOp interleaveOp,
8466 PatternRewriter &rewriter)
const override {
8467 auto lhsDefOp = interleaveOp.getLhs().getDefiningOp<DeinterleaveOp>();
8468 auto rhsDefOp = interleaveOp.getRhs().getDefiningOp<DeinterleaveOp>();
8469 if (!lhsDefOp || !rhsDefOp || lhsDefOp != rhsDefOp)
8471 for (
auto [idx, operand] : llvm::enumerate(interleaveOp.getOperands())) {
8472 if (cast<OpResult>(operand).getResultNumber() != idx)
8475 rewriter.
replaceOp(interleaveOp, lhsDefOp.getSource());
8481void InterleaveOp::getCanonicalizationPatterns(RewritePatternSet &results,
8482 MLIRContext *context) {
8483 results.
add<InterleaveDeinterleaveFolder>(context);
8486std::optional<SmallVector<int64_t, 4>> InterleaveOp::getShapeForUnroll() {
8487 return llvm::to_vector<4>(getResultVectorType().
getShape());
8494std::optional<SmallVector<int64_t, 4>> DeinterleaveOp::getShapeForUnroll() {
8495 return llvm::to_vector<4>(getResultVectorType().
getShape());
8502#define GET_ATTRDEF_CLASSES
8503#include "mlir/Dialect/Vector/IR/VectorAttributes.cpp.inc"
8505#define GET_OP_CLASSES
8506#include "mlir/Dialect/Vector/IR/VectorOps.cpp.inc"
p<< " : "<< getMemRefType()<< ", "<< getType();}static LogicalResult verifyVectorMemoryOp(Operation *op, MemRefType memrefType, VectorType vectorType) { if(memrefType.getElementType() !=vectorType.getElementType()) return op-> emitOpError("requires memref and vector types of the same elemental type")
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 Type getElementType(Type type)
Determine the element type of type.
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 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 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 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.
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.
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)