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 (op.getVectorType().getScalableDims()[resultIdx])
5386 if (!cstOp.has_value())
5389 int64_t sourceSize = op.getShapedType().getDimSize(indicesIdx);
5390 int64_t vectorSize = op.getVectorType().getDimSize(resultIdx);
5392 return cstOp.value() + vectorSize <= sourceSize;
5395template <
typename TransferOp>
5399 if (op.getTransferRank() == 0)
5402 bool changed =
false;
5404 newInBounds.reserve(op.getTransferRank());
5409 for (
unsigned i = 0; i < op.getTransferRank(); ++i) {
5411 if (op.isDimInBounds(i)) {
5412 newInBounds.push_back(
true);
5417 bool inBounds =
false;
5418 auto dimExpr = dyn_cast<AffineDimExpr>(permutationMap.
getResult(i));
5421 dimExpr.getPosition());
5422 nonBcastDims.push_back(i);
5425 newInBounds.push_back(inBounds);
5427 changed |= inBounds;
5433 bool allNonBcastDimsInBounds = llvm::all_of(
5434 nonBcastDims, [&newInBounds](
unsigned idx) {
return newInBounds[idx]; });
5435 if (allNonBcastDimsInBounds) {
5437 changed |= !newInBounds[idx];
5438 newInBounds[idx] =
true;
5446 op.setInBoundsAttr(
b.getBoolArrayAttr(newInBounds));
5450template <
typename TransferOp>
5452 auto mask = op.getMask();
5459 op.getMaskMutable().clear();
5467template <
typename TransferOp>
5469 VectorType vecType = op.getVectorType();
5470 if (vecType.getRank() != 1 || vecType.getShape()[0] != 1 ||
5471 vecType.isScalable())
5478 int64_t srcRank = op.getShapedType().getRank();
5484 op.setPermutationMapAttr(AffineMapAttr::get(minorIdentity));
5498static Value foldRAW(TransferReadOp readOp) {
5499 if (!llvm::isa<RankedTensorType>(readOp.getShapedType()))
5501 auto defWrite = readOp.getBase().getDefiningOp<vector::TransferWriteOp>();
5504 return defWrite.getVector();
5506 cast<VectorTransferOpInterface>(defWrite.getOperation()),
5507 cast<VectorTransferOpInterface>(readOp.getOperation())))
5509 defWrite = defWrite.getBase().getDefiningOp<vector::TransferWriteOp>();
5514OpFoldResult TransferReadOp::fold(FoldAdaptor) {
5515 if (Value vec = foldRAW(*
this))
5528 return OpFoldResult();
5531std::optional<SmallVector<int64_t, 4>> TransferReadOp::getShapeForUnroll() {
5535void TransferReadOp::getEffects(
5536 SmallVectorImpl<SideEffects::EffectInstance<MemoryEffects::Effect>>
5538 if (llvm::isa<MemRefType>(getShapedType()))
5539 effects.emplace_back(MemoryEffects::Read::get(), &getBaseMutable(),
5540 SideEffects::DefaultResource::get());
5544 if (hasPureTensorSemantics())
5551static AffineMap inverseWithUnusedDims(AffineMap map) {
5553 "expected a projected permutation map");
5558 int64_t pos = cast<AffineDimExpr>(
result).getPosition();
5588struct TransferReadAfterWriteToBroadcast
5589 :
public OpRewritePattern<TransferReadOp> {
5592 LogicalResult matchAndRewrite(TransferReadOp readOp,
5593 PatternRewriter &rewriter)
const override {
5594 auto defWrite = readOp.getBase().getDefiningOp<vector::TransferWriteOp>();
5598 if (!readOp.hasPureTensorSemantics() || !defWrite.hasPureTensorSemantics())
5602 if (readOp.getMask() || defWrite.getMask())
5605 if (readOp.getIndices() != defWrite.getIndices())
5608 if (readOp.hasOutOfBoundsDim() || defWrite.hasOutOfBoundsDim())
5612 if (readOp.getTransferChunkAccessed() !=
5613 defWrite.getTransferChunkAccessed())
5620 AffineMap readMap = readOp.getPermutationMap();
5621 AffineMap writeMap = defWrite.getPermutationMap();
5622 AffineMap invWriteMap = inverseWithUnusedDims(writeMap);
5623 AffineMap composedMap = readMap.
compose(invWriteMap);
5637 int64_t numBroadcastedDims = broadcastedDims.size();
5638 auto invPerm = llvm::to_vector_of<int64_t>(broadcastedDims);
5640 for (
auto [idx, expr] : llvm::enumerate(composedMap.
getResults())) {
5641 if (
auto dim = dyn_cast<AffineDimExpr>(expr)) {
5642 int64_t effectiveDim = dim.getPosition() + numBroadcastedDims;
5643 invPerm[effectiveDim] = idx;
5648 VectorType readVecTy = readOp.getVectorType();
5650 auto broadcastedVecTy =
5652 readVecTy.getElementType(),
5655 Value vec = defWrite.getVector();
5656 Location loc = readOp.getLoc();
5657 vec = vector::BroadcastOp::create(rewriter, loc, broadcastedVecTy, vec);
5664void TransferReadOp::getCanonicalizationPatterns(RewritePatternSet &results,
5665 MLIRContext *context) {
5666 results.
add<TransferReadAfterWriteToBroadcast>(context);
5669FailureOr<std::optional<SmallVector<Value>>>
5670TransferReadOp::bubbleDownCasts(OpBuilder &builder) {
5671 if (!hasPureBufferSemantics())
5682void TransferWriteOp::build(OpBuilder &builder, OperationState &
result,
5684 AffineMapAttr permutationMapAttr,
5687 Type resultType = llvm::dyn_cast<RankedTensorType>(dest.
getType());
5688 build(builder,
result, resultType, vector, dest,
indices, permutationMapAttr,
5689 mask, inBoundsAttr);
5693void TransferWriteOp::build(OpBuilder &builder, OperationState &
result,
5695 AffineMapAttr permutationMapAttr,
5697 build(builder,
result, vector, dest,
indices, permutationMapAttr,
5698 Value(), inBoundsAttr);
5703void TransferWriteOp::build(OpBuilder &builder, OperationState &
result,
5705 AffineMap permutationMap,
5706 std::optional<ArrayRef<bool>> inBounds) {
5707 if (!permutationMap)
5710 llvm::cast<VectorType>(vector.
getType()));
5711 auto permutationMapAttr = AffineMapAttr::get(permutationMap);
5713 (inBounds && !inBounds.value().empty())
5716 llvm::cast<VectorType>(vector.
getType()).getRank(),
false));
5717 build(builder,
result, vector, dest,
indices, permutationMapAttr,
5718 Value(), inBoundsAttr);
5723void TransferWriteOp::build(OpBuilder &builder, OperationState &
result,
5725 std::optional<ArrayRef<bool>> inBounds) {
5730ParseResult TransferWriteOp::parse(OpAsmParser &parser,
5731 OperationState &
result) {
5734 OpAsmParser::UnresolvedOperand vectorInfo, sourceInfo;
5735 SmallVector<OpAsmParser::UnresolvedOperand, 8> indexInfo;
5736 SmallVector<Type, 2> types;
5737 OpAsmParser::UnresolvedOperand maskInfo;
5743 if (hasMask.succeeded() && parser.
parseOperand(maskInfo))
5748 if (types.size() != 2)
5749 return parser.
emitError(typesLoc,
"requires two types");
5751 VectorType vectorType = llvm::dyn_cast<VectorType>(types[0]);
5753 return parser.
emitError(typesLoc,
"requires vector type");
5754 ShapedType shapedType = llvm::dyn_cast<ShapedType>(types[1]);
5755 if (!shapedType || !llvm::isa<MemRefType, RankedTensorType>(shapedType))
5756 return parser.
emitError(typesLoc,
"requires memref or ranked tensor type");
5757 auto permMapAttrName =
5758 TransferWriteOp::getPermutationMapAttrName(
result.name);
5759 auto permMapAttr =
result.attributes.get(permMapAttrName);
5762 if (shapedType.getRank() <
5765 "expected a custom permutation_map when "
5766 "rank(source) != rank(destination)");
5768 result.attributes.set(permMapAttrName, AffineMapAttr::get(permMap));
5770 permMap = llvm::cast<AffineMapAttr>(permMapAttr).getValue();
5772 auto inBoundsAttrName = TransferWriteOp::getInBoundsAttrName(
result.name);
5773 Attribute inBoundsAttr =
result.attributes.get(inBoundsAttrName);
5774 if (!inBoundsAttr) {
5775 result.addAttribute(inBoundsAttrName,
5783 if (hasMask.succeeded()) {
5784 if (llvm::dyn_cast<VectorType>(shapedType.getElementType()))
5786 maskInfo.
location,
"does not support masks with vector element type");
5789 "expected the same rank for the vector and the "
5790 "results of the permutation map");
5796 result.addAttribute(TransferWriteOp::getOperandSegmentSizeAttr(),
5798 {1, 1, static_cast<int32_t>(indexInfo.size()),
5799 static_cast<int32_t>(hasMask.succeeded())}));
5800 return failure(llvm::isa<RankedTensorType>(shapedType) &&
5804void TransferWriteOp::print(OpAsmPrinter &p) {
5807 p <<
", " << getMask();
5812LogicalResult TransferWriteOp::verify() {
5814 ShapedType shapedType = getShapedType();
5816 VectorType maskType = getMaskType();
5817 auto permutationMap = getPermutationMap();
5818 VectorType inferredMaskType =
5822 if (llvm::size(
getIndices()) != shapedType.getRank())
5823 return emitOpError(
"requires ") << shapedType.getRank() <<
" indices";
5827 if (hasBroadcastDim())
5828 return emitOpError(
"should not have broadcast dimensions");
5831 shapedType, vectorType, maskType,
5832 inferredMaskType, permutationMap, getInBounds())))
5845Type TransferWriteOp::getExpectedMaskType() {
5852Value TransferWriteOp::getVector() {
return getOperand(0); }
5853VectorType TransferWriteOp::getVectorType() {
5854 return cast<VectorType>(getValueToStore().
getType());
5877static LogicalResult foldReadInitWrite(TransferWriteOp write,
5878 ArrayRef<Attribute>,
5879 SmallVectorImpl<OpFoldResult> &results) {
5881 if (write.getTransferRank() == 0)
5883 auto rankedTensorType =
5884 llvm::dyn_cast<RankedTensorType>(write.getBase().getType());
5886 if (!rankedTensorType)
5889 auto read = write.getVector().getDefiningOp<vector::TransferReadOp>();
5893 if (read.getTransferRank() == 0)
5896 if (!read.getPermutationMap().isMinorIdentity() ||
5897 !write.getPermutationMap().isMinorIdentity())
5900 if (read.getTransferRank() != write.getTransferRank())
5903 if (read.hasOutOfBoundsDim() || write.hasOutOfBoundsDim())
5906 if (read.getMask() || write.getMask())
5909 if (read.getBase().getType() != rankedTensorType)
5912 if (read.getVectorType() != write.getVectorType())
5915 if (read.getVectorType().getShape() != rankedTensorType.getShape())
5918 auto isNotConstantZero = [](Value v) {
5920 return !cstOp.has_value() || cstOp.value() != 0;
5922 if (llvm::any_of(read.getIndices(), isNotConstantZero) ||
5923 llvm::any_of(write.getIndices(), isNotConstantZero))
5926 results.push_back(read.getBase());
5930static bool checkSameValueWAR(vector::TransferReadOp read,
5931 vector::TransferWriteOp write) {
5932 return read.getBase() == write.getBase() &&
5933 read.getIndices() == write.getIndices() &&
5934 read.getPermutationMap() == write.getPermutationMap() &&
5935 read.getVectorType() == write.getVectorType() && !read.getMask() &&
5952static LogicalResult foldWAR(TransferWriteOp write,
5953 SmallVectorImpl<OpFoldResult> &results) {
5954 if (!llvm::isa<RankedTensorType>(write.getBase().getType()))
5956 auto read = write.getVector().getDefiningOp<vector::TransferReadOp>();
5960 if (!checkSameValueWAR(read, write))
5962 results.push_back(read.getBase());
5966LogicalResult TransferWriteOp::fold(FoldAdaptor adaptor,
5967 SmallVectorImpl<OpFoldResult> &results) {
5968 if (succeeded(foldReadInitWrite(*
this, adaptor.getOperands(), results)))
5970 if (succeeded(foldWAR(*
this, results)))
5984std::optional<SmallVector<int64_t, 4>> TransferWriteOp::getShapeForUnroll() {
5988void TransferWriteOp::getEffects(
5989 SmallVectorImpl<SideEffects::EffectInstance<MemoryEffects::Effect>>
5991 if (llvm::isa<MemRefType>(getShapedType()))
5992 effects.emplace_back(MemoryEffects::Write::get(), &getBaseMutable(),
5993 SideEffects::DefaultResource::get());
5997 if (hasPureTensorSemantics())
6027class FoldWaw final :
public OpRewritePattern<TransferWriteOp> {
6030 LogicalResult matchAndRewrite(TransferWriteOp writeOp,
6031 PatternRewriter &rewriter)
const override {
6032 if (!llvm::isa<RankedTensorType>(writeOp.getShapedType()))
6034 vector::TransferWriteOp writeToModify = writeOp;
6036 auto defWrite = writeOp.getBase().getDefiningOp<vector::TransferWriteOp>();
6040 writeToModify.getBaseMutable().assign(defWrite.getBase());
6045 cast<VectorTransferOpInterface>(defWrite.getOperation()),
6046 cast<VectorTransferOpInterface>(writeOp.getOperation())))
6050 if (!defWrite->hasOneUse())
6052 writeToModify = defWrite;
6053 defWrite = defWrite.getBase().getDefiningOp<vector::TransferWriteOp>();
6082struct SwapExtractSliceOfTransferWrite
6083 :
public OpRewritePattern<tensor::InsertSliceOp> {
6087 LogicalResult matchAndRewrite(tensor::InsertSliceOp insertOp,
6088 PatternRewriter &rewriter)
const override {
6089 if (!insertOp.hasUnitStride())
6092 insertOp.getSource().getDefiningOp<tensor::ExtractSliceOp>();
6093 if (!extractOp || !extractOp.hasUnitStride() || !extractOp->hasOneUse())
6095 auto transferOp = extractOp.getSource().getDefiningOp<TransferWriteOp>();
6096 if (!transferOp || !transferOp->hasOneUse())
6101 if (insertOp.getSourceType().getRank() != transferOp.getTransferRank()) {
6103 "use-def chain is rank-reducing");
6107 if (!extractOp.hasZeroOffset()) {
6109 "ExtractSliceOp has non-zero offset");
6113 if (!llvm::all_of(transferOp.getIndices(), [](Value value) {
6114 return getConstantIntValue(value) == static_cast<int64_t>(0);
6117 "TranferWriteOp has non-zero offset");
6121 if (insertOp.getMixedSizes().size() != extractOp.getMixedSizes().size()) {
6123 insertOp,
"InsertSliceOp and ExtractSliceOp ranks differ");
6126 for (
auto [insertSize, extractSize] :
6127 llvm::zip_equal(insertOp.getMixedSizes(), extractOp.getMixedSizes())) {
6130 insertOp,
"InsertSliceOp and ExtractSliceOp sizes differ");
6135 assert(transferOp.getVectorType().hasStaticShape() &&
6136 "expected vector to have a static shape");
6137 ArrayRef<int64_t>
vectorShape = transferOp.getVectorType().getShape();
6139 transferOp.getPermutationMap(), transferOp.getShapedType().getShape());
6140 if (transferOp.getMask() || !
vectorShape.equals(resultShape)) {
6142 insertOp,
"TransferWriteOp may not write the full tensor.");
6147 SmallVector<bool> newInBounds(
vectorShape.size(),
false);
6148 auto newExtractOp = tensor::ExtractSliceOp::create(
6149 rewriter, extractOp.getLoc(), insertOp.getSourceType(),
6150 insertOp.getDest(), insertOp.getMixedOffsets(),
6151 insertOp.getMixedSizes(), insertOp.getMixedStrides());
6152 auto newTransferWriteOp = TransferWriteOp::create(
6153 rewriter, transferOp.getLoc(), transferOp.getVector(),
6154 newExtractOp.getResult(), transferOp.getIndices(),
6155 transferOp.getPermutationMapAttr(),
6158 insertOp.getSourceMutable().assign(newTransferWriteOp.getResult());
6166void TransferWriteOp::getCanonicalizationPatterns(RewritePatternSet &results,
6167 MLIRContext *context) {
6168 results.
add<FoldWaw, SwapExtractSliceOfTransferWrite>(context);
6171FailureOr<std::optional<SmallVector<Value>>>
6172TransferWriteOp::bubbleDownCasts(OpBuilder &builder) {
6173 if (!hasPureBufferSemantics())
6183static LogicalResult verifyLoadStoreMemRefLayout(Operation *op,
6185 MemRefType memRefTy) {
6188 if (!vecTy.isScalable() &&
6189 (vecTy.getRank() == 0 || vecTy.getNumElements() == 1))
6192 if (!memRefTy.isLastDimUnitStride())
6193 return op->
emitOpError(
"most minor memref dim must have unit stride");
6197LogicalResult vector::LoadOp::verify() {
6201 if (
failed(verifyLoadStoreMemRefLayout(*
this, resVecTy, memRefTy)))
6208 return emitOpError(
"memref strides must be non-negative");
6210 if (memRefTy.getRank() < resVecTy.getRank())
6212 "destination memref has lower rank than the result vector");
6215 Type memElemTy = memRefTy.getElementType();
6216 if (
auto memVecTy = llvm::dyn_cast<VectorType>(memElemTy)) {
6217 if (memVecTy != resVecTy)
6218 return emitOpError(
"base memref and result vector types should match");
6219 memElemTy = memVecTy.getElementType();
6222 if (resVecTy.getElementType() != memElemTy)
6223 return emitOpError(
"base and result element types should match");
6224 if (llvm::size(
getIndices()) != memRefTy.getRank())
6225 return emitOpError(
"requires ") << memRefTy.getRank() <<
" indices";
6229OpFoldResult LoadOp::fold(FoldAdaptor) {
6232 return OpFoldResult();
6235std::optional<SmallVector<int64_t, 4>> LoadOp::getShapeForUnroll() {
6239FailureOr<std::optional<SmallVector<Value>>>
6240LoadOp::bubbleDownCasts(OpBuilder &builder) {
6249LogicalResult vector::StoreOp::verify() {
6253 if (
failed(verifyLoadStoreMemRefLayout(*
this, valueVecTy, memRefTy)))
6260 return emitOpError(
"memref strides must be non-negative");
6262 if (memRefTy.getRank() < valueVecTy.getRank())
6263 return emitOpError(
"source memref has lower rank than the vector to store");
6266 Type memElemTy = memRefTy.getElementType();
6267 if (
auto memVecTy = llvm::dyn_cast<VectorType>(memElemTy)) {
6268 if (memVecTy != valueVecTy)
6270 "base memref and valueToStore vector types should match");
6271 memElemTy = memVecTy.getElementType();
6274 if (valueVecTy.getElementType() != memElemTy)
6275 return emitOpError(
"base and valueToStore element type should match");
6276 if (llvm::size(
getIndices()) != memRefTy.getRank())
6277 return emitOpError(
"requires ") << memRefTy.getRank() <<
" indices";
6281LogicalResult StoreOp::fold(FoldAdaptor adaptor,
6282 SmallVectorImpl<OpFoldResult> &results) {
6286std::optional<SmallVector<int64_t, 4>> StoreOp::getShapeForUnroll() {
6290FailureOr<std::optional<SmallVector<Value>>>
6291StoreOp::bubbleDownCasts(OpBuilder &builder) {
6300LogicalResult MaskedLoadOp::verify() {
6301 VectorType maskVType = getMaskVectorType();
6302 VectorType passVType = getPassThruVectorType();
6310 return emitOpError(
"memref strides must be non-negative");
6315 if (llvm::size(
getIndices()) != memType.getRank())
6316 return emitOpError(
"requires ") << memType.getRank() <<
" indices";
6317 if (resVType.getShape() != maskVType.getShape())
6318 return emitOpError(
"expected result shape to match mask shape");
6319 if (resVType != passVType)
6320 return emitOpError(
"expected pass_thru of same type as result type");
6325class MaskedLoadFolder final :
public OpRewritePattern<MaskedLoadOp> {
6328 LogicalResult matchAndRewrite(MaskedLoadOp
load,
6329 PatternRewriter &rewriter)
const override {
6341 llvm_unreachable(
"Unexpected 1DMaskFormat on MaskedLoad");
6346void MaskedLoadOp::getCanonicalizationPatterns(RewritePatternSet &results,
6347 MLIRContext *context) {
6348 results.
add<MaskedLoadFolder>(context);
6351OpFoldResult MaskedLoadOp::fold(FoldAdaptor) {
6354 return OpFoldResult();
6357FailureOr<std::optional<SmallVector<Value>>>
6358MaskedLoadOp::bubbleDownCasts(OpBuilder &builder) {
6367LogicalResult MaskedStoreOp::verify() {
6368 VectorType maskVType = getMaskVectorType();
6376 return emitOpError(
"memref strides must be non-negative");
6381 if (llvm::size(
getIndices()) != memType.getRank())
6382 return emitOpError(
"requires ") << memType.getRank() <<
" indices";
6383 if (valueVType.getShape() != maskVType.getShape())
6384 return emitOpError(
"expected valueToStore shape to match mask shape");
6389class MaskedStoreFolder final :
public OpRewritePattern<MaskedStoreOp> {
6392 LogicalResult matchAndRewrite(MaskedStoreOp store,
6393 PatternRewriter &rewriter)
const override {
6397 store, store.getValueToStore(), store.getBase(), store.getIndices());
6405 llvm_unreachable(
"Unexpected 1DMaskFormat on MaskedStore");
6410void MaskedStoreOp::getCanonicalizationPatterns(RewritePatternSet &results,
6411 MLIRContext *context) {
6412 results.
add<MaskedStoreFolder>(context);
6415LogicalResult MaskedStoreOp::fold(FoldAdaptor adaptor,
6416 SmallVectorImpl<OpFoldResult> &results) {
6420FailureOr<std::optional<SmallVector<Value>>>
6421MaskedStoreOp::bubbleDownCasts(OpBuilder &builder) {
6430LogicalResult GatherOp::verify() {
6431 VectorType indVType = getIndexVectorType();
6432 VectorType maskVType = getMaskVectorType();
6434 ShapedType baseType = getBaseType();
6436 if (!llvm::isa<MemRefType, RankedTensorType>(baseType))
6437 return emitOpError(
"requires base to be a memref or ranked tensor type");
6442 if (
auto memRefType = dyn_cast<MemRefType>(baseType))
6444 return emitOpError(
"memref strides must be non-negative");
6449 if (llvm::size(getOffsets()) != baseType.getRank())
6450 return emitOpError(
"requires ") << baseType.getRank() <<
" indices";
6451 if (resVType.getShape() != indVType.getShape())
6452 return emitOpError(
"expected result dim to match indices dim");
6453 if (resVType.getShape() != maskVType.getShape())
6454 return emitOpError(
"expected result dim to match mask dim");
6455 if (resVType != getPassThruVectorType())
6456 return emitOpError(
"expected pass_thru of same type as result type");
6457 if (getAlignmentAttr() && !isa<MemRefType>(baseType)) {
6459 "alignment is only supported for memref bases, not tensor bases");
6468Type GatherOp::getExpectedMaskType() {
6469 auto vecType = this->getIndexVectorType();
6470 return VectorType::get(vecType.getShape(),
6471 IntegerType::get(vecType.getContext(), 1),
6472 vecType.getScalableDims());
6475std::optional<SmallVector<int64_t, 4>> GatherOp::getShapeForUnroll() {
6480static LogicalResult isZeroBasedContiguousSeq(Value indexVec) {
6481 auto vecType = dyn_cast<VectorType>(indexVec.
getType());
6482 if (!vecType || vecType.getRank() != 1 || vecType.isScalable())
6488 DenseIntElementsAttr elements;
6493 llvm::equal(elements, llvm::seq<int64_t>(0, vecType.getNumElements())));
6497class GatherFolder final :
public OpRewritePattern<GatherOp> {
6500 LogicalResult matchAndRewrite(GatherOp gather,
6501 PatternRewriter &rewriter)
const override {
6506 rewriter.
replaceOp(gather, gather.getPassThru());
6511 llvm_unreachable(
"Unexpected 1DMaskFormat on GatherFolder");
6517class FoldContiguousGather final :
public OpRewritePattern<GatherOp> {
6520 LogicalResult matchAndRewrite(GatherOp op,
6521 PatternRewriter &rewriter)
const override {
6522 if (!isa<MemRefType>(op.getBase().getType()))
6525 if (
failed(isZeroBasedContiguousSeq(op.getIndices())))
6529 op.getOffsets(), op.getMask(),
6536void GatherOp::getCanonicalizationPatterns(RewritePatternSet &results,
6537 MLIRContext *context) {
6538 results.
add<GatherFolder, FoldContiguousGather>(context);
6541FailureOr<std::optional<SmallVector<Value>>>
6542GatherOp::bubbleDownCasts(OpBuilder &builder) {
6551LogicalResult ScatterOp::verify() {
6552 VectorType indVType = getIndexVectorType();
6553 VectorType maskVType = getMaskVectorType();
6555 ShapedType baseType = getBaseType();
6557 if (!llvm::isa<MemRefType, RankedTensorType>(baseType))
6558 return emitOpError(
"requires base to be a memref or ranked tensor type");
6563 if (
auto memRefType = dyn_cast<MemRefType>(baseType))
6565 return emitOpError(
"memref strides must be non-negative");
6570 if (llvm::size(getOffsets()) != baseType.getRank())
6571 return emitOpError(
"requires ") << baseType.getRank() <<
" indices";
6572 if (valueVType.getShape() != indVType.getShape())
6573 return emitOpError(
"expected valueToStore dim to match indices dim");
6574 if (valueVType.getShape() != maskVType.getShape())
6575 return emitOpError(
"expected valueToStore dim to match mask dim");
6576 if (getAlignmentAttr() && !isa<MemRefType>(baseType)) {
6578 "alignment is only supported for memref bases, not tensor bases");
6583class ScatterFolder final :
public OpRewritePattern<ScatterOp> {
6586 LogicalResult matchAndRewrite(ScatterOp scatter,
6587 PatternRewriter &rewriter)
const override {
6588 ShapedType baseType = scatter.getBaseType();
6589 bool isMemRef = isa<MemRefType>(baseType);
6590 if (!isMemRef && !isa<RankedTensorType>(baseType))
6603 rewriter.
replaceOp(scatter, scatter.getBase());
6608 llvm_unreachable(
"Unexpected 1DMaskFormat on ScatterFolder");
6614class FoldContiguousScatter final :
public OpRewritePattern<ScatterOp> {
6617 LogicalResult matchAndRewrite(ScatterOp op,
6618 PatternRewriter &rewriter)
const override {
6621 if (!isa<MemRefType>(op.getBase().getType()))
6624 if (
failed(isZeroBasedContiguousSeq(op.getIndices())))
6628 op, op.getBase(), op.getOffsets(), op.getMask(), op.getValueToStore());
6634void ScatterOp::getCanonicalizationPatterns(RewritePatternSet &results,
6635 MLIRContext *context) {
6636 results.
add<ScatterFolder, FoldContiguousScatter>(context);
6639FailureOr<std::optional<SmallVector<Value>>>
6640ScatterOp::bubbleDownCasts(OpBuilder &builder) {
6649LogicalResult ExpandLoadOp::verify() {
6650 VectorType maskVType = getMaskVectorType();
6651 VectorType passVType = getPassThruVectorType();
6658 if (llvm::size(
getIndices()) != memType.getRank())
6659 return emitOpError(
"requires ") << memType.getRank() <<
" indices";
6660 if (resVType.getShape() != maskVType.getShape())
6661 return emitOpError(
"expected result shape to match mask shape");
6662 if (resVType.getScalableDims() != maskVType.getScalableDims())
6664 "expected result scalable dims to match mask scalable dims");
6665 if (resVType != passVType)
6666 return emitOpError(
"expected pass_thru of same type as result type");
6671class ExpandLoadFolder final :
public OpRewritePattern<ExpandLoadOp> {
6674 LogicalResult matchAndRewrite(ExpandLoadOp expand,
6675 PatternRewriter &rewriter)
const override {
6679 expand, expand.getType(), expand.getBase(), expand.getIndices());
6682 rewriter.
replaceOp(expand, expand.getPassThru());
6687 llvm_unreachable(
"Unexpected 1DMaskFormat on ExpandLoadFolder");
6692void ExpandLoadOp::getCanonicalizationPatterns(RewritePatternSet &results,
6693 MLIRContext *context) {
6694 results.
add<ExpandLoadFolder>(context);
6697FailureOr<std::optional<SmallVector<Value>>>
6698ExpandLoadOp::bubbleDownCasts(OpBuilder &builder) {
6707LogicalResult CompressStoreOp::verify() {
6708 VectorType maskVType = getMaskVectorType();
6715 if (llvm::size(
getIndices()) != memType.getRank())
6716 return emitOpError(
"requires ") << memType.getRank() <<
" indices";
6717 if (valueVType.getShape() != maskVType.getShape())
6718 return emitOpError(
"expected valueToStore shape to match mask shape");
6719 if (valueVType.getScalableDims() != maskVType.getScalableDims())
6721 "expected valueToStore scalable dims to match mask scalable dims");
6726class CompressStoreFolder final :
public OpRewritePattern<CompressStoreOp> {
6729 LogicalResult matchAndRewrite(CompressStoreOp compress,
6730 PatternRewriter &rewriter)
const override {
6734 compress, compress.getValueToStore(), compress.getBase(),
6735 compress.getIndices());
6743 llvm_unreachable(
"Unexpected 1DMaskFormat on CompressStoreFolder");
6748void CompressStoreOp::getCanonicalizationPatterns(RewritePatternSet &results,
6749 MLIRContext *context) {
6750 results.
add<CompressStoreFolder>(context);
6753FailureOr<std::optional<SmallVector<Value>>>
6754CompressStoreOp::bubbleDownCasts(OpBuilder &builder) {
6763void ShapeCastOp::inferResultRanges(ArrayRef<ConstantIntRanges> argRanges,
6765 setResultRanges(getResult(), argRanges.front());
6768std::optional<SmallVector<int64_t, 4>> ShapeCastOp::getShapeForUnroll() {
6769 return llvm::to_vector<4>(getResultVectorType().
getShape());
6772LogicalResult ShapeCastOp::verify() {
6774 VectorType sourceType = getSourceVectorType();
6775 VectorType resultType = getResultVectorType();
6783 int64_t sourceNElms = sourceType.getNumElements();
6784 int64_t resultNElms = resultType.getNumElements();
6785 if (sourceNElms != resultNElms) {
6786 return emitOpError() <<
"has different number of elements at source ("
6787 << sourceNElms <<
") and result (" << resultNElms
6792 int64_t sourceNScalableDims = sourceType.getNumScalableDims();
6793 int64_t resultNScalableDims = resultType.getNumScalableDims();
6794 if (sourceNScalableDims != resultNScalableDims)
6795 return emitOpError() <<
"has different number of scalable dims at source ("
6796 << sourceNScalableDims <<
") and result ("
6797 << resultNScalableDims <<
")";
6808bool ShapeCastOp::isBroadcastLike() {
6809 auto srcType = getSourceVectorType();
6810 auto resType = getResultVectorType();
6813 std::pair<VectorDim, VectorDim> mismatchingDims;
6815 BroadcastableToResult::Success)
6822 size_t rankDiff = resType.getRank() - srcType.getRank();
6827 if (!llvm::all_of(resType.getShape().take_front(rankDiff),
6828 [](int64_t dim) { return dim == 1; }))
6832 return resType.getShape().take_back(srcType.getRank()) == srcType.getShape();
6839static bool isOrderPreserving(TransposeOp transpose) {
6840 ArrayRef<int64_t> permutation = transpose.getPermutation();
6841 VectorType sourceType = transpose.getSourceVectorType();
6842 ArrayRef<int64_t> inShape = sourceType.getShape();
6843 ArrayRef<bool> inDimIsScalable = sourceType.getScalableDims();
6844 auto isNonScalableUnitDim = [&](int64_t dim) {
6845 return inShape[dim] == 1 && !inDimIsScalable[dim];
6847 int64_t current = 0;
6848 for (
auto p : permutation) {
6849 if (!isNonScalableUnitDim(p)) {
6859OpFoldResult ShapeCastOp::fold(FoldAdaptor adaptor) {
6861 VectorType resultType =
getType();
6864 if (getSource().
getType() == resultType)
6868 if (
auto precedingShapeCast = getSource().getDefiningOp<ShapeCastOp>()) {
6869 setOperand(precedingShapeCast.getSource());
6874 if (
auto transpose = getSource().getDefiningOp<TransposeOp>()) {
6875 if (isOrderPreserving(transpose)) {
6876 setOperand(transpose.getVector());
6884 if (
auto bcastOp = getSource().getDefiningOp<BroadcastOp>()) {
6885 if (bcastOp.getSourceType() == resultType)
6886 return bcastOp.getSource();
6890 if (
auto denseAttr =
6891 dyn_cast_if_present<DenseElementsAttr>(adaptor.getSource()))
6892 return denseAttr.reshape(
getType());
6908static VectorType trimTrailingOneDims(VectorType oldType) {
6909 ArrayRef<int64_t> oldShape = oldType.getShape();
6910 ArrayRef<int64_t> newShape = oldShape;
6912 ArrayRef<bool> oldScalableDims = oldType.getScalableDims();
6913 ArrayRef<bool> newScalableDims = oldScalableDims;
6915 while (!newShape.empty() && newShape.back() == 1 && !newScalableDims.back()) {
6916 newShape = newShape.drop_back(1);
6917 newScalableDims = newScalableDims.drop_back(1);
6922 if (newShape.empty()) {
6923 newShape = oldShape.take_back();
6924 newScalableDims = oldScalableDims.take_back();
6927 return VectorType::get(newShape, oldType.getElementType(), newScalableDims);
6942class ShapeCastCreateMaskFolderTrailingOneDim final
6943 :
public OpRewritePattern<ShapeCastOp> {
6947 LogicalResult matchAndRewrite(ShapeCastOp shapeOp,
6948 PatternRewriter &rewriter)
const override {
6949 Value shapeOpSrc = shapeOp->getOperand(0);
6950 auto createMaskOp = shapeOpSrc.
getDefiningOp<vector::CreateMaskOp>();
6951 auto constantMaskOp = shapeOpSrc.
getDefiningOp<vector::ConstantMaskOp>();
6952 if (!createMaskOp && !constantMaskOp)
6955 VectorType shapeOpResTy = shapeOp.getResultVectorType();
6956 VectorType shapeOpSrcTy = shapeOp.getSourceVectorType();
6958 VectorType newVecType = trimTrailingOneDims(shapeOpSrcTy);
6959 if (newVecType != shapeOpResTy)
6962 auto numDimsToDrop =
6963 shapeOpSrcTy.getShape().size() - shapeOpResTy.getShape().size();
6970 auto maskOperands = createMaskOp.getOperands();
6971 auto numMaskOperands = maskOperands.size();
6974 for (
size_t i = numMaskOperands - 1; i >= numMaskOperands - numDimsToDrop;
6976 auto constant = maskOperands[i].getDefiningOp<arith::ConstantIndexOp>();
6977 if (!constant || (constant.value() != 1))
6980 SmallVector<Value> newMaskOperands =
6981 maskOperands.drop_back(numDimsToDrop);
6988 if (constantMaskOp) {
6989 auto maskDimSizes = constantMaskOp.getMaskDimSizes();
6990 auto numMaskOperands = maskDimSizes.size();
6993 for (
size_t i = numMaskOperands - 1; i >= numMaskOperands - numDimsToDrop;
6995 if (maskDimSizes[i] != 1)
6999 auto newMaskOperands = maskDimSizes.drop_back(numDimsToDrop);
7012int64_t getBroadcastStretchingFactor(ArrayRef<int64_t> srcShape,
7013 ArrayRef<int64_t> dstShape) {
7014 int stretchingFactor = 1;
7015 int numLeadingDims = dstShape.size() - srcShape.size();
7016 for (
int i = 0, e = srcShape.size(); i < e; i++) {
7017 int64_t dstDim = dstShape[numLeadingDims + i];
7018 if (srcShape[i] == 1 && dstDim != 1) {
7019 stretchingFactor *= dstDim;
7022 return stretchingFactor;
7026class ShapeCastBroadcastFolder final :
public OpRewritePattern<ShapeCastOp> {
7030 LogicalResult matchAndRewrite(ShapeCastOp shapeCastOp,
7031 PatternRewriter &rewriter)
const override {
7033 shapeCastOp.getSource().getDefiningOp<vector::BroadcastOp>();
7037 auto srcVectorType = dyn_cast<VectorType>(broadcastOp.getSourceType());
7038 bool srcIsScalar = !srcVectorType;
7046 VectorType dstVectorType = shapeCastOp.getResultVectorType();
7047 ArrayRef<int64_t> dstShape = dstVectorType.getShape();
7048 ArrayRef<int64_t> srcShape =
7049 srcIsScalar ? ArrayRef<int64_t>{} : srcVectorType.getShape();
7050 ArrayRef<int64_t> broadcastShape =
7051 broadcastOp.getResultVectorType().getShape();
7055 BroadcastableToResult::Success) {
7063 if (srcVectorType.getNumElements() != 1) {
7064 if (getBroadcastStretchingFactor(srcShape, dstShape) !=
7065 getBroadcastStretchingFactor(srcShape, broadcastShape)) {
7072 broadcastOp.getSource());
7091class FoldShapeCastOfFromElements final :
public OpRewritePattern<ShapeCastOp> {
7095 LogicalResult matchAndRewrite(ShapeCastOp shapeCastOp,
7096 PatternRewriter &rewriter)
const override {
7097 auto fromElements = shapeCastOp.getSource().getDefiningOp<FromElementsOp>();
7102 shapeCastOp, shapeCastOp.getResultVectorType(),
7103 fromElements.getElements());
7110void ShapeCastOp::getCanonicalizationPatterns(RewritePatternSet &results,
7111 MLIRContext *context) {
7112 results.
add<ShapeCastCreateMaskFolderTrailingOneDim, ShapeCastBroadcastFolder,
7113 FoldShapeCastOfFromElements>(context);
7120LogicalResult BitCastOp::verify() {
7121 auto sourceVectorType = getSourceVectorType();
7122 auto resultVectorType = getResultVectorType();
7124 for (int64_t i = 0, e = sourceVectorType.getRank() - 1; i < e; i++) {
7125 if (sourceVectorType.getDimSize(i) != resultVectorType.getDimSize(i))
7126 return emitOpError(
"dimension size mismatch at: ") << i;
7129 DataLayout dataLayout = DataLayout::closest(*
this);
7130 auto sourceElementBits =
7132 auto resultElementBits =
7135 if (sourceVectorType.getRank() == 0) {
7136 if (sourceElementBits != resultElementBits)
7137 return emitOpError(
"source/result bitwidth of the 0-D vector element "
7138 "types must be equal");
7139 }
else if (sourceElementBits * sourceVectorType.getShape().back() !=
7140 resultElementBits * resultVectorType.getShape().back()) {
7142 "source/result bitwidth of the minor 1-D vectors must be equal");
7148OpFoldResult BitCastOp::fold(FoldAdaptor adaptor) {
7154 if (
auto otherOp = getSource().getDefiningOp<BitCastOp>()) {
7155 if (getResult().
getType() == otherOp.getSource().getType())
7156 return otherOp.getSource();
7158 setOperand(otherOp.getSource());
7162 Attribute sourceConstant = adaptor.getSource();
7163 if (!sourceConstant)
7166 Type srcElemType = getSourceVectorType().getElementType();
7167 Type dstElemType = getResultVectorType().getElementType();
7169 if (
auto floatPack = llvm::dyn_cast<DenseFPElementsAttr>(sourceConstant)) {
7170 if (floatPack.isSplat()) {
7171 auto splat = floatPack.getSplatValue<FloatAttr>();
7174 if (srcElemType.
isF16() && dstElemType.
isF32()) {
7175 uint32_t bits =
static_cast<uint32_t
>(
7176 splat.getValue().bitcastToAPInt().getZExtValue());
7178 bits = (bits << 16) | (bits & 0xffff);
7179 APInt intBits(32, bits);
7180 APFloat floatBits(llvm::APFloat::IEEEsingle(), intBits);
7186 if (
auto intPack = llvm::dyn_cast<DenseIntElementsAttr>(sourceConstant)) {
7187 if (intPack.isSplat()) {
7188 auto splat = intPack.getSplatValue<IntegerAttr>();
7190 if (llvm::isa<IntegerType>(dstElemType) && srcElemType.
isIntOrFloat()) {
7195 if (dstBitWidth > srcBitWidth && dstBitWidth % srcBitWidth == 0) {
7196 APInt intBits = splat.getValue().zext(dstBitWidth);
7199 for (uint64_t i = 0; i < dstBitWidth / srcBitWidth - 1; i++)
7200 intBits = (intBits << srcBitWidth) | intBits;
7210std::optional<SmallVector<int64_t, 4>> BitCastOp::getShapeForUnroll() {
7211 return llvm::to_vector<4>(getResultVectorType().
getShape());
7218static SmallVector<int64_t, 8> extractShape(MemRefType memRefType) {
7219 auto vectorType = llvm::dyn_cast<VectorType>(memRefType.getElementType());
7220 SmallVector<int64_t, 8> res(memRefType.getShape());
7222 res.append(vectorType.getShape().begin(), vectorType.getShape().end());
7228void TypeCastOp::build(OpBuilder &builder, OperationState &
result,
7230 result.addOperands(source);
7231 MemRefType memRefType = llvm::cast<MemRefType>(source.
getType());
7232 VectorType vectorType =
7233 VectorType::get(extractShape(memRefType),
7235 result.addTypes(MemRefType::get({}, vectorType, MemRefLayoutAttrInterface(),
7236 memRefType.getMemorySpace()));
7239LogicalResult TypeCastOp::verify() {
7240 MemRefType canonicalType =
getMemRefType().canonicalizeStridedLayout();
7241 if (!canonicalType.getLayout().isIdentity())
7242 return emitOpError(
"expects operand to be a memref with identity layout");
7243 if (!getResultMemRefType().getLayout().isIdentity())
7244 return emitOpError(
"expects result to be a memref with identity layout");
7245 if (getResultMemRefType().getMemorySpace() !=
7247 return emitOpError(
"expects result in same memory space");
7250 auto resultType = getResultMemRefType();
7254 "expects result and operand with same underlying scalar type: ")
7256 if (extractShape(sourceType) != extractShape(resultType))
7258 "expects concatenated result and operand shapes to be equal: ")
7267void vector::TransposeOp::build(OpBuilder &builder, OperationState &
result,
7268 Value vector, ArrayRef<int64_t> permutation) {
7269 VectorType vt = llvm::cast<VectorType>(vector.
getType());
7270 SmallVector<int64_t, 4> transposedShape(vt.getRank());
7271 SmallVector<bool, 4> transposedScalableDims(vt.getRank());
7272 for (
unsigned i = 0; i < permutation.size(); ++i) {
7273 transposedShape[i] = vt.getShape()[permutation[i]];
7274 transposedScalableDims[i] = vt.getScalableDims()[permutation[i]];
7277 result.addOperands(vector);
7278 result.addTypes(VectorType::get(transposedShape, vt.getElementType(),
7279 transposedScalableDims));
7280 result.addAttribute(TransposeOp::getPermutationAttrName(
result.name),
7284OpFoldResult vector::TransposeOp::fold(FoldAdaptor adaptor) {
7287 llvm::dyn_cast_if_present<SplatElementsAttr>(adaptor.getVector()))
7288 return splat.reshape(getResultVectorType());
7305 if (getSourceVectorType() == getResultVectorType() &&
7306 isOrderPreserving(*
this))
7312LogicalResult vector::TransposeOp::verify() {
7313 VectorType vectorType = getSourceVectorType();
7314 VectorType resultType = getResultVectorType();
7315 int64_t rank = resultType.getRank();
7316 if (vectorType.getRank() != rank)
7317 return emitOpError(
"vector result rank mismatch: ") << rank;
7319 ArrayRef<int64_t> perm = getPermutation();
7320 int64_t size = perm.size();
7322 return emitOpError(
"transposition length mismatch: ") << size;
7323 SmallVector<bool, 8> seen(rank,
false);
7324 for (
const auto &ta : llvm::enumerate(perm)) {
7325 if (ta.value() < 0 || ta.value() >= rank)
7326 return emitOpError(
"transposition index out of range: ") << ta.value();
7327 if (seen[ta.value()])
7328 return emitOpError(
"duplicate position index: ") << ta.value();
7329 seen[ta.value()] =
true;
7330 if (resultType.getDimSize(ta.index()) != vectorType.getDimSize(ta.value()))
7331 return emitOpError(
"dimension size mismatch at: ") << ta.value();
7336std::optional<SmallVector<int64_t, 4>> TransposeOp::getShapeForUnroll() {
7337 return llvm::to_vector<4>(getResultVectorType().
getShape());
7340void TransposeOp::inferResultRanges(ArrayRef<ConstantIntRanges> argRanges,
7342 setResultRanges(getResult(), argRanges.front());
7348class TransposeFolder final :
public OpRewritePattern<vector::TransposeOp> {
7352 LogicalResult matchAndRewrite(vector::TransposeOp transposeOp,
7353 PatternRewriter &rewriter)
const override {
7355 auto composePermutations = [](ArrayRef<int64_t> permutation1,
7356 ArrayRef<int64_t> permutation2) {
7357 SmallVector<int64_t, 4>
result;
7358 for (
auto index : permutation2)
7359 result.push_back(permutation1[index]);
7364 vector::TransposeOp parentTransposeOp =
7365 transposeOp.getVector().getDefiningOp<vector::TransposeOp>();
7366 if (!parentTransposeOp)
7369 SmallVector<int64_t, 4> permutation = composePermutations(
7370 parentTransposeOp.getPermutation(), transposeOp.getPermutation());
7373 transposeOp, transposeOp.getResult().
getType(),
7374 parentTransposeOp.getVector(), permutation);
7380class FoldTransposeSplat final :
public OpRewritePattern<TransposeOp> {
7384 LogicalResult matchAndRewrite(TransposeOp transposeOp,
7385 PatternRewriter &rewriter)
const override {
7386 Value splat = getScalarSplatSource(transposeOp.getVector());
7391 transposeOp, transposeOp.getResultVectorType(), splat);
7397class FoldTransposeCreateMask final :
public OpRewritePattern<TransposeOp> {
7401 LogicalResult matchAndRewrite(TransposeOp transpOp,
7402 PatternRewriter &rewriter)
const override {
7403 Value transposeSrc = transpOp.getVector();
7404 auto createMaskOp = transposeSrc.
getDefiningOp<vector::CreateMaskOp>();
7405 auto constantMaskOp = transposeSrc.
getDefiningOp<vector::ConstantMaskOp>();
7406 if (!createMaskOp && !constantMaskOp)
7411 ArrayRef<int64_t> permutation = transpOp.getPermutation();
7414 auto maskOperands = createMaskOp.getOperands();
7415 SmallVector<Value> newOperands(maskOperands.begin(), maskOperands.end());
7419 transpOp, transpOp.getResultVectorType(), newOperands);
7424 auto maskDimSizes = constantMaskOp.getMaskDimSizes();
7428 transpOp, transpOp.getResultVectorType(), newMaskDimSizes);
7434class FoldTransposeShapeCast final :
public OpRewritePattern<TransposeOp> {
7438 LogicalResult matchAndRewrite(TransposeOp transposeOp,
7439 PatternRewriter &rewriter)
const override {
7441 transposeOp.getVector().getDefiningOp<vector::ShapeCastOp>();
7444 if (!isOrderPreserving(transposeOp))
7447 VectorType resultType = transposeOp.getType();
7454 shapeCastOp.getSource());
7473class FoldTransposeFromElements final :
public OpRewritePattern<TransposeOp> {
7476 LogicalResult matchAndRewrite(vector::TransposeOp transposeOp,
7477 PatternRewriter &rewriter)
const override {
7478 auto fromElementsOp =
7479 transposeOp.getVector().getDefiningOp<vector::FromElementsOp>();
7480 if (!fromElementsOp)
7483 VectorType srcTy = fromElementsOp.getDest().getType();
7484 VectorType dstTy = transposeOp.getType();
7486 ArrayRef<int64_t> permutation = transposeOp.getPermutation();
7487 int64_t rank = srcTy.getRank();
7490 SmallVector<int64_t> inversePerm(rank, 0);
7491 for (int64_t i = 0; i < rank; ++i)
7492 inversePerm[permutation[i]] = i;
7494 ArrayRef<int64_t> srcShape = srcTy.getShape();
7495 ArrayRef<int64_t> dstShape = dstTy.getShape();
7496 SmallVector<int64_t> srcIdx(rank, 0);
7497 SmallVector<int64_t> dstIdx(rank, 0);
7501 auto elementsOld = fromElementsOp.getElements();
7502 SmallVector<Value> elementsNew;
7503 int64_t dstNumElements = dstTy.getNumElements();
7504 elementsNew.reserve(dstNumElements);
7508 for (int64_t linearIdx = 0; linearIdx < dstNumElements; ++linearIdx) {
7512 for (int64_t j = 0; j < rank; ++j)
7513 srcIdx[j] = dstIdx[inversePerm[j]];
7515 int64_t srcLin =
linearize(srcIdx, srcStrides);
7517 elementsNew.push_back(elementsOld[srcLin]);
7551class FoldTransposeBroadcast :
public OpRewritePattern<vector::TransposeOp> {
7554 FoldTransposeBroadcast(MLIRContext *context, PatternBenefit benefit = 1)
7555 : OpRewritePattern<vector::TransposeOp>(context, benefit) {}
7557 LogicalResult matchAndRewrite(vector::TransposeOp transpose,
7558 PatternRewriter &rewriter)
const override {
7564 "not preceded by a broadcast");
7567 auto inputType = dyn_cast<VectorType>(
broadcast.getSourceType());
7568 VectorType outputType = transpose.getResultVectorType();
7571 bool inputIsScalar = !inputType;
7572 if (inputIsScalar) {
7578 ArrayRef<int64_t> permutation = transpose.getPermutation();
7579 ArrayRef<int64_t> inputShape = inputType.getShape();
7580 int64_t inputRank = inputType.getRank();
7581 int64_t outputRank = transpose.getType().getRank();
7582 int64_t deltaRank = outputRank - inputRank;
7585 for (
int inputIndex = 0; inputIndex < inputRank; ++inputIndex) {
7586 bool notOne = inputShape[inputIndex] != 1;
7587 bool prevNotOne = (inputIndex != 0 && inputShape[inputIndex - 1] != 1);
7588 bool groupEndFound = notOne || prevNotOne;
7589 if (groupEndFound) {
7590 int high = inputIndex + deltaRank;
7594 for (
int i = low; i < high; ++i) {
7595 if (permutation[i] < low || permutation[i] >= high) {
7597 transpose,
"permutation not local to group");
7611 vector::BroadcastableToResult::Success &&
7612 "not broadcastable directly to transpose output");
7623void vector::TransposeOp::getCanonicalizationPatterns(
7624 RewritePatternSet &results, MLIRContext *context) {
7625 results.
add<FoldTransposeCreateMask, FoldTransposeShapeCast, TransposeFolder,
7626 FoldTransposeSplat, FoldTransposeFromElements,
7627 FoldTransposeBroadcast>(context);
7634void ConstantMaskOp::build(OpBuilder &builder, OperationState &
result,
7636 assert(kind == ConstantMaskKind::AllTrue ||
7637 kind == ConstantMaskKind::AllFalse);
7638 build(builder,
result, type,
7639 kind == ConstantMaskKind::AllTrue
7641 : SmallVector<int64_t>(type.getRank(), 0));
7644LogicalResult ConstantMaskOp::verify() {
7645 auto resultType = llvm::cast<VectorType>(getResult().
getType());
7647 if (resultType.getRank() == 0) {
7648 if (getMaskDimSizes().size() != 1)
7649 return emitError(
"array attr must have length 1 for 0-D vectors");
7650 auto dim = getMaskDimSizes()[0];
7651 if (dim != 0 && dim != 1)
7652 return emitError(
"mask dim size must be either 0 or 1 for 0-D vectors");
7657 if (
static_cast<int64_t
>(getMaskDimSizes().size()) != resultType.getRank())
7659 "must specify array attr of size equal vector result rank");
7662 auto resultShape = resultType.getShape();
7663 auto resultScalableDims = resultType.getScalableDims();
7664 ArrayRef<int64_t> maskDimSizes = getMaskDimSizes();
7665 for (
const auto [index, maskDimSize] : llvm::enumerate(maskDimSizes)) {
7666 if (maskDimSize < 0 || maskDimSize > resultShape[index])
7668 "array attr of size out of bounds of vector result dimension size");
7669 if (resultScalableDims[index] && maskDimSize != 0 &&
7670 maskDimSize != resultShape[index])
7672 "only supports 'none set' or 'all set' scalable dimensions");
7676 bool anyZeros = llvm::is_contained(maskDimSizes, 0);
7677 bool allZeros = llvm::all_of(maskDimSizes, [](int64_t s) {
return s == 0; });
7678 if (anyZeros && !allZeros)
7679 return emitOpError(
"expected all mask dim sizes to be zeros, "
7680 "as a result of conjunction with zero mask dim");
7684bool ConstantMaskOp::isAllOnesMask() {
7687 if (resultType.getRank() == 0) {
7688 assert(getMaskDimSizes().size() == 1 &&
"invalid sizes for zero rank mask");
7689 return getMaskDimSizes()[0] == 1;
7691 for (
const auto [resultSize, maskDimSize] :
7692 llvm::zip_equal(resultType.getShape(), getMaskDimSizes())) {
7693 if (maskDimSize < resultSize)
7699OpFoldResult ConstantMaskOp::fold(FoldAdaptor adaptor) {
7700 ArrayRef<int64_t> bounds = getMaskDimSizes();
7703 auto createBoolSplat = [&](
bool x) {
7709 if (vectorSizes.empty()) {
7710 assert(bounds.size() == 1 &&
"invalid sizes for zero rank mask");
7711 return createBoolSplat(bounds[0] == 1);
7714 if (bounds == vectorSizes)
7715 return createBoolSplat(
true);
7716 if (llvm::all_of(bounds, [](int64_t x) {
return x == 0; }))
7717 return createBoolSplat(
false);
7718 return OpFoldResult();
7725void CreateMaskOp::build(OpBuilder &builder, OperationState &
result,
7727 ArrayRef<OpFoldResult> mixedOperands) {
7728 SmallVector<Value> operands =
7730 build(builder,
result, type, operands);
7733LogicalResult CreateMaskOp::verify() {
7734 auto vectorType = llvm::cast<VectorType>(getResult().
getType());
7736 if (vectorType.getRank() == 0) {
7737 if (getNumOperands() != 1)
7739 "must specify exactly one operand for 0-D create_mask");
7740 }
else if (getNumOperands() !=
7741 llvm::cast<VectorType>(getResult().
getType()).getRank()) {
7743 "must specify an operand for each result vector dimension");
7773class CreateMaskFolder final :
public OpRewritePattern<CreateMaskOp> {
7777 LogicalResult matchAndRewrite(CreateMaskOp createMaskOp,
7778 PatternRewriter &rewriter)
const override {
7779 VectorType maskType = createMaskOp.getVectorType();
7780 ArrayRef<int64_t> maskTypeDimSizes = maskType.getShape();
7781 ArrayRef<bool> maskTypeDimScalableFlags = maskType.getScalableDims();
7784 constexpr std::array<int64_t, 1> rankZeroShape{1};
7785 constexpr std::array<bool, 1> rankZeroScalableDims{
false};
7786 if (maskType.getRank() == 0) {
7787 maskTypeDimSizes = rankZeroShape;
7788 maskTypeDimScalableFlags = rankZeroScalableDims;
7793 SmallVector<int64_t, 4> constantDims;
7794 for (
auto [i, dimSize] : llvm::enumerate(createMaskOp.getOperands())) {
7799 if (maskTypeDimScalableFlags[i] && intSize >= 0)
7801 constantDims.push_back(*intSize);
7805 if (vscaleMultiplier < maskTypeDimSizes[i])
7807 constantDims.push_back(*vscaleMultiplier);
7814 for (
auto [value, maskDimSize] : llvm::zip(constantDims, maskTypeDimSizes))
7815 value = std::clamp<int64_t>(value, 0, maskDimSize);
7818 if (llvm::is_contained(constantDims, 0))
7819 constantDims.assign(constantDims.size(), 0);
7830void CreateMaskOp::getCanonicalizationPatterns(RewritePatternSet &results,
7831 MLIRContext *context) {
7832 results.
add<CreateMaskFolder>(context);
7840 OpBuilder &builder, OperationState &
result, Value mask,
7841 Operation *maskableOp,
7842 function_ref<
void(OpBuilder &, Operation *)> maskRegionBuilder) {
7843 assert(maskRegionBuilder &&
7844 "builder callback for 'maskRegion' must be present");
7846 result.addOperands(mask);
7847 OpBuilder::InsertionGuard guard(builder);
7848 Region *maskRegion =
result.addRegion();
7850 maskRegionBuilder(builder, maskableOp);
7855 Value mask, Operation *maskableOp,
7856 function_ref<
void(OpBuilder &, Operation *)> maskRegionBuilder) {
7857 build(builder,
result, resultTypes, mask, Value(), maskableOp,
7863 Value mask, Value passthru, Operation *maskableOp,
7864 function_ref<
void(OpBuilder &, Operation *)> maskRegionBuilder) {
7865 build(builder,
result, mask, maskableOp, maskRegionBuilder);
7867 result.addOperands(passthru);
7868 result.addTypes(resultTypes);
7871ParseResult MaskOp::parse(OpAsmParser &parser, OperationState &
result) {
7873 result.regions.reserve(1);
7874 Region &maskRegion = *
result.addRegion();
7879 OpAsmParser::UnresolvedOperand mask;
7884 OpAsmParser::UnresolvedOperand passthru;
7886 if (parsePassthru.succeeded() && parser.
parseOperand(passthru))
7893 MaskOp::ensureTerminator(maskRegion, builder,
result.location);
7904 SmallVector<Type> resultTypes;
7907 result.types.append(resultTypes);
7913 if (parsePassthru.succeeded()) {
7914 if (resultTypes.empty())
7917 "expects a result if passthru operand is provided");
7926void mlir::vector::MaskOp::print(OpAsmPrinter &p) {
7927 p <<
" " << getMask();
7929 p <<
", " << getPassthru();
7933 Block *singleBlock = &getMaskRegion().getBlocks().front();
7940 p <<
" : " << getMask().getType();
7941 if (getNumResults() > 0)
7942 p <<
" -> " << getResultTypes();
7945void MaskOp::ensureTerminator(Region ®ion, Builder &builder, Location loc) {
7948 OpTrait::SingleBlockImplicitTerminator<vector::YieldOp>::Impl<
7949 MaskOp>::ensureTerminator(region, builder, loc);
7955 if (isa<vector::YieldOp>(block.
back()))
7963 OpTrait::SingleBlockImplicitTerminator<vector::YieldOp>::Impl<
7964 MaskOp>::ensureTerminator(region, builder, loc);
7970 Operation *maskedOp = &block.
front();
7971 opBuilder.setInsertionPointToEnd(&block);
7972 vector::YieldOp::create(opBuilder, loc, maskedOp->
getResults());
7975LogicalResult MaskOp::verify() {
7977 Block &block = getMaskRegion().getBlocks().
front();
7979 return emitOpError(
"expects a terminator within the mask region");
7982 if (numMaskRegionOps > 2)
7983 return emitOpError(
"expects only one operation to mask");
7986 auto terminator = dyn_cast<vector::YieldOp>(block.
back());
7988 return emitOpError(
"expects a terminator within the mask region");
7990 if (terminator->getNumOperands() != getNumResults())
7992 "expects number of results to match mask region yielded values");
7995 if (numMaskRegionOps == 1)
7998 auto maskableOp = dyn_cast<MaskableOpInterface>(block.
front());
8000 return emitOpError(
"expects a MaskableOpInterface within the mask region");
8004 return emitOpError(
"expects number of results to match maskable operation "
8005 "number of results");
8007 if (!llvm::equal(maskableOp->
getResults(), terminator.getOperands()))
8008 return emitOpError(
"expects all the results from the MaskableOpInterface "
8009 "to match all the values returned by the terminator");
8011 if (!llvm::equal(maskableOp->
getResultTypes(), getResultTypes()))
8013 "expects result type to match maskable operation result type");
8016 [](Type t) { return llvm::isa<VectorType>(t); }) > 1)
8017 return emitOpError(
"multiple vector results not supported");
8020 Type expectedMaskType = maskableOp.getExpectedMaskType();
8021 if (getMask().
getType() != expectedMaskType)
8023 << expectedMaskType <<
" mask for the maskable operation";
8026 Value passthru = getPassthru();
8028 if (!maskableOp.supportsPassthru())
8030 "doesn't expect a passthru argument for this maskable operation");
8033 return emitOpError(
"expects result when passthru argument is provided");
8036 return emitOpError(
"expects passthru type to match result type");
8056static LogicalResult foldEmptyMaskOp(MaskOp maskOp, MaskOp::FoldAdaptor adaptor,
8057 SmallVectorImpl<OpFoldResult> &results) {
8058 if (!maskOp.isEmpty() || maskOp.hasPassthru())
8061 Block *block = maskOp.getMaskBlock();
8062 auto terminator = cast<vector::YieldOp>(block->
front());
8063 if (terminator.getNumOperands() == 0)
8067 llvm::append_range(results, terminator.getOperands());
8071LogicalResult MaskOp::fold(FoldAdaptor adaptor,
8072 SmallVectorImpl<OpFoldResult> &results) {
8073 if (succeeded(foldEmptyMaskOp(*
this, adaptor, results)))
8083 Operation *maskableOp = getMaskableOp();
8089 llvm::append_range(results, maskableOp->
getResults());
8105class CanonializeEmptyMaskOp :
public OpRewritePattern<MaskOp> {
8108 LogicalResult matchAndRewrite(MaskOp maskOp,
8109 PatternRewriter &rewriter)
const override {
8110 if (!maskOp.isEmpty())
8113 if (!maskOp.hasPassthru())
8120 VectorType maskType = maskOp.getMask().getType();
8121 for (Type resultType : maskOp.getResultTypes()) {
8122 auto vecResultType = dyn_cast<VectorType>(resultType);
8123 if (!vecResultType || vecResultType.getShape() != maskType.getShape())
8127 Block *block = maskOp.getMaskBlock();
8128 auto terminator = cast<vector::YieldOp>(block->
front());
8129 assert(terminator.getNumOperands() == 1 &&
8130 "expected one result when passthru is provided");
8133 maskOp, maskOp.getResultTypes(), maskOp.getMask(),
8134 terminator.getOperand(0), maskOp.getPassthru());
8140void MaskOp::getCanonicalizationPatterns(RewritePatternSet &results,
8141 MLIRContext *context) {
8142 results.
add<CanonializeEmptyMaskOp>(context);
8148Operation *MaskOp::getMaskableOp() {
8149 Block *block = getMaskBlock();
8153 return &block->
front();
8157bool MaskOp::hasPassthru() {
return getPassthru() != Value(); }
8163LogicalResult ScanOp::verify() {
8164 VectorType srcType = getSourceType();
8165 VectorType initialType = getInitialValueType();
8167 int64_t srcRank = srcType.getRank();
8168 int64_t reductionDim = getReductionDim();
8169 if (reductionDim >= srcRank)
8171 << reductionDim <<
" has to be less than " << srcRank;
8174 int64_t initialValueRank = initialType.getRank();
8175 if (initialValueRank != srcRank - 1)
8177 << initialValueRank <<
" has to be equal to " << srcRank - 1;
8180 ArrayRef<int64_t> srcShape = srcType.getShape();
8181 ArrayRef<int64_t> initialValueShapes = initialType.getShape();
8182 SmallVector<int64_t> expectedShape;
8183 for (
int i = 0; i < srcRank; i++) {
8184 if (i != reductionDim)
8185 expectedShape.push_back(srcShape[i]);
8187 if (!llvm::equal(initialValueShapes, expectedShape)) {
8188 return emitOpError(
"incompatible input/initial value shapes");
8192 Type eltType = getDestType().getElementType();
8195 << eltType <<
" for kind '" << stringifyCombiningKind(getKind())
8202 RewritePatternSet &patterns, PatternBenefit benefit) {
8204 .
add<CreateMaskFolder, MaskedLoadFolder, MaskedStoreFolder, GatherFolder,
8205 ScatterFolder, ExpandLoadFolder, CompressStoreFolder,
8206 StridedSliceConstantMaskFolder, TransposeFolder>(
8211 CombiningKind kind, Value v1, Value acc,
8212 arith::FastMathFlagsAttr fastmath,
8219 case CombiningKind::ADD:
8221 result =
b.createOrFold<arith::AddIOp>(loc, v1, acc);
8222 else if (llvm::isa<FloatType>(t1) && llvm::isa<FloatType>(tAcc))
8223 result =
b.createOrFold<arith::AddFOp>(loc, v1, acc, fastmath);
8225 llvm_unreachable(
"invalid value types for ADD reduction");
8227 case CombiningKind::AND:
8229 result =
b.createOrFold<arith::AndIOp>(loc, v1, acc);
8231 case CombiningKind::MAXNUMF:
8232 assert(llvm::isa<FloatType>(t1) && llvm::isa<FloatType>(tAcc) &&
8233 "expected float values");
8234 result =
b.createOrFold<arith::MaxNumFOp>(loc, v1, acc, fastmath);
8236 case CombiningKind::MAXIMUMF:
8237 assert(llvm::isa<FloatType>(t1) && llvm::isa<FloatType>(tAcc) &&
8238 "expected float values");
8239 result =
b.createOrFold<arith::MaximumFOp>(loc, v1, acc, fastmath);
8241 case CombiningKind::MINNUMF:
8242 assert(llvm::isa<FloatType>(t1) && llvm::isa<FloatType>(tAcc) &&
8243 "expected float values");
8244 result =
b.createOrFold<arith::MinNumFOp>(loc, v1, acc, fastmath);
8246 case CombiningKind::MINIMUMF:
8247 assert(llvm::isa<FloatType>(t1) && llvm::isa<FloatType>(tAcc) &&
8248 "expected float values");
8249 result =
b.createOrFold<arith::MinimumFOp>(loc, v1, acc, fastmath);
8251 case CombiningKind::MAXSI:
8253 result =
b.createOrFold<arith::MaxSIOp>(loc, v1, acc);
8255 case CombiningKind::MINSI:
8257 result =
b.createOrFold<arith::MinSIOp>(loc, v1, acc);
8259 case CombiningKind::MAXUI:
8261 result =
b.createOrFold<arith::MaxUIOp>(loc, v1, acc);
8263 case CombiningKind::MINUI:
8265 result =
b.createOrFold<arith::MinUIOp>(loc, v1, acc);
8267 case CombiningKind::MUL:
8269 result =
b.createOrFold<arith::MulIOp>(loc, v1, acc);
8270 else if (llvm::isa<FloatType>(t1) && llvm::isa<FloatType>(tAcc))
8271 result =
b.createOrFold<arith::MulFOp>(loc, v1, acc, fastmath);
8273 llvm_unreachable(
"invalid value types for MUL reduction");
8275 case CombiningKind::OR:
8277 result =
b.createOrFold<arith::OrIOp>(loc, v1, acc);
8279 case CombiningKind::XOR:
8281 result =
b.createOrFold<arith::XOrIOp>(loc, v1, acc);
8285 assert(
result &&
"unknown CombiningKind");
8293void StepOp::inferResultRanges(ArrayRef<ConstantIntRanges> argRanges,
8295 auto resultType = cast<VectorType>(
getType());
8296 if (resultType.isScalable()) {
8302 uint64_t maxIndex = resultType.getDimSize(0) - 1;
8303 APInt umin = APInt::getZero(bitwidth);
8304 APInt umax = APInt::getMaxValue(bitwidth).ugt(maxIndex)
8305 ? APInt(bitwidth, maxIndex)
8306 : APInt::getMaxValue(bitwidth);
8337struct StepCompareFolder :
public OpRewritePattern<StepOp> {
8340 LogicalResult matchAndRewrite(StepOp stepOp,
8341 PatternRewriter &rewriter)
const override {
8342 const int64_t stepSize = stepOp.getResult().getType().getNumElements();
8344 for (OpOperand &use : stepOp.getResult().getUses()) {
8345 auto cmpiOp = dyn_cast<arith::CmpIOp>(use.getOwner());
8350 const unsigned stepOperandNumber = use.getOperandNumber();
8351 if (stepOperandNumber != 0)
8355 unsigned constOperandNumber = 1;
8356 Value otherOperand = cmpiOp.getOperand(constOperandNumber);
8357 std::optional<int64_t> maybeConstValue =
8359 if (!maybeConstValue.has_value())
8362 int64_t constValue = maybeConstValue.value();
8363 arith::CmpIPredicate pred = cmpiOp.getPredicate();
8365 auto maybeSplat = [&]() -> std::optional<bool> {
8367 if ((pred == arith::CmpIPredicate::ult ||
8368 pred == arith::CmpIPredicate::uge) &&
8369 stepSize <= constValue)
8370 return pred == arith::CmpIPredicate::ult;
8373 if ((pred == arith::CmpIPredicate::ule ||
8374 pred == arith::CmpIPredicate::ugt) &&
8375 stepSize - 1 <= constValue) {
8376 return pred == arith::CmpIPredicate::ule;
8380 if ((pred == arith::CmpIPredicate::eq ||
8381 pred == arith::CmpIPredicate::ne) &&
8382 stepSize <= constValue)
8383 return pred == arith::CmpIPredicate::ne;
8385 return std::nullopt;
8388 if (!maybeSplat.has_value())
8393 auto type = dyn_cast<VectorType>(cmpiOp.getResult().getType());
8398 Value splat = mlir::arith::ConstantOp::create(rewriter, cmpiOp.getLoc(),
8410void StepOp::getCanonicalizationPatterns(RewritePatternSet &results,
8411 MLIRContext *context) {
8412 results.
add<StepCompareFolder>(context);
8422 Operation *maskableOp) {
8423 assert(maskableOp->
getBlock() &&
"MaskableOp must be inserted into a block");
8435 Operation *maskableOp, Value mask,
8440 return MaskOp::create(builder, maskableOp->
getLoc(),
8443 return MaskOp::create(builder, maskableOp->
getLoc(),
8456 Value newValue, Value passthru) {
8460 return arith::SelectOp::create(builder, newValue.
getLoc(), newValue.
getType(),
8461 mask, newValue, passthru);
8472struct InterleaveDeinterleaveFolder :
public OpRewritePattern<InterleaveOp> {
8475 LogicalResult matchAndRewrite(InterleaveOp interleaveOp,
8476 PatternRewriter &rewriter)
const override {
8477 auto lhsDefOp = interleaveOp.getLhs().getDefiningOp<DeinterleaveOp>();
8478 auto rhsDefOp = interleaveOp.getRhs().getDefiningOp<DeinterleaveOp>();
8479 if (!lhsDefOp || !rhsDefOp || lhsDefOp != rhsDefOp)
8481 for (
auto [idx, operand] : llvm::enumerate(interleaveOp.getOperands())) {
8482 if (cast<OpResult>(operand).getResultNumber() != idx)
8485 rewriter.
replaceOp(interleaveOp, lhsDefOp.getSource());
8491void InterleaveOp::getCanonicalizationPatterns(RewritePatternSet &results,
8492 MLIRContext *context) {
8493 results.
add<InterleaveDeinterleaveFolder>(context);
8496OpFoldResult InterleaveOp::fold(FoldAdaptor adaptor) {
8498 auto splat = dyn_cast_if_present<SplatElementsAttr>(adaptor.getLhs());
8499 if (!splat || adaptor.getLhs() != adaptor.getRhs())
8501 return SplatElementsAttr::get(getResultVectorType(),
8502 splat.getSplatValue<Attribute>());
8505std::optional<SmallVector<int64_t, 4>> InterleaveOp::getShapeForUnroll() {
8506 return llvm::to_vector<4>(getResultVectorType().
getShape());
8513std::optional<SmallVector<int64_t, 4>> DeinterleaveOp::getShapeForUnroll() {
8514 return llvm::to_vector<4>(getResultVectorType().
getShape());
8521#define GET_ATTRDEF_CLASSES
8522#include "mlir/Dialect/Vector/IR/VectorAttributes.cpp.inc"
8524#define GET_OP_CLASSES
8525#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)