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() {
607 int64_t sourceRank = getSourceVectorType().getRank();
609 for (
int64_t dim : getReductionDims()) {
610 if (dim < 0 || dim >= sourceRank)
611 return emitOpError(
"reduction dimension out of range: ") << dim;
613 return emitOpError(
"duplicate reduction dimension: ") << dim;
614 isReduced[dim] =
true;
619 Type inferredReturnType;
620 auto sourceScalableDims = getSourceVectorType().getScalableDims();
621 for (
auto [dimIdx, dimSize] :
622 llvm::enumerate(getSourceVectorType().
getShape()))
623 if (!isReduced[dimIdx]) {
624 targetShape.push_back(dimSize);
625 scalableDims.push_back(sourceScalableDims[dimIdx]);
628 if (targetShape.empty())
629 inferredReturnType = getSourceVectorType().getElementType();
631 inferredReturnType = VectorType::get(
632 targetShape, getSourceVectorType().
getElementType(), scalableDims);
633 if (
getType() != inferredReturnType)
635 <<
" is incompatible with source type "
636 << getSourceVectorType();
642Type MultiDimReductionOp::getExpectedMaskType() {
643 auto vecType = getSourceVectorType();
644 return VectorType::get(vecType.getShape(),
645 IntegerType::get(vecType.getContext(), 1),
646 vecType.getScalableDims());
655struct ElideUnitDimsInMultiDimReduction
659 LogicalResult matchAndRewrite(MultiDimReductionOp reductionOp,
660 PatternRewriter &rewriter)
const override {
661 ArrayRef<int64_t> shape = reductionOp.getSourceVectorType().getShape();
662 for (
const auto &dim :
enumerate(shape)) {
663 if (reductionOp.isReducedDim(dim.index()) && dim.value() != 1)
668 OpBuilder::InsertionGuard guard(rewriter);
671 if (reductionOp.isMasked()) {
673 rootOp = reductionOp.getMaskingOp();
674 mask = reductionOp.getMaskingOp().getMask();
676 rootOp = reductionOp;
679 Location loc = reductionOp.getLoc();
680 Value acc = reductionOp.getAcc();
682 if (
auto dstVecType = dyn_cast<VectorType>(reductionOp.getDestType())) {
684 VectorType newMaskType =
685 VectorType::get(dstVecType.getShape(), rewriter.
getI1Type(),
686 dstVecType.getScalableDims());
687 mask = vector::ShapeCastOp::create(rewriter, loc, newMaskType, mask);
689 cast = vector::ShapeCastOp::create(
690 rewriter, loc, reductionOp.getDestType(), reductionOp.getSource());
695 mask = vector::ExtractOp::create(rewriter, loc, mask);
696 cast = vector::ExtractOp::create(rewriter, loc, reductionOp.getSource());
701 cast,
nullptr, mask);
708void MultiDimReductionOp::getCanonicalizationPatterns(
710 results.
add<ElideUnitDimsInMultiDimReduction>(context);
719 arith::FastMathFlags fastMathFlags) {
725 arith::FastMathFlags fastMathFlags) {
727 llvm::cast<VectorType>(
vector.getType()).getElementType(), kind,
vector,
731LogicalResult ReductionOp::verify() {
733 int64_t rank = getSourceVectorType().getRank();
735 return emitOpError(
"unsupported reduction rank: ") << rank;
738 Type eltType = getDest().getType();
741 << eltType <<
"' for kind '" << stringifyCombiningKind(getKind())
750Type ReductionOp::getExpectedMaskType() {
751 auto vecType = getSourceVectorType();
752 return VectorType::get(vecType.getShape(),
753 IntegerType::get(vecType.getContext(), 1),
754 vecType.getScalableDims());
761 case arith::AtomicRMWKind::addf:
762 case arith::AtomicRMWKind::addi:
763 return vector::ReductionOp::create(builder,
vector.getLoc(),
764 CombiningKind::ADD,
vector);
765 case arith::AtomicRMWKind::mulf:
766 case arith::AtomicRMWKind::muli:
767 return vector::ReductionOp::create(builder,
vector.getLoc(),
768 CombiningKind::MUL,
vector);
769 case arith::AtomicRMWKind::minimumf:
770 return vector::ReductionOp::create(builder,
vector.getLoc(),
771 CombiningKind::MINIMUMF,
vector);
772 case arith::AtomicRMWKind::mins:
773 return vector::ReductionOp::create(builder,
vector.getLoc(),
774 CombiningKind::MINSI,
vector);
775 case arith::AtomicRMWKind::minu:
776 return vector::ReductionOp::create(builder,
vector.getLoc(),
777 CombiningKind::MINUI,
vector);
778 case arith::AtomicRMWKind::maximumf:
779 return vector::ReductionOp::create(builder,
vector.getLoc(),
780 CombiningKind::MAXIMUMF,
vector);
781 case arith::AtomicRMWKind::maxs:
782 return vector::ReductionOp::create(builder,
vector.getLoc(),
783 CombiningKind::MAXSI,
vector);
784 case arith::AtomicRMWKind::maxu:
785 return vector::ReductionOp::create(builder,
vector.getLoc(),
786 CombiningKind::MAXUI,
vector);
787 case arith::AtomicRMWKind::andi:
788 return vector::ReductionOp::create(builder,
vector.getLoc(),
789 CombiningKind::AND,
vector);
790 case arith::AtomicRMWKind::ori:
791 return vector::ReductionOp::create(builder,
vector.getLoc(),
792 CombiningKind::OR,
vector);
793 case arith::AtomicRMWKind::minnumf:
794 return vector::ReductionOp::create(builder,
vector.getLoc(),
795 CombiningKind::MINNUMF,
vector);
796 case arith::AtomicRMWKind::maxnumf:
797 return vector::ReductionOp::create(builder,
vector.getLoc(),
798 CombiningKind::MAXNUMF,
vector);
799 case arith::AtomicRMWKind::xori:
800 return vector::ReductionOp::create(builder,
vector.getLoc(),
801 CombiningKind::XOR,
vector);
809std::optional<SmallVector<int64_t, 4>> ReductionOp::getShapeForUnroll() {
810 return llvm::to_vector<4>(getSourceVectorType().
getShape());
817 LogicalResult matchAndRewrite(ReductionOp reductionOp,
822 cast<vector::MaskableOpInterface>(reductionOp.getOperation());
825 if (maskableOp.isMasked()) {
827 rootOp = maskableOp.getMaskingOp();
828 mask = maskableOp.getMaskingOp().getMask();
830 rootOp = reductionOp;
833 auto vectorType = reductionOp.getSourceVectorType();
834 if (vectorType.getRank() != 0 && vectorType.getDimSize(0) != 1)
837 Location loc = reductionOp.getLoc();
839 mask = ExtractOp::create(rewriter, loc, mask);
840 Value
result = ExtractOp::create(rewriter, loc, reductionOp.getVector());
842 if (Value acc = reductionOp.getAcc())
845 reductionOp.getFastmathAttr(), mask);
855 results.
add<ElideSingleElementReduction>(context);
869 getIndexingMapsAttrName(
result.name),
873 getIteratorTypesAttrName(
result.name),
876 return IteratorTypeAttr::get(builder.getContext(), t);
885 ContractionOp::getDefaultKind());
891 ArrayAttr iteratorTypes, CombiningKind kind,
892 arith::FastMathFlags fastMathFlags) {
895 result.addAttribute(getIndexingMapsAttrName(
result.name), indexingMaps);
896 result.addAttribute(getIteratorTypesAttrName(
result.name), iteratorTypes);
898 CombiningKindAttr::get(builder.
getContext(), kind));
899 if (fastMathFlags != arith::FastMathFlags::none)
901 getFastmathAttrName(
result.name),
902 arith::FastMathFlagsAttr::get(builder.
getContext(), fastMathFlags));
913 DictionaryAttr dictAttr;
927 result.attributes.append(dictAttr.getValue().begin(),
928 dictAttr.getValue().end());
934 auto iteratorTypes = dyn_cast_or_null<ArrayAttr>(
935 result.attributes.get(getIteratorTypesAttrName(
result.name)));
936 if (!iteratorTypes) {
938 <<
"expected " << getIteratorTypesAttrName(
result.name)
939 <<
" array attribute";
944 for (StringRef s : iteratorTypes.getAsValueRange<StringAttr>()) {
945 auto maybeIteratorType = symbolizeIteratorType(s);
946 if (!maybeIteratorType.has_value())
947 return parser.
emitError(loc) <<
"unexpected iterator_type (" << s <<
")";
949 iteratorTypeAttrs.push_back(
950 IteratorTypeAttr::get(parser.
getContext(), maybeIteratorType.value()));
952 result.attributes.set(getIteratorTypesAttrName(
result.name),
955 if (!
result.attributes.get(getKindAttrName(
result.name))) {
957 getKindAttrName(
result.name),
958 CombiningKindAttr::get(
result.getContext(),
959 ContractionOp::getDefaultKind()));
961 if (masksInfo.empty())
963 if (masksInfo.size() != 2)
965 "expected zero or exactly 2 vector mask operands");
966 auto lhsType = llvm::cast<VectorType>(types[0]);
967 auto rhsType = llvm::cast<VectorType>(types[1]);
969 std::array<VectorType, 2> maskTypes = {
979 auto attrNames = getTraitAttrNames();
981 traitAttrsSet.insert_range(attrNames);
983 for (
auto attr : (*this)->getAttrs()) {
984 if (attr.getName() == getIteratorTypesAttrName()) {
986 llvm::cast<ArrayAttr>(attr.getValue())
987 .getAsValueRange<IteratorTypeAttr, IteratorType>();
993 llvm::map_to_vector(iteratorTypes, [&](IteratorType t) ->
Attribute {
994 return StringAttr::get(
getContext(), stringifyIteratorType(t));
997 attrs.emplace_back(getIteratorTypesAttrName(),
998 ArrayAttr::get(
getContext(), iteratorTypeNames));
999 }
else if (traitAttrsSet.count(attr.getName().strref()) > 0) {
1001 if (attr.getName() == getFastmathAttrName() &&
1002 llvm::cast<arith::FastMathFlagsAttr>(attr.getValue()).getValue() ==
1003 arith::FastMathFlags::none)
1005 attrs.push_back(attr);
1009 auto dictAttr = DictionaryAttr::get(
getContext(), attrs);
1010 p <<
" " << dictAttr <<
" " << getLhs() <<
", ";
1011 p << getRhs() <<
", " << getAcc();
1014 p <<
" : " << getLhs().getType() <<
", " << getRhs().getType() <<
" into "
1019 const std::vector<std::pair<int64_t, int64_t>> &map) {
1020 for (
auto &dimPair : map) {
1021 if (dimPair.first < 0 || dimPair.first >= lhsType.getRank() ||
1022 dimPair.second < 0 || dimPair.second >= rhsType.getRank() ||
1023 lhsType.getDimSize(dimPair.first) != rhsType.getDimSize(dimPair.second))
1030 ContractionOp op, VectorType lhsType, VectorType rhsType,
Type accType,
1032 const std::vector<std::pair<int64_t, int64_t>> &contractingDimMap,
1033 const std::vector<std::pair<int64_t, int64_t>> &batchDimMap) {
1036 for (
auto &dimPair : contractingDimMap) {
1037 lhsContractingDimSet.insert(dimPair.first);
1038 rhsContractingDimSet.insert(dimPair.second);
1041 llvm::make_second_range(batchDimMap));
1045 for (
int64_t i = 0, e = lhsType.getRank(); i < e; ++i) {
1046 if (lhsContractingDimSet.count(i) > 0)
1048 expectedResultDims.push_back(lhsType.getDimSize(i));
1052 for (
int64_t i = 0, e = rhsType.getRank(); i < e; ++i) {
1053 if (rhsContractingDimSet.count(i) > 0 || rhsBatchDimSet.count(i) > 0)
1055 expectedResultDims.push_back(rhsType.getDimSize(i));
1059 if (expectedResultDims.empty()) {
1061 if (llvm::isa<VectorType>(resType) || llvm::isa<VectorType>(accType))
1062 return op.emitOpError(
"invalid accumulator/result vector shape");
1065 auto resVectorType = llvm::dyn_cast<VectorType>(resType);
1066 auto accVectorType = llvm::dyn_cast<VectorType>(accType);
1067 if (!resVectorType || !accVectorType)
1068 return op.emitOpError(
"invalid accumulator/result vector shape");
1074 AffineMap lhsMap = op.getIndexingMapsArray()[0];
1075 AffineMap rhsMap = op.getIndexingMapsArray()[1];
1077 return op.emitOpError(
1078 "expected all dimensions to be either a LHS or a RHS dimension");
1081 {std::make_pair(lhsType, lhsMap), std::make_pair(rhsType, rhsMap)}) {
1082 VectorType v = pair.first;
1083 auto map = pair.second;
1084 for (
unsigned idx = 0, e = v.getRank(); idx < e; ++idx) {
1085 unsigned pos = map.getDimPosition(idx);
1090 if (!llvm::all_of(extents, [](
AffineExpr e) {
return e; }))
1091 return op.emitOpError(
"expected all dimensions to get an extent as "
1092 "either a LHS or a RHS dimension");
1094 AffineMap resMap = op.getIndexingMapsArray()[2];
1099 assert(llvm::all_of(expectedMap.
getResults(),
1100 llvm::IsaPred<AffineConstantExpr>) &&
1101 "expected constant extent along all dimensions.");
1103 auto expectedShape =
1105 return cast<AffineConstantExpr>(e).getValue();
1108 VectorType::get(expectedShape, resVectorType.getElementType(),
1109 resVectorType.getScalableDims());
1110 if (resVectorType != expected || accVectorType != expected)
1111 return op.emitOpError(
1112 "invalid accumulator/result vector shape, expected: ")
1118LogicalResult ContractionOp::verify() {
1119 VectorType lhsType = getLhsType();
1120 VectorType rhsType = getRhsType();
1121 Type accType = getAccType();
1122 Type resType = getResultType();
1124 if (llvm::isa<IntegerType>(lhsType.getElementType())) {
1125 if (!lhsType.getElementType().isSignlessInteger())
1126 return emitOpError(
"only supports signless integer types");
1130 if (getIndexingMapsArray().size() != 3)
1131 return emitOpError(
"expected an indexing map for each vector operand");
1136 unsigned numIterators = getIteratorTypes().getValue().size();
1137 for (
const auto &it : llvm::enumerate(getIndexingMapsArray())) {
1138 auto index = it.index();
1139 auto map = it.value();
1140 if (map.getNumSymbols() != 0)
1142 <<
index <<
" to have no symbols";
1143 auto vectorType = llvm::dyn_cast<VectorType>(getOperand(
index).
getType());
1144 unsigned rank = vectorType ? vectorType.getShape().size() : 0;
1147 if (map.getNumDims() != numIterators)
1149 <<
index <<
" to have " << numIterators <<
" number of inputs";
1150 if (map.getNumResults() != rank)
1152 <<
index <<
" to have " << rank <<
" number of outputs";
1153 if (!map.isProjectedPermutation())
1155 <<
index <<
" to be a projected permutation of its inputs";
1158 auto contractingDimMap = getContractingDimMap();
1159 auto batchDimMap = getBatchDimMap();
1162 if (contractingDimMap.empty())
1163 return emitOpError(
"expected at least one contracting dimension pair");
1166 if (!
verifyDimMap(lhsType, rhsType, contractingDimMap))
1167 return emitOpError(
"invalid contracting dimension map");
1171 return emitOpError(
"invalid batch dimension map");
1175 contractingDimMap, batchDimMap)))
1178 if (!getKindAttr()) {
1179 return emitOpError(
"expected 'kind' attribute of type CombiningKind (e.g. "
1180 "'vector.kind<add>')");
1184 auto vectorType = llvm::dyn_cast<VectorType>(resType);
1185 auto elementType = vectorType ? vectorType.getElementType() : resType;
1187 return emitOpError(
"unsupported contraction type");
1190 return cast<IndexingMapOpInterface>(this->getOperation()).verifyImpl();
1197Type ContractionOp::getExpectedMaskType() {
1198 auto indexingMaps = this->getIndexingMapsArray();
1201 VectorType lhsType = this->getLhsType();
1202 VectorType rhsType = this->getRhsType();
1204 unsigned numVecDims = lhsIdxMap.
getNumDims();
1210 for (
auto [dimIdx, dimSize] : llvm::enumerate(lhsType.getShape())) {
1213 lhsType.getScalableDims()[dimIdx];
1215 for (
auto [dimIdx, dimSize] : llvm::enumerate(rhsType.getShape())) {
1218 rhsType.getScalableDims()[dimIdx];
1221 assert(ShapedType::isStaticShape(maskShape) &&
1222 "Mask shape couldn't be computed");
1224 return VectorType::get(maskShape,
1225 IntegerType::get(lhsType.getContext(), 1),
1226 maskShapeScalableDims);
1231 getIteratorTypesAttrName(), getKindAttrName(),
1232 getFastmathAttrName()};
1242static std::vector<std::pair<int64_t, int64_t>>
1244 IteratorType targetIteratorType,
MLIRContext *context) {
1245 std::vector<std::pair<int64_t, int64_t>> dimMap;
1246 for (
const auto &it : llvm::enumerate(iteratorTypes)) {
1247 auto iteratorType = llvm::cast<IteratorTypeAttr>(it.value()).getValue();
1248 if (iteratorType != targetIteratorType)
1254 if (lhsDim >= 0 && rhsDim >= 0)
1255 dimMap.emplace_back(lhsDim, rhsDim);
1260void ContractionOp::getIterationBounds(
1262 auto lhsShape = getLhsType().getShape();
1263 auto resVectorType = llvm::dyn_cast<VectorType>(getResultType());
1265 for (
const auto &it : llvm::enumerate(getIteratorTypes())) {
1268 auto iteratorType = llvm::cast<IteratorTypeAttr>(it.value()).getValue();
1269 if (iteratorType == IteratorType::reduction) {
1272 assert(lhsDimIndex >= 0);
1273 iterationBounds.push_back(lhsShape[lhsDimIndex]);
1278 assert(resDimIndex >= 0);
1279 assert(resVectorType !=
nullptr);
1280 iterationBounds.push_back(resVectorType.getShape()[resDimIndex]);
1284void ContractionOp::getIterationIndexMap(
1286 unsigned numMaps = getIndexingMapsArray().size();
1287 iterationIndexMap.resize(numMaps);
1288 for (
const auto &it : llvm::enumerate(getIndexingMapsArray())) {
1289 auto index = it.index();
1290 auto map = it.value();
1291 for (
unsigned i = 0, e = map.getNumResults(); i < e; ++i) {
1292 auto dim = cast<AffineDimExpr>(map.getResult(i));
1293 iterationIndexMap[
index][dim.getPosition()] = i;
1298std::vector<std::pair<int64_t, int64_t>> ContractionOp::getContractingDimMap() {
1300 return getDimMap(indexingMaps, getIteratorTypes(), IteratorType::reduction,
1304std::vector<std::pair<int64_t, int64_t>> ContractionOp::getBatchDimMap() {
1306 return getDimMap(indexingMaps, getIteratorTypes(), IteratorType::parallel,
1310std::optional<SmallVector<int64_t, 4>> ContractionOp::getShapeForUnroll() {
1312 getIterationBounds(
shape);
1334template <
typename AddOpType>
1340 auto canonicalize = [&](
Value maybeContraction,
1341 Value otherOperand) -> vector::ContractionOp {
1342 vector::ContractionOp contractionOp =
1343 dyn_cast_or_null<vector::ContractionOp>(
1346 return vector::ContractionOp();
1347 if (
auto maybeZero = dyn_cast_or_null<arith::ConstantOp>(
1348 contractionOp.getAcc().getDefiningOp())) {
1349 if (maybeZero.getValue() ==
1350 rewriter.
getZeroAttr(contractionOp.getAcc().getType())) {
1352 bvm.
map(contractionOp.getAcc(), otherOperand);
1353 auto newContraction =
1354 cast<vector::ContractionOp>(rewriter.
clone(*contractionOp, bvm));
1355 rewriter.
replaceOp(addOp, newContraction.getResult());
1356 return newContraction;
1359 return vector::ContractionOp();
1362 Value a = addOp->getOperand(0),
b = addOp->getOperand(1);
1363 vector::ContractionOp
contract = canonicalize(a,
b);
1388 setResultRanges(getResult(), argRanges.front());
1393 auto vectorTy = cast<VectorType>(source.
getType());
1418 build(builder,
result, source, dynamicPos,
1423ExtractOp::inferReturnTypes(
MLIRContext *, std::optional<Location>,
1424 ExtractOp::Adaptor adaptor,
1426 auto vectorType = llvm::cast<VectorType>(adaptor.getSource().getType());
1427 if (
static_cast<int64_t>(adaptor.getStaticPosition().size()) ==
1428 vectorType.getRank()) {
1429 inferredReturnTypes.push_back(vectorType.getElementType());
1431 auto n = std::min<size_t>(adaptor.getStaticPosition().size(),
1432 vectorType.getRank());
1433 inferredReturnTypes.push_back(VectorType::get(
1434 vectorType.getShape().drop_front(n), vectorType.getElementType(),
1435 vectorType.getScalableDims().drop_front(n)));
1440LogicalResult vector::ExtractOp::verify() {
1441 if (
auto resTy = dyn_cast<VectorType>(getResult().
getType()))
1442 if (resTy.getRank() == 0)
1444 "expected a scalar instead of a 0-d vector as the result type");
1447 auto dynamicMarkersCount =
1448 llvm::count_if(getStaticPosition(), ShapedType::isDynamic);
1449 if (
static_cast<size_t>(dynamicMarkersCount) != getDynamicPosition().size())
1451 "mismatch between dynamic and static positions (kDynamic marker but no "
1452 "corresponding dynamic position) -- this can only happen due to an "
1453 "incorrect fold/rewrite");
1454 auto position = getMixedPosition();
1455 if (position.size() >
static_cast<unsigned>(getSourceVectorType().getRank()))
1457 "expected position attribute of rank no greater than vector rank");
1458 for (
auto [idx, pos] : llvm::enumerate(position)) {
1459 if (
auto attr = dyn_cast<Attribute>(pos)) {
1460 int64_t constIdx = cast<IntegerAttr>(attr).getInt();
1462 constIdx, kPoisonIndex, getSourceVectorType().getDimSize(idx))) {
1463 return emitOpError(
"expected position attribute #")
1465 <<
" to be a non-negative integer smaller than the "
1466 "corresponding vector dimension or poison (-1)";
1473template <
typename IntType>
1475 return llvm::map_to_vector<4>(
1476 arrayAttr.getAsRange<IntegerAttr>(),
1477 [](IntegerAttr attr) { return static_cast<IntType>(attr.getInt()); });
1483 if (!extractOp.getSource().getDefiningOp<ExtractOp>())
1487 if (extractOp.hasDynamicPosition())
1491 ExtractOp currentOp = extractOp;
1493 globalPosition.append(extrPos.rbegin(), extrPos.rend());
1494 while (ExtractOp nextOp = currentOp.getSource().getDefiningOp<ExtractOp>()) {
1497 if (currentOp.hasDynamicPosition())
1500 globalPosition.append(extrPos.rbegin(), extrPos.rend());
1502 extractOp.setOperand(0, currentOp.getSource());
1505 std::reverse(globalPosition.begin(), globalPosition.end());
1506 extractOp.setStaticPosition(globalPosition);
1518class ExtractFromInsertTransposeChainState {
1520 ExtractFromInsertTransposeChainState(ExtractOp e);
1529 template <
typename ContainerA,
typename ContainerB>
1530 bool isContainedWithin(
const ContainerA &a,
const ContainerB &
b) {
1531 return a.size() <=
b.size() &&
1532 std::equal(a.begin(), a.begin() + a.size(),
b.begin());
1539 template <
typename ContainerA,
typename ContainerB>
1540 bool intersectsWhereNonNegative(
const ContainerA &a,
const ContainerB &
b) {
1541 for (
auto [elemA, elemB] : llvm::zip(a,
b)) {
1542 if (elemA < 0 || elemB < 0)
1553 return (sentinels == ArrayRef(extractPosition).drop_front(extractedRank));
1557 void updateStateForNextIteration(Value v) {
1564 LogicalResult handleTransposeOp();
1567 LogicalResult handleInsertOpWithMatchingPos(Value &res);
1582 LogicalResult handleInsertOpWithPrefixPos(Value &res);
1587 Value tryToFoldExtractOpInPlace(Value source);
1589 ExtractOp extractOp;
1591 int64_t extractedRank;
1593 InsertOp nextInsertOp;
1594 TransposeOp nextTransposeOp;
1604 SmallVector<int64_t> sentinels;
1605 SmallVector<int64_t> extractPosition;
1609ExtractFromInsertTransposeChainState::ExtractFromInsertTransposeChainState(
1611 : extractOp(e), vectorRank(extractOp.getSourceVectorType().getRank()),
1612 extractedRank(extractOp.getNumIndices()) {
1613 assert(vectorRank >= extractedRank &&
"Extracted position overflow");
1614 sentinels.reserve(vectorRank - extractedRank);
1615 for (
int64_t i = 0, e = vectorRank - extractedRank; i < e; ++i)
1616 sentinels.push_back(-(i + 1));
1618 extractOp.getStaticPosition().end());
1624LogicalResult ExtractFromInsertTransposeChainState::handleTransposeOp() {
1626 if (extractOp.hasDynamicPosition())
1629 if (!nextTransposeOp)
1632 nextTransposeOp.getPermutation(), extractOp.getContext()));
1639ExtractFromInsertTransposeChainState::handleInsertOpWithMatchingPos(
1642 if (extractOp.hasDynamicPosition() || nextInsertOp.hasDynamicPosition())
1645 ArrayRef<int64_t> insertedPos = nextInsertOp.getStaticPosition();
1646 if (insertedPos != llvm::ArrayRef(
extractPosition).take_front(extractedRank))
1649 res = nextInsertOp.getValueToStore();
1658ExtractFromInsertTransposeChainState::handleInsertOpWithPrefixPos(Value &res) {
1660 if (extractOp.hasDynamicPosition() || nextInsertOp.hasDynamicPosition())
1663 ArrayRef<int64_t> insertedPos = nextInsertOp.getStaticPosition();
1673 res = nextInsertOp.getValueToStore();
1681Value ExtractFromInsertTransposeChainState::tryToFoldExtractOpInPlace(
1684 if (extractOp.hasDynamicPosition())
1688 bool nothingToFold = (source == extractOp.getSource());
1689 if (nothingToFold || !canFold())
1693 OpBuilder
b(extractOp.getContext());
1694 extractOp.setStaticPosition(
1696 extractOp.getSourceMutable().assign(source);
1697 return extractOp.getResult();
1701Value ExtractFromInsertTransposeChainState::fold() {
1703 if (extractOp.hasDynamicPosition())
1706 Value valueToExtractFrom = extractOp.getSource();
1707 updateStateForNextIteration(valueToExtractFrom);
1708 while (nextInsertOp || nextTransposeOp) {
1711 if (succeeded(handleTransposeOp())) {
1712 valueToExtractFrom = nextTransposeOp.getVector();
1713 updateStateForNextIteration(valueToExtractFrom);
1719 if (succeeded(handleInsertOpWithMatchingPos(
result)))
1724 if (succeeded(handleInsertOpWithPrefixPos(
result)))
1725 return tryToFoldExtractOpInPlace(
result);
1729 ArrayRef<int64_t> insertedPos = nextInsertOp.getStaticPosition();
1735 valueToExtractFrom = nextInsertOp.getDest();
1736 updateStateForNextIteration(valueToExtractFrom);
1739 return tryToFoldExtractOpInPlace(valueToExtractFrom);
1744 auto hasZeroDimVectorType = [](
Type type) ->
bool {
1745 auto vecType = dyn_cast<VectorType>(type);
1746 return vecType && vecType.getRank() == 0;
1756 if (isa<BroadcastOp>(op))
1759 auto shapeCast = dyn_cast<ShapeCastOp>(op);
1767 VectorType srcType = shapeCast.getSourceVectorType();
1769 uint64_t srcRank = srcType.getRank();
1771 return dstShape.size() >= srcRank && dstShape.take_back(srcRank) == srcShape;
1797 Operation *defOp = extractOp.getSource().getDefiningOp();
1804 if (extractOp.getType() == input.
getType())
1810 auto inputType = llvm::dyn_cast<VectorType>(input.
getType());
1811 auto extractType = llvm::dyn_cast<VectorType>(extractOp.getType());
1812 unsigned inputRank = inputType ? inputType.getRank() : 0;
1813 unsigned broadcastRank = extractOp.getSourceVectorType().getRank();
1814 unsigned extractRank = extractType ? extractType.getRank() : 0;
1817 if (extractRank > inputRank)
1821 assert(inputType &&
"input must be a vector type because of previous checks");
1830 extractType.getShape() != inputShape.take_back(extractRank))
1835 unsigned deltaOverall = inputRank - extractRank;
1836 unsigned deltaBroadcast = broadcastRank - inputRank;
1840 for (
auto [i, size] : llvm::enumerate(inputShape.take_front(deltaOverall))) {
1841 newPositions[i] = size == 1 ? zero : oldPositions[i + deltaBroadcast];
1844 extractOp->setOperands(
1845 llvm::to_vector(llvm::concat<Value>(
ValueRange(input), dynPos)));
1846 extractOp.setStaticPosition(staticPos);
1847 return extractOp.getResult();
1863 if (extractOp.hasDynamicPosition())
1866 auto shuffleOp = extractOp.getSource().getDefiningOp<ShuffleOp>();
1871 if (shuffleOp.getResultVectorType().getRank() != 1)
1874 int64_t inputVecSize = shuffleOp.getV1().getType().getShape()[0];
1875 auto shuffleMask = shuffleOp.getMask();
1876 int64_t extractIdx = extractOp.getStaticPosition()[0];
1877 int64_t shuffleIdx = shuffleMask[extractIdx];
1880 if (shuffleIdx < inputVecSize) {
1881 extractOp.setOperand(0, shuffleOp.getV1());
1882 extractOp.setStaticPosition({shuffleIdx});
1884 extractOp.setOperand(0, shuffleOp.getV2());
1885 extractOp.setStaticPosition({shuffleIdx - inputVecSize});
1888 return extractOp.getResult();
1894 if (extractOp.hasDynamicPosition())
1897 auto shapeCastOp = extractOp.getSource().getDefiningOp<vector::ShapeCastOp>();
1902 auto getDimReverse = [](VectorType type,
int64_t n) {
1903 return type.getShape().take_back(n + 1).front();
1906 llvm::isa<VectorType>(extractOp.getType())
1907 ? llvm::cast<VectorType>(extractOp.getType()).getRank()
1909 if (destinationRank > shapeCastOp.getSourceVectorType().getRank())
1911 if (destinationRank > 0) {
1912 auto destinationType =
1913 llvm::cast<VectorType>(extractOp.getResult().getType());
1914 for (
int64_t i = 0; i < destinationRank; i++) {
1918 if (getDimReverse(shapeCastOp.getSourceVectorType(), i) !=
1919 getDimReverse(destinationType, i))
1926 std::reverse(extractedPos.begin(), extractedPos.end());
1929 for (
int64_t i = 0, e = extractedPos.size(); i < e; i++) {
1930 strides.push_back(stride);
1932 getDimReverse(extractOp.getSourceVectorType(), i + destinationRank);
1940 shapeCastOp.getSourceVectorType().getRank() - destinationRank;
1942 for (
int64_t i = 0; i < numDimension; i++) {
1943 newStrides.push_back(stride);
1945 getDimReverse(shapeCastOp.getSourceVectorType(), i + destinationRank);
1947 std::reverse(newStrides.begin(), newStrides.end());
1951 extractOp.setStaticPosition(newPosition);
1952 extractOp.setOperand(0, shapeCastOp.getSource());
1953 return extractOp.getResult();
1959 if (extractOp.hasDynamicPosition())
1962 auto extractStridedSliceOp =
1963 extractOp.getSource().getDefiningOp<vector::ExtractStridedSliceOp>();
1964 if (!extractStridedSliceOp)
1973 if (extractStridedSliceOp.hasNonUnitStrides())
1979 while (!sliceOffsets.empty()) {
1980 size_t lastOffset = sliceOffsets.size() - 1;
1981 if (sliceOffsets.back() != 0 ||
1982 extractStridedSliceOp.getType().getDimSize(lastOffset) !=
1983 extractStridedSliceOp.getSourceVectorType().getDimSize(lastOffset))
1985 sliceOffsets.pop_back();
1987 unsigned destinationRank = 0;
1988 if (
auto vecType = llvm::dyn_cast<VectorType>(extractOp.getType()))
1989 destinationRank = vecType.getRank();
1992 if (destinationRank > extractStridedSliceOp.getSourceVectorType().getRank() -
1993 sliceOffsets.size())
1997 assert(extractedPos.size() >= sliceOffsets.size());
1998 for (
size_t i = 0, e = sliceOffsets.size(); i < e; i++)
1999 extractedPos[i] = extractedPos[i] + sliceOffsets[i];
2000 extractOp.getSourceMutable().assign(extractStridedSliceOp.getSource());
2004 extractOp.setStaticPosition(extractedPos);
2005 return extractOp.getResult();
2011 if (extractOp.hasDynamicPosition())
2015 llvm::isa<VectorType>(extractOp.getType())
2016 ? llvm::cast<VectorType>(extractOp.getType()).getRank()
2018 auto insertOp = extractOp.getSource().getDefiningOp<InsertStridedSliceOp>();
2028 int64_t insertRankDiff = insertOp.getDestVectorType().getRank() -
2029 insertOp.getSourceVectorType().getRank();
2030 if (destinationRank > insertOp.getSourceVectorType().getRank())
2035 if (llvm::any_of(insertOp.getStrides(), [](
Attribute attr) {
2036 return llvm::cast<IntegerAttr>(attr).getInt() != 1;
2039 bool disjoint =
false;
2041 for (
unsigned dim = 0, e = extractOffsets.size(); dim < e; ++dim) {
2042 int64_t start = insertOffsets[dim];
2044 (dim < insertRankDiff)
2046 : insertOp.getSourceVectorType().getDimSize(dim - insertRankDiff);
2048 int64_t offset = extractOffsets[dim];
2050 if (start <= offset && offset < end) {
2051 if (dim >= insertRankDiff)
2052 offsetDiffs.push_back(offset - start);
2063 insertOp.getSourceVectorType().getRank() - destinationRank;
2064 for (
int64_t i = 0; i < destinationRank; i++) {
2065 if (insertOp.getSourceVectorType().getDimSize(i + srcRankDiff) !=
2066 insertOp.getDestVectorType().getDimSize(i + srcRankDiff +
2070 extractOp.getSourceMutable().assign(insertOp.getValueToStore());
2073 extractOp.setStaticPosition(offsetDiffs);
2074 return extractOp.getResult();
2078 insertOp = insertOp.getDest().getDefiningOp<InsertStridedSliceOp>();
2091 if (extractOp.hasDynamicPosition())
2095 auto fromElementsOp = extractOp.getSource().
getDefiningOp<FromElementsOp>();
2096 if (!fromElementsOp)
2100 auto vecType = llvm::cast<VectorType>(fromElementsOp.getType());
2101 if (vecType.isScalable())
2105 int64_t rank = vecType.getRank();
2107 if (extractOp.getType() != vecType.getElementType())
2110 "unexpected number of indices");
2115 for (
int i = rank - 1; i >= 0; --i) {
2116 flatIndex +=
indices[i] * stride;
2117 stride *= vecType.getDimSize(i);
2119 return fromElementsOp.getElements()[flatIndex];
2124template <
typename OpType,
typename AdaptorType>
2127 std::vector<int64_t> staticPosition = op.getStaticPosition().vec();
2128 OperandRange dynamicPosition = op.getDynamicPosition();
2131 if constexpr (std::is_same_v<OpType, ExtractOp>)
2132 vectorShape = op.getSourceVectorType().getShape();
2137 if (!dynamicPosition.size())
2144 bool opChange =
false;
2145 for (
unsigned i = 0, e = staticPosition.size(); i < e; ++i) {
2146 if (ShapedType::isStatic(staticPosition[i]))
2150 if (
auto attr = mlir::dyn_cast_if_present<IntegerAttr>(positionAttr)) {
2151 int64_t value = attr.getInt();
2155 staticPosition[i] = attr.getInt();
2160 operands.push_back(position);
2164 op.setStaticPosition(staticPosition);
2165 op.getOperation()->setOperands(operands);
2167 return op.getResult();
2177 if (!is_contained(staticPos, poisonVal))
2180 return ub::PoisonAttr::get(context);
2194 auto denseAttr = dyn_cast_if_present<DenseElementsAttr>(srcAttr);
2199 if (denseAttr.isSplat()) {
2201 if (
auto vecDstType = dyn_cast<VectorType>(extractOp.getType()))
2206 auto vecTy = cast<VectorType>(extractOp.getSourceVectorType());
2207 if (vecTy.isScalable())
2210 if (extractOp.hasDynamicPosition()) {
2225 copy(extractOp.getStaticPosition(), completePositions.begin());
2228 auto denseValuesBegin = denseAttr.value_begin<TypedAttr>() + startPos;
2231 if (
auto resVecTy = dyn_cast<VectorType>(extractOp.getType())) {
2233 denseValuesBegin, denseValuesBegin + resVecTy.getNumElements());
2236 newAttr = *denseValuesBegin;
2242OpFoldResult ExtractOp::fold(FoldAdaptor adaptor) {
2246 if (getNumIndices() == 0 && getSource().
getType() == getResult().
getType())
2253 SmallVector<Value> operands = {getSource()};
2257 getContext(), adaptor.getStaticPosition(), kPoisonIndex))
2263 if (
auto res = ExtractFromInsertTransposeChainState(*this).fold())
2278 return inplaceFolded;
2284class ExtractOpFromBroadcast final :
public OpRewritePattern<ExtractOp> {
2288 LogicalResult matchAndRewrite(ExtractOp extractOp,
2289 PatternRewriter &rewriter)
const override {
2292 VectorType outType = dyn_cast<VectorType>(extractOp.getType());
2298 BroadcastableToResult::Success)
2307class ExtractOpFromCreateMask final :
public OpRewritePattern<ExtractOp> {
2311 LogicalResult matchAndRewrite(ExtractOp extractOp,
2312 PatternRewriter &rewriter)
const override {
2314 extractOp.getSource().getDefiningOp<vector::CreateMaskOp>();
2318 VectorType extractedMaskType =
2319 llvm::dyn_cast<VectorType>(extractOp.getResult().getType());
2321 if (!extractedMaskType)
2324 auto maskOperands = createMaskOp.getOperands();
2325 ArrayRef<int64_t> extractOpPos = extractOp.getStaticPosition();
2326 VectorType maskType = createMaskOp.getVectorType();
2328 bool containsUnknownDims =
false;
2331 for (
size_t dimIdx = 0; !allFalse && dimIdx < extractOpPos.size();
2333 int64_t pos = extractOpPos[dimIdx];
2334 Value operand = maskOperands[dimIdx];
2335 auto constantOp = operand.
getDefiningOp<arith::ConstantOp>();
2338 containsUnknownDims =
true;
2342 int64_t createMaskBound =
2343 llvm::cast<IntegerAttr>(constantOp.getValue()).getInt();
2345 if (pos != ShapedType::kDynamic) {
2348 allFalse |= pos >= createMaskBound;
2349 }
else if (createMaskBound < maskType.getDimSize(dimIdx)) {
2353 containsUnknownDims =
true;
2360 }
else if (!containsUnknownDims) {
2362 extractOp, extractedMaskType,
2363 maskOperands.drop_front(extractOpPos.size()));
2372class ExtractOpFromConstantMask final :
public OpRewritePattern<ExtractOp> {
2376 LogicalResult matchAndRewrite(ExtractOp extractOp,
2377 PatternRewriter &rewriter)
const override {
2378 auto constantMaskOp =
2379 extractOp.getSource().getDefiningOp<vector::ConstantMaskOp>();
2380 if (!constantMaskOp)
2383 Type resultType = extractOp.getResult().getType();
2384 auto extractedMaskType = dyn_cast<VectorType>(resultType);
2386 ArrayRef<int64_t> extractOpPos = extractOp.getStaticPosition();
2387 ArrayRef<int64_t> maskDimSizes = constantMaskOp.getMaskDimSizes();
2389 VectorType maskType = constantMaskOp.getVectorType();
2392 for (
size_t dimIdx = 0; dimIdx < extractOpPos.size(); dimIdx++) {
2393 int64_t pos = extractOpPos[dimIdx];
2394 if (pos == ShapedType::kDynamic) {
2397 if (maskDimSizes[dimIdx] == maskType.getDimSize(dimIdx))
2406 if (pos >= maskDimSizes[dimIdx]) {
2407 if (extractedMaskType) {
2419 if (extractedMaskType) {
2423 extractOp, extractedMaskType,
2424 maskDimSizes.drop_front(extractOpPos.size()));
2437LogicalResult foldExtractFromShapeCastToShapeCast(ExtractOp extractOp,
2438 PatternRewriter &rewriter) {
2439 auto castOp = extractOp.getSource().getDefiningOp<ShapeCastOp>();
2443 VectorType sourceType = castOp.getSourceVectorType();
2444 auto targetType = dyn_cast<VectorType>(extractOp.getResult().getType());
2448 if (sourceType.getNumElements() != targetType.getNumElements())
2452 castOp.getSource());
2462LogicalResult foldExtractFromFromElements(ExtractOp extractOp,
2463 PatternRewriter &rewriter) {
2465 if (extractOp.hasDynamicPosition())
2469 auto resultType = dyn_cast<VectorType>(extractOp.getType());
2474 auto fromElementsOp = extractOp.getSource().getDefiningOp<FromElementsOp>();
2475 if (!fromElementsOp)
2477 VectorType inputType = fromElementsOp.getType();
2480 if (resultType.isScalable() || inputType.isScalable())
2485 SmallVector<int64_t> firstElementPos =
2486 llvm::to_vector(extractOp.getStaticPosition());
2487 firstElementPos.append(resultType.getRank(), 0);
2490 for (int64_t i = inputType.getRank() - 1; i >= 0; --i) {
2491 flatIndex += firstElementPos[i] * stride;
2492 stride *= inputType.getDimSize(i);
2497 extractOp, resultType,
2498 fromElementsOp.getElements().slice(flatIndex,
2499 resultType.getNumElements()));
2511struct ExtractToShapeCast final : OpRewritePattern<vector::ExtractOp> {
2513 LogicalResult matchAndRewrite(vector::ExtractOp extractOp,
2514 PatternRewriter &rewriter)
const override {
2515 VectorType sourceType = extractOp.getSourceVectorType();
2516 VectorType outType = dyn_cast<VectorType>(extractOp.getType());
2520 if (sourceType.getNumElements() != outType.getNumElements())
2522 extractOp,
"extract to vector with fewer elements");
2526 if (llvm::any_of(extractOp.getMixedPosition(),
2527 [](OpFoldResult v) { return !isConstantIntValue(v, 0); }))
2529 "leaving for extract poison folder");
2532 extractOp.getSource());
2553struct FoldExtractFromInsertUnitDim final
2554 : OpRewritePattern<vector::ExtractOp> {
2557 LogicalResult matchAndRewrite(vector::ExtractOp extractOp,
2558 PatternRewriter &rewriter)
const override {
2559 if (extractOp.hasDynamicPosition())
2562 auto insertOp = extractOp.getSource().getDefiningOp<vector::InsertOp>();
2563 if (!insertOp || insertOp.hasDynamicPosition())
2566 ArrayRef<int64_t> extractPos = extractOp.getStaticPosition();
2567 ArrayRef<int64_t> insertPos = insertOp.getStaticPosition();
2570 if (extractPos.size() >= insertPos.size() ||
2571 extractPos != insertPos.take_front(extractPos.size()))
2577 auto srcVecType = extractOp.getSourceVectorType();
2578 for (int64_t i = extractPos.size(), e = srcVecType.getRank(); i < e; ++i)
2579 if (srcVecType.getDimSize(i) != 1)
2582 Value
inserted = insertOp.getValueToStore();
2583 Type extractedType = extractOp.getResult().getType();
2584 if (isa<VectorType>(
inserted.getType())) {
2591 extractOp, extractOp.getResult().
getType(),
2592 insertOp.getValueToStore());
2600void ExtractOp::getCanonicalizationPatterns(RewritePatternSet &results,
2601 MLIRContext *context) {
2602 results.
add<ExtractOpFromBroadcast, ExtractOpFromCreateMask,
2603 ExtractOpFromConstantMask, ExtractToShapeCast,
2604 FoldExtractFromInsertUnitDim>(context);
2605 results.
add(foldExtractFromShapeCastToShapeCast);
2606 results.
add(foldExtractFromFromElements);
2611 for (
auto attr : arrayAttr)
2612 results.push_back(llvm::cast<IntegerAttr>(attr).getInt());
2619std::optional<SmallVector<int64_t, 4>> FMAOp::getShapeForUnroll() {
2630 if (operands.empty())
2633 return llvm::all_of(operands, [&](
Value operand) {
2635 return currentDef == defOp;
2653 auto fromElementsOp =
2654 toElementsOp.getSource().getDefiningOp<FromElementsOp>();
2655 if (!fromElementsOp)
2658 llvm::append_range(results, fromElementsOp.getElements());
2675 auto bcastOp = toElementsOp.getSource().getDefiningOp<BroadcastOp>();
2679 if (isa<VectorType>(bcastOp.getSource().getType()))
2682 auto resultVecType = cast<VectorType>(toElementsOp.getSource().getType());
2684 Value scalar = bcastOp.getSource();
2685 results.assign(resultVecType.getNumElements(), scalar);
2689LogicalResult ToElementsOp::fold(FoldAdaptor adaptor,
2690 SmallVectorImpl<OpFoldResult> &results) {
2695 if (
auto shapeCast = getSource().getDefiningOp<ShapeCastOp>()) {
2696 setOperand(shapeCast.getSource());
2704ToElementsOp::inferReturnTypes(MLIRContext *ctx, std::optional<Location> loc,
2705 ToElementsOp::Adaptor adaptor,
2706 SmallVectorImpl<Type> &inferredReturnTypes) {
2707 auto vecType = cast<VectorType>(adaptor.getSource().getType());
2708 Type elType = vecType.getElementType();
2709 inferredReturnTypes.append(vecType.getNumElements(), elType);
2731 auto bcastOp = toElementsOp.getSource().getDefiningOp<BroadcastOp>();
2736 auto srcType = dyn_cast<VectorType>(bcastOp.getSource().getType());
2740 auto dstType = cast<VectorType>(toElementsOp.getSource().getType());
2745 int64_t dstRank = dstShape.size();
2746 int64_t srcRank = srcShape.size();
2749 auto srcElems = vector::ToElementsOp::create(
2750 rewriter, toElementsOp.getLoc(), bcastOp.getSource());
2752 int64_t dstCount = llvm::product_of(dstShape);
2755 replacements.reserve(dstCount);
2780 for (
int64_t lin = 0; lin < dstCount; ++lin) {
2783 for (
int64_t k = 0; k < srcRank; ++k)
2784 srcIdx[k] = (srcShape[k] == 1) ? 0 : dstIdx[dstRank - srcRank + k];
2787 replacements.push_back(srcElems.getResult(srcLin));
2790 rewriter.
replaceOp(toElementsOp, replacements);
2795void ToElementsOp::getCanonicalizationPatterns(RewritePatternSet &results,
2796 MLIRContext *context) {
2797 results.
add<ToElementsOfBroadcast>(context);
2817 OperandRange fromElemsOperands = fromElementsOp.getElements();
2818 if (fromElemsOperands.empty())
2821 auto toElementsOp = fromElemsOperands[0].getDefiningOp<ToElementsOp>();
2829 Value toElementsInput = toElementsOp.getSource();
2830 if (fromElementsOp.getType() == toElementsInput.
getType() &&
2831 llvm::equal(fromElemsOperands, toElementsOp.getResults())) {
2832 return toElementsInput;
2852 if (llvm::any_of(elements, [](
Attribute attr) {
2858 auto destVecType = fromElementsOp.getDest().getType();
2859 auto destEltType = destVecType.getElementType();
2860 if (!destEltType.isIntOrIndexOrFloat() && !isa<ComplexType>(destEltType))
2865 auto convertedElements = llvm::map_to_vector(elements, [&](
Attribute attr) {
2872OpFoldResult FromElementsOp::fold(FoldAdaptor adaptor) {
2889 if (!llvm::all_equal(fromElementsOp.getElements()))
2892 fromElementsOp, fromElementsOp.getType(),
2893 fromElementsOp.getElements().front());
2921 LogicalResult matchAndRewrite(FromElementsOp fromElements,
2925 if (fromElements.getType().getNumElements() == 1)
2936 for (
auto [insertIndex, element] :
2937 llvm::enumerate(fromElements.getElements())) {
2940 auto extractOp = element.getDefiningOp<vector::ExtractOp>();
2943 "element not from vector.extract");
2948 if (insertIndex == 0) {
2949 source = extractOp.getSource();
2950 }
else if (extractOp.getSource() != source) {
2952 "element from different vector");
2956 int64_t rank = position.size();
2957 assert(rank == source.getType().getRank() &&
2958 "scalar extract must have full rank position");
2969 if (insertIndex == 0) {
2970 const int64_t numElms = fromElements.getType().getNumElements();
2973 while (
index > 0 && position[
index - 1] == 0 &&
2974 numSuffixElms < numElms) {
2975 numSuffixElms *= source.getType().getDimSize(
index - 1);
2978 if (numSuffixElms != numElms) {
2980 fromElements,
"elements do not form a suffix of source");
2982 expectedPosition = llvm::to_vector(position);
2983 combinedPosition = position.drop_back(rank -
index);
2987 else if (expectedPosition != position) {
2989 fromElements,
"elements not in ascending order (static order)");
2991 increment(expectedPosition, source.getType().getShape());
2994 auto extracted = rewriter.
createOrFold<vector::ExtractOp>(
2995 fromElements.getLoc(), source, combinedPosition);
2998 fromElements, fromElements.getType(), extracted);
3006 for (
int dim : llvm::reverse(llvm::seq<int>(0,
indices.size()))) {
3025void BroadcastOp::inferResultRanges(ArrayRef<ConstantIntRanges> argRanges,
3027 setResultRanges(getResult(), argRanges.front());
3030std::optional<SmallVector<int64_t, 4>> BroadcastOp::getShapeForUnroll() {
3031 return llvm::to_vector<4>(getResultVectorType().
getShape());
3036static llvm::SetVector<int64_t>
3039 int64_t rankDiff = dstShape.size() - srcShape.size();
3042 for (
auto [s1, s2] :
3043 llvm::zip_equal(srcShape, dstShape.drop_front(rankDiff))) {
3045 assert(s1 == 1 &&
"expected \"dim-1\" broadcasting");
3053llvm::SetVector<int64_t> BroadcastOp::computeBroadcastedUnitDims() {
3055 auto srcVectorType = llvm::dyn_cast<VectorType>(getSourceType());
3058 return ::computeBroadcastedUnitDims(srcVectorType.getShape(),
3074Value BroadcastOp::createOrFoldBroadcastOp(
3075 OpBuilder &
b, Value value, ArrayRef<int64_t> dstShape,
3076 const llvm::SetVector<int64_t> &broadcastedDims) {
3077 assert(!dstShape.empty() &&
"unexpected empty dst shape");
3080 SmallVector<int64_t> checkShape;
3081 for (
int i = 0, e = dstShape.size(); i < e; ++i) {
3082 if (broadcastedDims.contains(i))
3084 checkShape.push_back(dstShape[i]);
3086 assert(broadcastedDims.size() == dstShape.size() - checkShape.size() &&
3087 "ill-formed broadcastedDims contains values not confined to "
3090 Location loc = value.
getLoc();
3092 VectorType srcVectorType = llvm::dyn_cast<VectorType>(value.
getType());
3093 VectorType dstVectorType = VectorType::get(dstShape, elementType);
3096 if (!srcVectorType) {
3097 assert(checkShape.empty() &&
3098 "ill-formed createOrFoldBroadcastOp arguments");
3099 return b.createOrFold<vector::BroadcastOp>(loc, dstVectorType, value);
3102 assert(srcVectorType.getShape().equals(checkShape) &&
3103 "ill-formed createOrFoldBroadcastOp arguments");
3113 SmallVector<int64_t> broadcastShape, permutation(dstShape.size(), -1);
3114 broadcastShape.reserve(dstShape.size());
3130 int64_t nextSrcShapeDim = broadcastedDims.size();
3131 for (int64_t i = 0, e = dstShape.size(); i < e; ++i) {
3132 if (broadcastedDims.contains(i)) {
3137 broadcastShape.push_back(dstShape[i]);
3138 permutation[i] = broadcastShape.size() - 1;
3144 permutation[i] = nextSrcShapeDim++;
3148 llvm::append_range(broadcastShape, srcVectorType.getShape());
3153 "unexpected \"dim-1\" broadcast");
3155 VectorType broadcastType = VectorType::get(broadcastShape, elementType);
3157 vector::BroadcastableToResult::Success &&
3158 "must be broadcastable");
3159 Value res =
b.createOrFold<vector::BroadcastOp>(loc, broadcastType, value);
3162 for (int64_t i = 0, e = permutation.size(); i < e; ++i)
3163 if (permutation[i] != i)
3164 return b.createOrFold<vector::TransposeOp>(loc, res, permutation);
3169LogicalResult BroadcastOp::verify() {
3170 std::pair<VectorDim, VectorDim> mismatchingDims;
3172 getSourceType(), getResultVectorType(), &mismatchingDims);
3173 if (res == BroadcastableToResult::Success)
3175 if (res == BroadcastableToResult::SourceRankHigher)
3176 return emitOpError(
"source rank higher than destination rank");
3177 if (res == BroadcastableToResult::DimensionMismatch) {
3179 << (mismatchingDims.first.isScalable ?
"[" :
"")
3180 << mismatchingDims.first.dim
3181 << (mismatchingDims.first.isScalable ?
"]" :
"") <<
" vs. "
3182 << (mismatchingDims.second.isScalable ?
"[" :
"")
3183 << mismatchingDims.second.dim
3184 << (mismatchingDims.second.isScalable ?
"]" :
"") <<
")";
3186 if (res == BroadcastableToResult::SourceTypeNotAVector)
3187 return emitOpError(
"source type is not a vector");
3188 llvm_unreachable(
"unexpected vector.broadcast op error");
3195 auto srcShapeCast = broadcastOp.getSource().getDefiningOp<ShapeCastOp>();
3199 VectorType srcType = srcShapeCast.getSourceVectorType();
3200 VectorType destType = broadcastOp.getResultVectorType();
3208 srcShapeCast.getResultVectorType().getShape();
3211 unsigned numTrailingDims = std::min(srcShape.size(), shapecastShape.size());
3212 if (!llvm::equal(srcShape.take_back(numTrailingDims),
3213 shapecastShape.take_back(numTrailingDims)))
3216 assert(all_of(srcShape.drop_back(numTrailingDims),
3217 [](
int64_t E) { return E == 1; }) &&
3218 all_of(shapecastShape.drop_back(numTrailingDims),
3219 [](
int64_t E) { return E == 1; }) &&
3220 "ill-formed shape_cast");
3222 broadcastOp.getSourceMutable().assign(srcShapeCast.getSource());
3226OpFoldResult BroadcastOp::fold(FoldAdaptor adaptor) {
3227 if (getSourceType() == getResultVectorType())
3232 if (!adaptor.getSource())
3234 auto vectorType = getResultVectorType();
3235 if (
auto attr = llvm::dyn_cast<IntegerAttr>(adaptor.getSource())) {
3236 if (vectorType.getElementType() != attr.getType())
3240 if (
auto attr = llvm::dyn_cast<FloatAttr>(adaptor.getSource())) {
3241 if (vectorType.getElementType() != attr.getType())
3245 if (
auto attr = llvm::dyn_cast<SplatElementsAttr>(adaptor.getSource()))
3255struct BroadcastFolder :
public OpRewritePattern<BroadcastOp> {
3258 LogicalResult matchAndRewrite(BroadcastOp broadcastOp,
3259 PatternRewriter &rewriter)
const override {
3260 auto srcBroadcast = broadcastOp.getSource().getDefiningOp<BroadcastOp>();
3264 broadcastOp.getResultVectorType(),
3265 srcBroadcast.getSource());
3278struct BroadcastToShapeCast final
3279 :
public OpRewritePattern<vector::BroadcastOp> {
3281 LogicalResult matchAndRewrite(vector::BroadcastOp
broadcast,
3282 PatternRewriter &rewriter)
const override {
3284 auto sourceType = dyn_cast<VectorType>(
broadcast.getSourceType());
3287 broadcast,
"source is a scalar, shape_cast doesn't support scalar");
3291 if (sourceType.getNumElements() != outType.getNumElements()) {
3293 broadcast,
"broadcast to a greater number of elements");
3303void BroadcastOp::getCanonicalizationPatterns(RewritePatternSet &results,
3304 MLIRContext *context) {
3305 results.
add<BroadcastFolder, BroadcastToShapeCast>(context);
3312LogicalResult ShuffleOp::verify() {
3313 VectorType resultType = getResultVectorType();
3314 VectorType v1Type = getV1VectorType();
3315 VectorType v2Type = getV2VectorType();
3317 int64_t resRank = resultType.getRank();
3318 int64_t v1Rank = v1Type.getRank();
3319 int64_t v2Rank = v2Type.getRank();
3320 bool wellFormed0DCase = v1Rank == 0 && v2Rank == 0 && resRank == 1;
3321 bool wellFormedNDCase = v1Rank == resRank && v2Rank == resRank;
3322 if (!wellFormed0DCase && !wellFormedNDCase)
3326 for (int64_t r = 1; r < v1Rank; ++r) {
3327 int64_t resDim = resultType.getDimSize(r);
3328 int64_t v1Dim = v1Type.getDimSize(r);
3329 int64_t v2Dim = v2Type.getDimSize(r);
3330 if (resDim != v1Dim || v1Dim != v2Dim)
3334 ArrayRef<int64_t> mask = getMask();
3335 int64_t maskLength = mask.size();
3336 if (maskLength <= 0)
3338 if (maskLength != resultType.getDimSize(0))
3341 int64_t indexSize = (v1Type.getRank() == 0 ? 1 : v1Type.getDimSize(0)) +
3342 (v2Type.getRank() == 0 ? 1 : v2Type.getDimSize(0));
3343 for (
auto [idx, maskPos] : llvm::enumerate(mask)) {
3345 return emitOpError(
"mask index #") << (idx + 1) <<
" out of range";
3351ShuffleOp::inferReturnTypes(MLIRContext *, std::optional<Location> loc,
3352 ShuffleOp::Adaptor adaptor,
3353 SmallVectorImpl<Type> &inferredReturnTypes) {
3354 auto v1Type = llvm::dyn_cast<VectorType>(adaptor.getV1().getType());
3358 auto v1Rank = v1Type.getRank();
3361 SmallVector<int64_t, 4> shape;
3362 shape.reserve(v1Rank);
3363 shape.push_back(std::max<size_t>(1, adaptor.getMask().size()));
3366 llvm::append_range(shape, v1Type.getShape().drop_front());
3367 inferredReturnTypes.push_back(
3368 VectorType::get(shape, v1Type.getElementType()));
3372template <
typename T>
3375 return idxArr.size() == width && llvm::all_of(idxArr, [&expected](T value) {
3376 return value == expected++;
3383 auto v1Type = op.getV1VectorType();
3384 auto v2Type = op.getV2VectorType();
3385 auto mask = op.getMask();
3398 if (!isV1Poison && !isV2Poison)
3401 int64_t v1Size = op.getV1VectorType().getDimSize(0);
3402 bool changed =
false;
3404 for (
int64_t &idx : newMask) {
3405 if (idx == ShuffleOp::kPoisonIndex)
3407 if ((isV1Poison && idx < v1Size) || (isV2Poison && idx >= v1Size)) {
3408 idx = ShuffleOp::kPoisonIndex;
3416 op.setMask(newMask);
3417 return op.getResult();
3426 return ub::PoisonAttr::get(context);
3433 auto v1Type = op.getV1VectorType();
3434 if (v1Type.getRank() != 1)
3446 auto v2DenseAttr = dyn_cast<DenseElementsAttr>(v2Attr);
3449 v2Elements = to_vector(v2DenseAttr.getValues<
Attribute>());
3450 poisonElement = v2Elements[0];
3453 auto v1DenseAttr = dyn_cast<DenseElementsAttr>(v1Attr);
3456 v1Elements = to_vector(v1DenseAttr.getValues<
Attribute>());
3457 poisonElement = v1Elements[0];
3462 int64_t v1Size = v1Type.getDimSize(0);
3463 for (
int64_t maskIdx : mask) {
3466 if (maskIdx == ShuffleOp::kPoisonIndex) {
3467 indexedElm = poisonElement;
3469 if (maskIdx < v1Size)
3470 indexedElm = isV1Poison ? poisonElement : v1Elements[maskIdx];
3472 indexedElm = isV2Poison ? poisonElement : v2Elements[maskIdx - v1Size];
3475 results.push_back(indexedElm);
3481OpFoldResult vector::ShuffleOp::fold(FoldAdaptor adaptor) {
3482 auto v1Type = getV1VectorType();
3484 assert(!v1Type.isScalable() && !getV2VectorType().isScalable() &&
3485 "Vector shuffle does not support scalable vectors");
3489 if (v1Type.getRank() == 0)
3497 Attribute v1Attr = adaptor.getV1(), v2Attr = adaptor.getV2();
3498 if (!v1Attr || !v2Attr)
3513struct Canonicalize0DShuffleOp :
public OpRewritePattern<ShuffleOp> {
3516 LogicalResult matchAndRewrite(ShuffleOp shuffleOp,
3517 PatternRewriter &rewriter)
const override {
3518 VectorType v1VectorType = shuffleOp.getV1VectorType();
3519 ArrayRef<int64_t> mask = shuffleOp.getMask();
3520 if (v1VectorType.getRank() > 0)
3522 if (mask.size() != 1)
3524 VectorType resType = VectorType::Builder(v1VectorType).setShape({1});
3542static Value getScalarSplatSource(Value value) {
3548 auto broadcast = dyn_cast<vector::BroadcastOp>(defOp);
3555 if (isa<VectorType>(
broadcast.getSourceType()))
3563class ShuffleSplat final :
public OpRewritePattern<ShuffleOp> {
3567 LogicalResult matchAndRewrite(ShuffleOp op,
3568 PatternRewriter &rewriter)
const override {
3569 Value splat = getScalarSplatSource(op.getV1());
3570 if (!splat || getScalarSplatSource(op.getV2()) != splat)
3580class ShuffleInterleave :
public OpRewritePattern<ShuffleOp> {
3584 LogicalResult matchAndRewrite(ShuffleOp op,
3585 PatternRewriter &rewriter)
const override {
3586 VectorType resultType = op.getResultVectorType();
3587 if (resultType.isScalable())
3589 op,
"ShuffleOp can't represent a scalable interleave");
3591 if (resultType.getRank() != 1)
3593 op,
"ShuffleOp can't represent an n-D interleave");
3595 VectorType sourceType = op.getV1VectorType();
3596 if (sourceType != op.getV2VectorType() ||
3597 sourceType.getNumElements() * 2 != resultType.getNumElements()) {
3599 op,
"ShuffleOp types don't match an interleave");
3602 ArrayRef<int64_t> shuffleMask = op.getMask();
3603 int64_t resultVectorSize = resultType.getNumElements();
3604 for (
int i = 0, e = resultVectorSize / 2; i < e; ++i) {
3605 int64_t maskValueA = shuffleMask[i * 2];
3606 int64_t maskValueB = shuffleMask[(i * 2) + 1];
3607 if (maskValueA != i || maskValueB != (resultVectorSize / 2) + i)
3609 "ShuffleOp mask not interleaving");
3625class FoldUnusedShuffleOperand final :
public OpRewritePattern<ShuffleOp> {
3629 LogicalResult matchAndRewrite(ShuffleOp op,
3630 PatternRewriter &rewriter)
const override {
3632 if (llvm::all_of(op.getMask(), [](int64_t mask) {
3633 return mask == ShuffleOp::kPoisonIndex;
3640 auto replaceOperandWithPoison = [&](OpOperand &operand) {
3643 Value poison = ub::PoisonOp::create(rewriter, op.getLoc(),
3652 int64_t leadingV1Size = op.getV1VectorType().getRank() > 0
3653 ? op.getV1VectorType().getDimSize(0)
3655 bool isV1Used = llvm::any_of(op.getMask(), [&](int64_t mask) {
3656 return mask != ShuffleOp::kPoisonIndex && mask < leadingV1Size;
3658 if (!isV1Used && succeeded(replaceOperandWithPoison(op.getV1Mutable())))
3662 bool isV2Used = llvm::any_of(op.getMask(), [&](int64_t mask) {
3663 return mask != ShuffleOp::kPoisonIndex && mask >= leadingV1Size;
3665 if (!isV2Used && succeeded(replaceOperandWithPoison(op.getV2Mutable())))
3673void ShuffleOp::getCanonicalizationPatterns(RewritePatternSet &results,
3674 MLIRContext *context) {
3675 results.
add<ShuffleSplat, ShuffleInterleave, Canonicalize0DShuffleOp,
3676 FoldUnusedShuffleOperand>(context);
3683void vector::InsertOp::inferResultRanges(ArrayRef<ConstantIntRanges> argRanges,
3685 setResultRanges(getResult(), argRanges[0].rangeUnion(argRanges[1]));
3688void vector::InsertOp::build(OpBuilder &builder, OperationState &
result,
3689 Value source, Value dest) {
3690 auto vectorTy = cast<VectorType>(dest.
getType());
3691 build(builder,
result, source, dest,
3692 SmallVector<int64_t>(vectorTy.getRank(), 0));
3695void vector::InsertOp::build(OpBuilder &builder, OperationState &
result,
3696 Value source, Value dest, int64_t position) {
3697 build(builder,
result, source, dest, ArrayRef<int64_t>{position});
3700void vector::InsertOp::build(OpBuilder &builder, OperationState &
result,
3701 Value source, Value dest, OpFoldResult position) {
3702 build(builder,
result, source, dest, ArrayRef<OpFoldResult>{position});
3705void vector::InsertOp::build(OpBuilder &builder, OperationState &
result,
3706 Value source, Value dest,
3707 ArrayRef<int64_t> position) {
3708 SmallVector<OpFoldResult> posVals;
3709 posVals.reserve(position.size());
3710 llvm::transform(position, std::back_inserter(posVals),
3712 build(builder,
result, source, dest, posVals);
3715void vector::InsertOp::build(OpBuilder &builder, OperationState &
result,
3716 Value source, Value dest,
3717 ArrayRef<OpFoldResult> position) {
3718 SmallVector<int64_t> staticPos;
3719 SmallVector<Value> dynamicPos;
3721 build(builder,
result, source, dest, dynamicPos,
3725LogicalResult InsertOp::verify() {
3726 if (
auto srcTy = dyn_cast<VectorType>(getValueToStoreType()))
3727 if (srcTy.getRank() == 0)
3729 "expected a scalar instead of a 0-d vector as the source operand");
3731 SmallVector<OpFoldResult> position = getMixedPosition();
3732 auto destVectorType = getDestVectorType();
3733 if (position.size() >
static_cast<unsigned>(destVectorType.getRank()))
3735 "expected position attribute of rank no greater than dest vector rank");
3736 auto srcVectorType = llvm::dyn_cast<VectorType>(getValueToStoreType());
3737 if (srcVectorType &&
3738 (
static_cast<unsigned>(srcVectorType.getRank()) + position.size() !=
3739 static_cast<unsigned>(destVectorType.getRank())))
3740 return emitOpError(
"expected position attribute rank + source rank to "
3741 "match dest vector rank");
3742 if (!srcVectorType &&
3743 (position.size() !=
static_cast<unsigned>(destVectorType.getRank())))
3745 "expected position attribute rank to match the dest vector rank");
3746 for (
auto [idx, pos] : llvm::enumerate(position)) {
3747 if (
auto attr = dyn_cast<Attribute>(pos)) {
3748 int64_t constIdx = cast<IntegerAttr>(attr).getInt();
3750 destVectorType.getDimSize(idx))) {
3751 return emitOpError(
"expected position attribute #")
3753 <<
" to be a non-negative integer smaller than the "
3755 "dest vector dimension";
3768 assert(positions.size() <= completePositions.size() &&
3769 "positions size must be less than or equal to destTy rank");
3770 copy(positions, completePositions.begin());
3778class InsertToBroadcast final :
public OpRewritePattern<InsertOp> {
3782 LogicalResult matchAndRewrite(InsertOp insertOp,
3783 PatternRewriter &rewriter)
const override {
3785 llvm::dyn_cast<VectorType>(insertOp.getValueToStoreType());
3786 if (!srcVecType || insertOp.getDestVectorType().getNumElements() !=
3787 srcVecType.getNumElements())
3790 insertOp, insertOp.getDestVectorType(), insertOp.getValueToStore());
3796class InsertSplatToSplat final :
public OpRewritePattern<InsertOp> {
3800 LogicalResult matchAndRewrite(InsertOp op,
3801 PatternRewriter &rewriter)
const override {
3803 Value splat = getScalarSplatSource(op.getValueToStore());
3804 if (!splat || getScalarSplatSource(op.getDest()) != splat)
3832class InsertChainFullyInitialized final :
public OpRewritePattern<InsertOp> {
3835 LogicalResult matchAndRewrite(InsertOp op,
3836 PatternRewriter &rewriter)
const override {
3838 VectorType destTy = op.getDestVectorType();
3839 if (destTy.isScalable())
3842 for (Operation *user : op.getResult().getUsers())
3843 if (
auto insertOp = dyn_cast<InsertOp>(user))
3844 if (insertOp.getDest() == op.getResult())
3847 InsertOp currentOp = op;
3848 SmallVector<InsertOp> chainInsertOps;
3851 if (currentOp.hasDynamicPosition())
3854 chainInsertOps.push_back(currentOp);
3855 currentOp = currentOp.getDest().getDefiningOp<InsertOp>();
3858 if (currentOp && !currentOp->hasOneUse())
3862 int64_t vectorSize = destTy.getNumElements();
3863 int64_t initializedCount = 0;
3864 SmallVector<bool> initializedDestIdxs(vectorSize,
false);
3865 SmallVector<int64_t> pendingInsertPos;
3866 SmallVector<int64_t> pendingInsertSize;
3867 SmallVector<Value> pendingInsertValues;
3869 for (
auto insertOp : chainInsertOps) {
3871 if (is_contained(insertOp.getStaticPosition(), InsertOp::kPoisonIndex))
3875 int64_t insertBeginPosition =
3880 int64_t insertSize = 1;
3881 if (
auto srcVectorType =
3882 llvm::dyn_cast<VectorType>(insertOp.getValueToStoreType()))
3883 insertSize = srcVectorType.getNumElements();
3885 assert(insertBeginPosition + insertSize <= vectorSize &&
3886 "insert would overflow the vector");
3888 for (
auto index : llvm::seq<int64_t>(insertBeginPosition,
3889 insertBeginPosition + insertSize)) {
3890 if (initializedDestIdxs[index])
3892 initializedDestIdxs[index] =
true;
3898 pendingInsertPos.push_back(insertBeginPosition);
3899 pendingInsertSize.push_back(insertSize);
3900 pendingInsertValues.push_back(insertOp.getValueToStore());
3902 if (initializedCount == vectorSize)
3907 if (initializedCount != vectorSize)
3910 SmallVector<Value> elements(vectorSize);
3911 for (
auto [insertBeginPosition, insertSize, valueToStore] :
3912 llvm::reverse(llvm::zip(pendingInsertPos, pendingInsertSize,
3913 pendingInsertValues))) {
3914 auto srcVectorType = llvm::dyn_cast<VectorType>(valueToStore.getType());
3916 if (!srcVectorType) {
3917 elements[insertBeginPosition] = valueToStore;
3921 Repeated<Type> elementToInsertTypes(insertSize,
3922 srcVectorType.getElementType());
3924 auto elementsToInsert = vector::ToElementsOp::create(
3925 rewriter, op.getLoc(), elementToInsertTypes, valueToStore);
3926 for (int64_t linearIdx = 0; linearIdx < insertSize; linearIdx++) {
3927 elements[insertBeginPosition + linearIdx] =
3928 elementsToInsert.getResult(linearIdx);
3942 int64_t maxVectorSizeFoldThreshold) {
3943 if (insertOp.hasDynamicPosition())
3946 auto denseDst = llvm::dyn_cast_if_present<DenseElementsAttr>(dstAttr);
3954 VectorType destTy = insertOp.getDestVectorType();
3955 if (destTy.isScalable())
3959 if (destTy.getNumElements() > maxVectorSizeFoldThreshold &&
3960 !insertOp->hasOneUse())
3965 if (is_contained(insertOp.getStaticPosition(), InsertOp::kPoisonIndex))
3972 Type destEltType = destTy.getElementType();
3976 if (
auto denseSource = llvm::dyn_cast<DenseElementsAttr>(srcAttr)) {
3977 for (
auto value : denseSource.getValues<
Attribute>())
3983 auto allValues = llvm::to_vector(denseDst.getValues<
Attribute>());
3984 copy(insertedValues, allValues.begin() + insertBeginPosition);
3993 auto destInsert = insertOp.getDest().
getDefiningOp<InsertOp>();
3997 if (insertOp.getMixedPosition() != destInsert.getMixedPosition())
4000 insertOp.
setOperand(1, destInsert.getDest());
4001 return insertOp.getResult();
4004void InsertOp::getCanonicalizationPatterns(RewritePatternSet &results,
4005 MLIRContext *context) {
4006 results.
add<InsertToBroadcast, BroadcastFolder, InsertSplatToSplat,
4007 InsertChainFullyInitialized>(context);
4010OpFoldResult InsertOp::fold(FoldAdaptor adaptor) {
4013 constexpr int64_t vectorSizeFoldThreshold = 256;
4017 if (getNumIndices() == 0 && getValueToStoreType() ==
getType())
4018 return getValueToStore();
4022 SmallVector<Value> operands = {getValueToStore(), getDest()};
4028 getContext(), adaptor.getStaticPosition(), kPoisonIndex))
4031 *
this, adaptor.getValueToStore(), adaptor.getDest(),
4032 vectorSizeFoldThreshold)) {
4036 return inplaceFolded;
4043void InsertStridedSliceOp::build(OpBuilder &builder, OperationState &
result,
4044 Value source, Value dest,
4045 ArrayRef<int64_t> offsets,
4046 ArrayRef<int64_t> strides) {
4047 result.addOperands({source, dest});
4051 result.addAttribute(InsertStridedSliceOp::getOffsetsAttrName(
result.name),
4053 result.addAttribute(InsertStridedSliceOp::getStridesAttrName(
result.name),
4058template <
typename OpType>
4062 StringRef attrName) {
4063 if (arrayAttr.size() >
shape.size())
4064 return op.emitOpError(
"expected ")
4065 << attrName <<
" attribute of rank no greater than vector rank";
4072template <
typename OpType>
4076 bool halfOpen =
true) {
4077 for (
auto attr : arrayAttr) {
4078 auto val = llvm::cast<IntegerAttr>(attr).getInt();
4082 if (val < min || val >= upper)
4083 return op.emitOpError(
"expected ") << attrName <<
" to be confined to ["
4084 <<
min <<
", " << upper <<
")";
4092template <
typename OpType>
4097 for (
auto [
index, attrDimPair] :
4098 llvm::enumerate(llvm::zip_first(arrayAttr,
shape))) {
4099 int64_t val = llvm::cast<IntegerAttr>(std::get<0>(attrDimPair)).getInt();
4103 if (val < min || val >=
max)
4104 return op.emitOpError(
"expected ")
4105 << attrName <<
" dimension " <<
index <<
" to be confined to ["
4106 <<
min <<
", " <<
max <<
")";
4116template <
typename OpType>
4121 assert(arrayAttr1.size() <=
shape.size());
4122 assert(arrayAttr2.size() <=
shape.size());
4123 for (
auto [
index, it] :
4124 llvm::enumerate(llvm::zip(arrayAttr1, arrayAttr2,
shape))) {
4125 auto val1 = llvm::cast<IntegerAttr>(std::get<0>(it)).getInt();
4126 auto val2 = llvm::cast<IntegerAttr>(std::get<1>(it)).getInt();
4130 if (val1 + val2 < 0 || val1 + val2 >=
max)
4131 return op.emitOpError(
"expected sum(")
4132 << attrName1 <<
", " << attrName2 <<
") dimension " <<
index
4133 <<
" to be confined to [" <<
min <<
", " <<
max <<
")";
4141 return IntegerAttr::get(IntegerType::get(context, 64), APInt(64, v));
4143 return ArrayAttr::get(context, llvm::to_vector<8>(attrs));
4146LogicalResult InsertStridedSliceOp::verify() {
4147 auto sourceVectorType = getSourceVectorType();
4148 auto destVectorType = getDestVectorType();
4149 auto offsets = getOffsetsAttr();
4150 auto strides = getStridesAttr();
4151 if (offsets.size() !=
static_cast<unsigned>(destVectorType.getRank()))
4153 "expected offsets of same size as destination vector rank");
4154 if (strides.size() !=
static_cast<unsigned>(sourceVectorType.getRank()))
4155 return emitOpError(
"expected strides of same size as source vector rank");
4156 if (sourceVectorType.getRank() > destVectorType.getRank())
4158 "expected source rank to be no greater than destination rank");
4160 auto sourceShape = sourceVectorType.getShape();
4161 auto destShape = destVectorType.getShape();
4162 SmallVector<int64_t, 4> sourceShapeAsDestShape(
4163 destShape.size() - sourceShape.size(), 0);
4164 sourceShapeAsDestShape.append(sourceShape.begin(), sourceShape.end());
4165 auto offName = InsertStridedSliceOp::getOffsetsAttrName();
4166 auto stridesName = InsertStridedSliceOp::getStridesAttrName();
4175 offName,
"source vector shape",
4179 unsigned rankDiff = destShape.size() - sourceShape.size();
4180 for (
unsigned idx = 0; idx < sourceShape.size(); ++idx) {
4181 if (sourceVectorType.getScalableDims()[idx] !=
4182 destVectorType.getScalableDims()[idx + rankDiff]) {
4183 return emitOpError(
"mismatching scalable flags (at source vector idx=")
4186 if (sourceVectorType.getScalableDims()[idx]) {
4187 auto sourceSize = sourceShape[idx];
4188 auto destSize = destShape[idx + rankDiff];
4189 if (sourceSize != destSize) {
4192 << (
" to match the corresponding base size from the input "
4194 << sourceSize << (
" vs ") << destSize << (
")");
4204class FoldInsertStridedSliceSplat final
4205 :
public OpRewritePattern<InsertStridedSliceOp> {
4209 LogicalResult matchAndRewrite(InsertStridedSliceOp insertStridedSliceOp,
4210 PatternRewriter &rewriter)
const override {
4212 auto dst = insertStridedSliceOp.getDest();
4213 auto splat = getScalarSplatSource(insertStridedSliceOp.getValueToStore());
4214 if (!splat || getScalarSplatSource(dst) != splat)
4217 rewriter.
replaceOp(insertStridedSliceOp, dst);
4224class FoldInsertStridedSliceOfExtract final
4225 :
public OpRewritePattern<InsertStridedSliceOp> {
4229 LogicalResult matchAndRewrite(InsertStridedSliceOp insertStridedSliceOp,
4230 PatternRewriter &rewriter)
const override {
4231 auto extractStridedSliceOp =
4232 insertStridedSliceOp.getValueToStore()
4233 .getDefiningOp<vector::ExtractStridedSliceOp>();
4235 if (!extractStridedSliceOp)
4238 if (extractStridedSliceOp.getOperand() != insertStridedSliceOp.getDest())
4242 if (extractStridedSliceOp.getStrides() !=
4243 insertStridedSliceOp.getStrides() ||
4244 extractStridedSliceOp.getOffsets() != insertStridedSliceOp.getOffsets())
4247 rewriter.
replaceOp(insertStridedSliceOp, insertStridedSliceOp.getDest());
4254class InsertStridedSliceConstantFolder final
4255 :
public OpRewritePattern<InsertStridedSliceOp> {
4261 static constexpr int64_t vectorSizeFoldThreshold = 256;
4263 LogicalResult matchAndRewrite(InsertStridedSliceOp op,
4264 PatternRewriter &rewriter)
const override {
4268 Attribute vectorDestCst;
4272 VectorType destTy = destVector.getType();
4273 if (destTy.isScalable())
4277 if (destTy.getNumElements() > vectorSizeFoldThreshold &&
4278 !destVector.hasOneUse())
4282 Attribute sourceCst;
4292 if (op.hasNonUnitStrides())
4295 VectorType sliceVecTy = sourceValue.getType();
4296 ArrayRef<int64_t> sliceShape = sliceVecTy.getShape();
4297 int64_t rankDifference = destTy.getRank() - sliceVecTy.getRank();
4298 SmallVector<int64_t, 4> offsets =
getI64SubArray(op.getOffsets());
4299 SmallVector<int64_t, 4> destStrides =
computeStrides(destTy.getShape());
4307 auto denseDest = llvm::cast<DenseElementsAttr>(vectorDestCst);
4308 auto denseSlice = llvm::cast<DenseElementsAttr>(sourceCst);
4309 auto sliceValuesIt = denseSlice.value_begin<Attribute>();
4310 auto newValues = llvm::to_vector(denseDest.getValues<Attribute>());
4311 SmallVector<int64_t> currDestPosition(offsets.begin(), offsets.end());
4312 MutableArrayRef<int64_t> currSlicePosition(
4313 currDestPosition.begin() + rankDifference, currDestPosition.end());
4314 ArrayRef<int64_t> sliceOffsets(offsets.begin() + rankDifference,
4317 int64_t linearizedPosition =
linearize(currDestPosition, destStrides);
4318 assert(linearizedPosition < destTy.getNumElements() &&
"Invalid index");
4319 assert(sliceValuesIt != denseSlice.value_end<Attribute>() &&
4320 "Invalid slice element");
4321 newValues[linearizedPosition] = *sliceValuesIt;
4334void vector::InsertStridedSliceOp::getCanonicalizationPatterns(
4335 RewritePatternSet &results, MLIRContext *context) {
4336 results.
add<FoldInsertStridedSliceSplat, FoldInsertStridedSliceOfExtract,
4337 InsertStridedSliceConstantFolder>(context);
4340OpFoldResult InsertStridedSliceOp::fold(FoldAdaptor adaptor) {
4341 if (getSourceVectorType() == getDestVectorType())
4342 return getValueToStore();
4351void OuterProductOp::build(OpBuilder &builder, OperationState &
result,
4352 Value
lhs, Value
rhs, Value acc) {
4357void OuterProductOp::print(OpAsmPrinter &p) {
4358 p <<
" " << getLhs() <<
", " << getRhs();
4360 p <<
", " << getAcc();
4363 p <<
" : " << getLhs().getType() <<
", " << getRhs().getType();
4366ParseResult OuterProductOp::parse(OpAsmParser &parser, OperationState &
result) {
4367 SmallVector<OpAsmParser::UnresolvedOperand, 3> operandsInfo;
4374 if (operandsInfo.size() < 2)
4376 "expected at least 2 operands");
4377 VectorType vLHS = llvm::dyn_cast<VectorType>(tLHS);
4378 VectorType vRHS = llvm::dyn_cast<VectorType>(tRHS);
4381 "expected vector type for operand #1");
4385 SmallVector<bool> scalableDimsRes{vLHS.getScalableDims()[0],
4386 vRHS.getScalableDims()[0]};
4387 resType = VectorType::get({vLHS.getDimSize(0), vRHS.getDimSize(0)},
4388 vLHS.getElementType(), scalableDimsRes);
4391 SmallVector<bool> scalableDimsRes{vLHS.getScalableDims()[0]};
4392 resType = VectorType::get({vLHS.getDimSize(0)}, vLHS.getElementType(),
4396 if (!
result.attributes.get(OuterProductOp::getKindAttrName(
result.name))) {
4397 result.attributes.append(
4398 OuterProductOp::getKindAttrName(
result.name),
4399 CombiningKindAttr::get(
result.getContext(),
4400 OuterProductOp::getDefaultKind()));
4406 (operandsInfo.size() > 2 &&
4411LogicalResult OuterProductOp::verify() {
4412 Type tRHS = getOperandTypeRHS();
4413 VectorType vLHS = getOperandVectorTypeLHS(),
4414 vRHS = llvm::dyn_cast<VectorType>(tRHS),
4415 vACC = getOperandVectorTypeACC(), vRES = getResultVectorType();
4417 if (vLHS.getRank() != 1)
4418 return emitOpError(
"expected 1-d vector for operand #1");
4422 if (vRHS.getRank() != 1)
4423 return emitOpError(
"expected 1-d vector for operand #2");
4424 if (vRES.getRank() != 2)
4426 if (vLHS.getDimSize(0) != vRES.getDimSize(0))
4427 return emitOpError(
"expected #1 operand dim to match result dim #1");
4428 if (vRHS.getDimSize(0) != vRES.getDimSize(1))
4429 return emitOpError(
"expected #2 operand dim to match result dim #2");
4430 if (vLHS.isScalable() && !vRHS.isScalable()) {
4434 "expected either both or only #2 operand dim to be scalable");
4438 if (vRES.getRank() != 1)
4440 if (vLHS.getDimSize(0) != vRES.getDimSize(0))
4441 return emitOpError(
"expected #1 operand dim to match result dim #1");
4444 if (vACC && vACC != vRES)
4445 return emitOpError(
"expected operand #3 of same type as result type");
4447 if (!getKindAttr()) {
4448 return emitOpError(
"expected 'kind' attribute of type CombiningKind (e.g. "
4449 "'vector.kind<add>')");
4454 return emitOpError(
"unsupported outerproduct type");
4463Type OuterProductOp::getExpectedMaskType() {
4464 auto vecType = this->getResultVectorType();
4465 return VectorType::get(vecType.getShape(),
4466 IntegerType::get(vecType.getContext(), 1),
4467 vecType.getScalableDims());
4481 assert(offsets.size() == sizes.size() && offsets.size() == strides.size());
4483 shape.reserve(vectorType.getRank());
4485 for (
unsigned e = offsets.size(); idx < e; ++idx)
4486 shape.push_back(llvm::cast<IntegerAttr>(sizes[idx]).getInt());
4487 for (
unsigned e = vectorType.getShape().size(); idx < e; ++idx)
4488 shape.push_back(vectorType.getShape()[idx]);
4490 return VectorType::get(
shape, vectorType.getElementType(),
4491 vectorType.getScalableDims());
4494void ExtractStridedSliceOp::build(OpBuilder &builder, OperationState &
result,
4495 Value source, ArrayRef<int64_t> offsets,
4496 ArrayRef<int64_t> sizes,
4497 ArrayRef<int64_t> strides) {
4498 result.addOperands(source);
4504 offsetsAttr, sizesAttr, stridesAttr));
4505 result.addAttribute(ExtractStridedSliceOp::getOffsetsAttrName(
result.name),
4507 result.addAttribute(ExtractStridedSliceOp::getSizesAttrName(
result.name),
4509 result.addAttribute(ExtractStridedSliceOp::getStridesAttrName(
result.name),
4513LogicalResult ExtractStridedSliceOp::verify() {
4514 auto type = getSourceVectorType();
4515 auto offsets = getOffsetsAttr();
4516 auto sizes = getSizesAttr();
4517 auto strides = getStridesAttr();
4518 if (offsets.size() != sizes.size() || offsets.size() != strides.size())
4520 "expected offsets, sizes and strides attributes of same size");
4522 auto shape = type.getShape();
4523 auto offName = getOffsetsAttrName();
4524 auto sizesName = getSizesAttrName();
4525 auto stridesName = getStridesAttrName();
4541 shape, offName, sizesName,
4546 offsets, sizes, strides);
4547 if (getResult().
getType() != resultType)
4548 return emitOpError(
"expected result type to be ") << resultType;
4550 for (
unsigned idx = 0; idx < sizes.size(); ++idx) {
4551 if (type.getScalableDims()[idx]) {
4552 auto inputDim = type.getShape()[idx];
4553 auto inputSize = llvm::cast<IntegerAttr>(sizes[idx]).getInt();
4554 if (inputDim != inputSize)
4557 << (
" to match the corresponding base size from the input "
4559 << inputSize << (
" vs ") << inputDim << (
")");
4572 auto getElement = [](
ArrayAttr array,
int idx) {
4573 return llvm::cast<IntegerAttr>(array[idx]).getInt();
4575 ArrayAttr extractOffsets = op.getOffsets();
4578 auto insertOp = op.getSource().getDefiningOp<InsertStridedSliceOp>();
4580 if (op.getSourceVectorType().getRank() !=
4581 insertOp.getSourceVectorType().getRank())
4583 ArrayAttr insertOffsets = insertOp.getOffsets();
4584 ArrayAttr insertStrides = insertOp.getStrides();
4587 if (extractOffsets.size() > insertOffsets.size())
4589 bool patialoverlap =
false;
4590 bool disjoint =
false;
4592 for (
unsigned dim = 0, e = extractOffsets.size(); dim < e; ++dim) {
4593 if (getElement(
extractStrides, dim) != getElement(insertStrides, dim))
4595 int64_t start = getElement(insertOffsets, dim);
4596 int64_t end = start + insertOp.getSourceVectorType().getDimSize(dim);
4597 int64_t offset = getElement(extractOffsets, dim);
4598 int64_t size = getElement(extractSizes, dim);
4600 if (start <= offset && offset < end) {
4603 if (offset + size > end)
4604 patialoverlap =
true;
4605 offsetDiffs.push_back(offset - start);
4612 if (!disjoint && !patialoverlap) {
4613 op.setOperand(insertOp.getValueToStore());
4616 op.setOffsetsAttr(
b.getI64ArrayAttr(offsetDiffs));
4622 insertOp = insertOp.getDest().getDefiningOp<InsertStridedSliceOp>();
4637 auto dense = llvm::dyn_cast_if_present<DenseElementsAttr>(foldInput);
4642 if (op.hasNonUnitStrides())
4645 VectorType sourceVecTy = op.getSourceVectorType();
4649 VectorType sliceVecTy = op.getType();
4651 int64_t rank = sliceVecTy.getRank();
4663 const auto denseValuesBegin = dense.value_begin<
Attribute>();
4665 sliceValues.reserve(sliceVecTy.getNumElements());
4669 assert(linearizedPosition < sourceVecTy.getNumElements() &&
4671 sliceValues.push_back(*(denseValuesBegin + linearizedPosition));
4672 }
while (succeeded(
incSlicePosition(currSlicePosition, sliceShape, offsets)));
4674 assert(
static_cast<int64_t>(sliceValues.size()) ==
4675 sliceVecTy.getNumElements() &&
4676 "Invalid number of slice elements");
4680OpFoldResult ExtractStridedSliceOp::fold(FoldAdaptor adaptor) {
4681 if (getSourceVectorType() == getResult().
getType())
4688 llvm::dyn_cast_if_present<SplatElementsAttr>(adaptor.getSource()))
4695void ExtractStridedSliceOp::getOffsets(SmallVectorImpl<int64_t> &results) {
4717class StridedSliceFolder final
4718 :
public OpRewritePattern<ExtractStridedSliceOp> {
4720 using OpRewritePattern<ExtractStridedSliceOp>::OpRewritePattern;
4722 LogicalResult matchAndRewrite(ExtractStridedSliceOp secondOp,
4723 PatternRewriter &rewriter)
const override {
4724 auto firstOp = secondOp.getSource().getDefiningOp<ExtractStridedSliceOp>();
4728 if (secondOp.hasNonUnitStrides() || firstOp.hasNonUnitStrides())
4731 SmallVector<int64_t> firstOffsets =
getI64SubArray(firstOp.getOffsets());
4732 SmallVector<int64_t> firstSizes =
getI64SubArray(firstOp.getSizes());
4733 SmallVector<int64_t> secondOffsets =
getI64SubArray(secondOp.getOffsets());
4734 SmallVector<int64_t> secondSizes =
getI64SubArray(secondOp.getSizes());
4736 unsigned newRank = std::max(firstOffsets.size(), secondOffsets.size());
4737 SmallVector<int64_t> combinedOffsets(newRank, 0);
4738 SmallVector<int64_t> combinedSizes(newRank);
4739 ArrayRef<int64_t> firstSourceShape =
4740 firstOp.getSourceVectorType().getShape();
4741 for (
unsigned i = 0; i < newRank; ++i) {
4742 int64_t off1 = (i < firstOffsets.size()) ? firstOffsets[i] : 0;
4743 int64_t off2 = (i < secondOffsets.size()) ? secondOffsets[i] : 0;
4744 combinedOffsets[i] = off1 + off2;
4746 if (i < secondSizes.size()) {
4747 combinedSizes[i] = secondSizes[i];
4748 }
else if (i < firstSizes.size()) {
4749 combinedSizes[i] = firstSizes[i];
4751 combinedSizes[i] = firstSourceShape[i];
4755 SmallVector<int64_t> combinedStrides(newRank, 1);
4757 secondOp, firstOp.getSource(), combinedOffsets, combinedSizes,
4775class StridedSliceCreateMaskFolder final
4776 :
public OpRewritePattern<ExtractStridedSliceOp> {
4780 LogicalResult matchAndRewrite(ExtractStridedSliceOp extractStridedSliceOp,
4781 PatternRewriter &rewriter)
const override {
4782 Location loc = extractStridedSliceOp.getLoc();
4786 extractStridedSliceOp.getSource().getDefiningOp<CreateMaskOp>();
4790 if (extractStridedSliceOp.hasNonUnitStrides())
4793 SmallVector<Value> maskDimSizes(createMaskOp.getOperands());
4795 SmallVector<int64_t> sliceOffsets;
4798 SmallVector<int64_t> sliceSizes;
4802 SmallVector<Value> sliceMaskDimSizes;
4803 sliceMaskDimSizes.reserve(maskDimSizes.size());
4807 for (
auto [maskDimSize, sliceOffset, sliceSize] :
4808 llvm::zip(maskDimSizes, sliceOffsets, sliceSizes)) {
4812 IntegerAttr offsetAttr =
4814 Value offset = arith::ConstantOp::create(rewriter, loc, offsetAttr);
4815 Value sliceMaskDimSize =
4816 arith::SubIOp::create(rewriter, loc, maskDimSize, offset);
4817 sliceMaskDimSizes.push_back(sliceMaskDimSize);
4822 llvm::drop_begin(maskDimSizes, sliceMaskDimSizes.size()));
4826 extractStridedSliceOp, extractStridedSliceOp.getResult().
getType(),
4834class StridedSliceConstantMaskFolder final
4835 :
public OpRewritePattern<ExtractStridedSliceOp> {
4839 LogicalResult matchAndRewrite(ExtractStridedSliceOp extractStridedSliceOp,
4840 PatternRewriter &rewriter)
const override {
4843 auto *defOp = extractStridedSliceOp.getSource().getDefiningOp();
4844 auto constantMaskOp = dyn_cast_or_null<ConstantMaskOp>(defOp);
4845 if (!constantMaskOp)
4848 if (extractStridedSliceOp.hasNonUnitStrides())
4851 ArrayRef<int64_t> maskDimSizes = constantMaskOp.getMaskDimSizes();
4853 SmallVector<int64_t> sliceOffsets;
4856 SmallVector<int64_t> sliceSizes;
4860 SmallVector<int64_t> sliceMaskDimSizes;
4861 sliceMaskDimSizes.reserve(maskDimSizes.size());
4862 for (
auto [maskDimSize, sliceOffset, sliceSize] :
4863 llvm::zip(maskDimSizes, sliceOffsets, sliceSizes)) {
4864 int64_t sliceMaskDimSize = std::max(
4865 static_cast<int64_t
>(0),
4866 std::min(sliceOffset + sliceSize, maskDimSize) - sliceOffset);
4867 sliceMaskDimSizes.push_back(sliceMaskDimSize);
4870 if (sliceMaskDimSizes.size() < maskDimSizes.size())
4871 for (
size_t i = sliceMaskDimSizes.size(); i < maskDimSizes.size(); ++i)
4872 sliceMaskDimSizes.push_back(maskDimSizes[i]);
4875 if (llvm::is_contained(sliceMaskDimSizes, 0))
4876 sliceMaskDimSizes.assign(maskDimSizes.size(), 0);
4881 extractStridedSliceOp, extractStridedSliceOp.getResult().
getType(),
4889class StridedSliceBroadcast final
4890 :
public OpRewritePattern<ExtractStridedSliceOp> {
4894 LogicalResult matchAndRewrite(ExtractStridedSliceOp op,
4895 PatternRewriter &rewriter)
const override {
4901 unsigned srcRank = srcVecType ? srcVecType.getRank() : 0;
4902 auto dstVecType = llvm::cast<VectorType>(op.getType());
4903 unsigned dstRank = dstVecType.getRank();
4904 unsigned rankDiff = dstRank - srcRank;
4908 bool needsSlice =
false;
4909 for (
unsigned i = 0; i < srcRank; i++) {
4910 if (srcVecType.getDimSize(i) != 1 &&
4911 srcVecType.getDimSize(i) != dstVecType.getDimSize(i + rankDiff)) {
4918 SmallVector<int64_t> offsets =
4920 SmallVector<int64_t> sizes =
4922 for (
unsigned i = 0; i < srcRank; i++) {
4923 if (srcVecType.getDimSize(i) == 1) {
4931 source = ExtractStridedSliceOp::create(
4932 rewriter, op->getLoc(), source, offsets, sizes,
4941class StridedSliceSplat final :
public OpRewritePattern<ExtractStridedSliceOp> {
4945 LogicalResult matchAndRewrite(ExtractStridedSliceOp op,
4946 PatternRewriter &rewriter)
const override {
4948 Value splat = getScalarSplatSource(op.getSource());
4972class ContiguousExtractStridedSliceToExtract final
4973 :
public OpRewritePattern<ExtractStridedSliceOp> {
4977 LogicalResult matchAndRewrite(ExtractStridedSliceOp op,
4978 PatternRewriter &rewriter)
const override {
4979 if (op.hasNonUnitStrides())
4981 Value source = op.getOperand();
4982 auto sourceType = cast<VectorType>(source.
getType());
4983 if (sourceType.isScalable() || sourceType.getRank() == 0)
4992 for (numOffsets = sizes.size(); numOffsets > 0; --numOffsets) {
4993 if (sizes[numOffsets - 1] != sourceType.getDimSize(numOffsets - 1))
5000 if (numOffsets == 0)
5005 if (numOffsets == sourceType.getRank() &&
5006 static_cast<int>(sizes.size()) == sourceType.getRank())
5010 for (
int i = 0; i < numOffsets; ++i) {
5018 while (numOffsets <
static_cast<int>(sizes.size()) - 1 &&
5019 sizes[numOffsets] == 1) {
5024 auto extractOffsets = ArrayRef(offsets).take_front(numOffsets);
5025 Value extract = vector::ExtractOp::create(rewriter, op->getLoc(), source,
5034void ExtractStridedSliceOp::getCanonicalizationPatterns(
5035 RewritePatternSet &results, MLIRContext *context) {
5038 results.
add<StridedSliceFolder, StridedSliceCreateMaskFolder,
5039 StridedSliceConstantMaskFolder, StridedSliceBroadcast,
5040 StridedSliceSplat, ContiguousExtractStridedSliceToExtract>(
5050void TransferReadOp::build(OpBuilder &builder, OperationState &
result,
5051 VectorType vectorType, Value source,
5053 AffineMapAttr permutationMapAttr,
5056 Type elemType = llvm::cast<ShapedType>(source.
getType()).getElementType();
5058 padding = ub::PoisonOp::create(builder,
result.location, elemType);
5061 build(builder,
result, vectorType, source,
indices, permutationMapAttr,
5062 *padding, Value(), inBoundsAttr);
5070void TransferReadOp::build(OpBuilder &builder, OperationState &
result,
5071 VectorType vectorType, Value source,
5073 AffineMap permutationMap,
5074 std::optional<ArrayRef<bool>> inBounds) {
5075 if (!permutationMap)
5077 llvm::cast<ShapedType>(source.
getType()), vectorType);
5078 auto permutationMapAttr = AffineMapAttr::get(permutationMap);
5079 auto inBoundsAttr = (inBounds && !inBounds.value().empty())
5082 SmallVector<bool>(vectorType.getRank(),
false));
5084 build(builder,
result, vectorType, source,
indices, padding,
5085 permutationMapAttr, inBoundsAttr);
5091void TransferReadOp::build(OpBuilder &builder, OperationState &
result,
5092 VectorType vectorType, Value source,
5094 std::optional<ArrayRef<bool>> inBounds) {
5096 build(builder,
result, vectorType, source,
indices, padding,
5097 AffineMap(), inBounds);
5100template <
typename EmitFun>
5104 for (
auto expr : permutationMap.
getResults()) {
5105 auto dim = dyn_cast<AffineDimExpr>(expr);
5106 auto zero = dyn_cast<AffineConstantExpr>(expr);
5108 if (zero.getValue() != 0) {
5110 "requires a projected permutation_map (at most one dim or the zero "
5111 "constant can appear in each result)");
5116 return emitOpError(
"requires a projected permutation_map (at most one "
5117 "dim or the zero constant can appear in each result)");
5119 if (seen[dim.getPosition()]) {
5121 "requires a permutation_map that is a permutation (found one dim "
5122 "used more than once)");
5124 seen[dim.getPosition()] =
true;
5131 VectorType vectorType, VectorType maskType,
5132 VectorType inferredMaskType,
AffineMap permutationMap,
5134 if (op->hasAttr(
"masked")) {
5135 return op->emitOpError(
"masked attribute has been removed. "
5136 "Use in_bounds instead.");
5139 if (!llvm::isa<MemRefType, RankedTensorType>(shapedType))
5140 return op->emitOpError(
5141 "requires source to be a memref or ranked tensor type");
5143 auto elementType = shapedType.getElementType();
5145 if (
auto vectorElementType = llvm::dyn_cast<VectorType>(elementType)) {
5147 unsigned sourceVecSize =
5149 vectorElementType.getShape().back();
5150 unsigned resultVecSize =
5152 vectorType.getShape().back();
5153 if (resultVecSize % sourceVecSize != 0)
5154 return op->emitOpError(
5155 "requires the bitwidth of the minor 1-D vector to be an integral "
5156 "multiple of the bitwidth of the minor 1-D vector of the source");
5158 unsigned sourceVecEltRank = vectorElementType.getRank();
5159 unsigned resultVecRank = vectorType.getRank();
5160 if (sourceVecEltRank > resultVecRank)
5161 return op->emitOpError(
5162 "requires source vector element and vector result ranks to match.");
5163 unsigned rankOffset = resultVecRank - sourceVecEltRank;
5166 return op->emitOpError(
"requires a permutation_map with result dims of "
5167 "the same rank as the vector type");
5170 return op->emitOpError(
"does not support masks with vector element type");
5173 unsigned minorSize =
5174 vectorType.getRank() == 0 ? 1 : vectorType.getShape().back();
5175 unsigned resultVecSize =
5178 return op->emitOpError(
5179 "requires the bitwidth of the minor 1-D vector to be an integral "
5180 "multiple of the bitwidth of the source element type");
5184 return op->emitOpError(
"requires a permutation_map with result dims of "
5185 "the same rank as the vector type");
5189 return op->emitOpError(
"requires permutation_map without symbols");
5191 if (permutationMap.
getNumInputs() != shapedType.getRank())
5192 return op->emitOpError(
"requires a permutation_map with input dims of the "
5193 "same rank as the source type");
5195 if (maskType && maskType != inferredMaskType)
5196 return op->emitOpError(
"inferred mask type (")
5197 << inferredMaskType <<
") and mask operand type (" << maskType
5201 return op->emitOpError(
"expects the in_bounds attr of same rank "
5202 "as permutation_map results: ")
5203 << AffineMapAttr::get(permutationMap)
5204 <<
" vs inBounds of size: " << inBounds.size();
5211 elidedAttrs.push_back(TransferReadOp::getOperandSegmentSizeAttr());
5212 if (op.getPermutationMap().isMinorIdentity())
5213 elidedAttrs.push_back(op.getPermutationMapAttrName());
5215 if (llvm::none_of(op.getInBoundsValues(), [](
bool b) { return b; }))
5216 elidedAttrs.push_back(op.getInBoundsAttrName());
5220void TransferReadOp::print(OpAsmPrinter &p) {
5223 p <<
", " << getMask();
5230 auto i1Type = IntegerType::get(permMap.
getContext(), 1);
5232 assert(invPermMap &&
"Inversed permutation map couldn't be computed");
5237 if (maskShape.empty())
5238 maskShape.push_back(1);
5243 return VectorType::get(maskShape, i1Type, scalableDims);
5260 if (hasMask.succeeded()) {
5267 if (types.size() != 2)
5268 return parser.
emitError(typesLoc,
"requires two types");
5270 auto shapedType = llvm::dyn_cast<ShapedType>(types[0]);
5271 if (!shapedType || !llvm::isa<MemRefType, RankedTensorType>(shapedType))
5272 return parser.
emitError(typesLoc,
"requires memref or ranked tensor type");
5273 VectorType vectorType = llvm::dyn_cast<VectorType>(types[1]);
5275 return parser.
emitError(typesLoc,
"requires vector type");
5276 auto permMapAttrName = TransferReadOp::getPermutationMapAttrName(
result.name);
5280 if (shapedType.getRank() <
5283 "expected a custom permutation_map when "
5284 "rank(source) != rank(destination)");
5286 result.attributes.set(permMapAttrName, AffineMapAttr::get(permMap));
5288 permMap = llvm::cast<AffineMapAttr>(permMapAttr).getValue();
5290 auto inBoundsAttrName = TransferReadOp::getInBoundsAttrName(
result.name);
5291 Attribute inBoundsAttr =
result.attributes.get(inBoundsAttrName);
5292 if (!inBoundsAttr) {
5293 result.addAttribute(inBoundsAttrName,
5302 if (hasMask.succeeded()) {
5303 if (llvm::dyn_cast<VectorType>(shapedType.getElementType()))
5305 maskInfo.
location,
"does not support masks with vector element type");
5308 "expected the same rank for the vector and the "
5309 "results of the permutation map");
5317 result.addAttribute(TransferReadOp::getOperandSegmentSizeAttr(),
5319 {1, static_cast<int32_t>(indexInfo.size()), 1,
5320 static_cast<int32_t>(hasMask.succeeded())}));
5324LogicalResult TransferReadOp::verify() {
5326 ShapedType shapedType = getShapedType();
5328 VectorType maskType = getMaskType();
5329 auto paddingType = getPadding().getType();
5330 auto permutationMap = getPermutationMap();
5331 VectorType inferredMaskType =
5334 auto sourceElementType = shapedType.getElementType();
5336 if (
static_cast<int64_t
>(
getIndices().size()) != shapedType.getRank())
5337 return emitOpError(
"requires ") << shapedType.getRank() <<
" indices";
5340 shapedType, vectorType, maskType,
5341 inferredMaskType, permutationMap, getInBounds())))
5344 if (
auto sourceVectorElementType =
5345 llvm::dyn_cast<VectorType>(sourceElementType)) {
5348 if (sourceVectorElementType != paddingType)
5350 "requires source element type and padding type to match.");
5354 if (!VectorType::isValidElementType(paddingType))
5355 return emitOpError(
"requires valid padding vector elemental type");
5358 if (paddingType != sourceElementType)
5360 "requires formal padding and source of the same elemental type");
5371Type TransferReadOp::getExpectedMaskType() {
5378VectorType TransferReadOp::getVectorType() {
5379 return cast<VectorType>(getVector().
getType());
5382template <
typename TransferOp>
5386 if (op.getShapedType().isDynamicDim(indicesIdx))
5390 if (op.getVectorType().getScalableDims()[resultIdx])
5394 if (!cstOp.has_value())
5397 int64_t sourceSize = op.getShapedType().getDimSize(indicesIdx);
5398 int64_t vectorSize = op.getVectorType().getDimSize(resultIdx);
5400 return cstOp.value() + vectorSize <= sourceSize;
5403template <
typename TransferOp>
5407 if (op.getTransferRank() == 0)
5410 bool changed =
false;
5412 newInBounds.reserve(op.getTransferRank());
5417 for (
unsigned i = 0; i < op.getTransferRank(); ++i) {
5419 if (op.isDimInBounds(i)) {
5420 newInBounds.push_back(
true);
5425 bool inBounds =
false;
5426 auto dimExpr = dyn_cast<AffineDimExpr>(permutationMap.
getResult(i));
5429 dimExpr.getPosition());
5430 nonBcastDims.push_back(i);
5433 newInBounds.push_back(inBounds);
5435 changed |= inBounds;
5441 bool allNonBcastDimsInBounds = llvm::all_of(
5442 nonBcastDims, [&newInBounds](
unsigned idx) {
return newInBounds[idx]; });
5443 if (allNonBcastDimsInBounds) {
5445 changed |= !newInBounds[idx];
5446 newInBounds[idx] =
true;
5454 op.setInBoundsAttr(
b.getBoolArrayAttr(newInBounds));
5458template <
typename TransferOp>
5460 auto mask = op.getMask();
5467 op.getMaskMutable().clear();
5475template <
typename TransferOp>
5477 VectorType vecType = op.getVectorType();
5478 if (vecType.getRank() != 1 || vecType.getShape()[0] != 1 ||
5479 vecType.isScalable())
5486 int64_t srcRank = op.getShapedType().getRank();
5492 op.setPermutationMapAttr(AffineMapAttr::get(minorIdentity));
5506static Value foldRAW(TransferReadOp readOp) {
5507 if (!llvm::isa<RankedTensorType>(readOp.getShapedType()))
5509 auto defWrite = readOp.getBase().getDefiningOp<vector::TransferWriteOp>();
5512 return defWrite.getVector();
5514 cast<VectorTransferOpInterface>(defWrite.getOperation()),
5515 cast<VectorTransferOpInterface>(readOp.getOperation())))
5517 defWrite = defWrite.getBase().getDefiningOp<vector::TransferWriteOp>();
5522OpFoldResult TransferReadOp::fold(FoldAdaptor) {
5523 if (Value vec = foldRAW(*
this))
5536 return OpFoldResult();
5539std::optional<SmallVector<int64_t, 4>> TransferReadOp::getShapeForUnroll() {
5543void TransferReadOp::getEffects(
5544 SmallVectorImpl<SideEffects::EffectInstance<MemoryEffects::Effect>>
5546 if (llvm::isa<MemRefType>(getShapedType()))
5547 effects.emplace_back(MemoryEffects::Read::get(), &getBaseMutable(),
5548 SideEffects::DefaultResource::get());
5552 if (hasPureTensorSemantics())
5559static AffineMap inverseWithUnusedDims(AffineMap map) {
5561 "expected a projected permutation map");
5566 int64_t pos = cast<AffineDimExpr>(
result).getPosition();
5596struct TransferReadAfterWriteToBroadcast
5597 :
public OpRewritePattern<TransferReadOp> {
5600 LogicalResult matchAndRewrite(TransferReadOp readOp,
5601 PatternRewriter &rewriter)
const override {
5602 auto defWrite = readOp.getBase().getDefiningOp<vector::TransferWriteOp>();
5606 if (!readOp.hasPureTensorSemantics() || !defWrite.hasPureTensorSemantics())
5610 if (readOp.getMask() || defWrite.getMask())
5613 if (readOp.getIndices() != defWrite.getIndices())
5616 if (readOp.hasOutOfBoundsDim() || defWrite.hasOutOfBoundsDim())
5620 if (readOp.getTransferChunkAccessed() !=
5621 defWrite.getTransferChunkAccessed())
5628 AffineMap readMap = readOp.getPermutationMap();
5629 AffineMap writeMap = defWrite.getPermutationMap();
5630 AffineMap invWriteMap = inverseWithUnusedDims(writeMap);
5631 AffineMap composedMap = readMap.
compose(invWriteMap);
5645 int64_t numBroadcastedDims = broadcastedDims.size();
5646 auto invPerm = llvm::to_vector_of<int64_t>(broadcastedDims);
5648 for (
auto [idx, expr] : llvm::enumerate(composedMap.
getResults())) {
5649 if (
auto dim = dyn_cast<AffineDimExpr>(expr)) {
5650 int64_t effectiveDim = dim.getPosition() + numBroadcastedDims;
5651 invPerm[effectiveDim] = idx;
5656 VectorType readVecTy = readOp.getVectorType();
5658 auto broadcastedVecTy =
5660 readVecTy.getElementType(),
5663 Value vec = defWrite.getVector();
5664 Location loc = readOp.getLoc();
5665 vec = vector::BroadcastOp::create(rewriter, loc, broadcastedVecTy, vec);
5672void TransferReadOp::getCanonicalizationPatterns(RewritePatternSet &results,
5673 MLIRContext *context) {
5674 results.
add<TransferReadAfterWriteToBroadcast>(context);
5677FailureOr<std::optional<SmallVector<Value>>>
5678TransferReadOp::bubbleDownCasts(OpBuilder &builder) {
5679 if (!hasPureBufferSemantics())
5690void TransferWriteOp::build(OpBuilder &builder, OperationState &
result,
5692 AffineMapAttr permutationMapAttr,
5695 Type resultType = llvm::dyn_cast<RankedTensorType>(dest.
getType());
5696 build(builder,
result, resultType, vector, dest,
indices, permutationMapAttr,
5697 mask, inBoundsAttr);
5701void TransferWriteOp::build(OpBuilder &builder, OperationState &
result,
5703 AffineMapAttr permutationMapAttr,
5705 build(builder,
result, vector, dest,
indices, permutationMapAttr,
5706 Value(), inBoundsAttr);
5711void TransferWriteOp::build(OpBuilder &builder, OperationState &
result,
5713 AffineMap permutationMap,
5714 std::optional<ArrayRef<bool>> inBounds) {
5715 if (!permutationMap)
5718 llvm::cast<VectorType>(vector.
getType()));
5719 auto permutationMapAttr = AffineMapAttr::get(permutationMap);
5721 (inBounds && !inBounds.value().empty())
5724 llvm::cast<VectorType>(vector.
getType()).getRank(),
false));
5725 build(builder,
result, vector, dest,
indices, permutationMapAttr,
5726 Value(), inBoundsAttr);
5731void TransferWriteOp::build(OpBuilder &builder, OperationState &
result,
5733 std::optional<ArrayRef<bool>> inBounds) {
5738ParseResult TransferWriteOp::parse(OpAsmParser &parser,
5739 OperationState &
result) {
5742 OpAsmParser::UnresolvedOperand vectorInfo, sourceInfo;
5743 SmallVector<OpAsmParser::UnresolvedOperand, 8> indexInfo;
5744 SmallVector<Type, 2> types;
5745 OpAsmParser::UnresolvedOperand maskInfo;
5751 if (hasMask.succeeded() && parser.
parseOperand(maskInfo))
5756 if (types.size() != 2)
5757 return parser.
emitError(typesLoc,
"requires two types");
5759 VectorType vectorType = llvm::dyn_cast<VectorType>(types[0]);
5761 return parser.
emitError(typesLoc,
"requires vector type");
5762 ShapedType shapedType = llvm::dyn_cast<ShapedType>(types[1]);
5763 if (!shapedType || !llvm::isa<MemRefType, RankedTensorType>(shapedType))
5764 return parser.
emitError(typesLoc,
"requires memref or ranked tensor type");
5765 auto permMapAttrName =
5766 TransferWriteOp::getPermutationMapAttrName(
result.name);
5767 auto permMapAttr =
result.attributes.get(permMapAttrName);
5770 if (shapedType.getRank() <
5773 "expected a custom permutation_map when "
5774 "rank(source) != rank(destination)");
5776 result.attributes.set(permMapAttrName, AffineMapAttr::get(permMap));
5778 permMap = llvm::cast<AffineMapAttr>(permMapAttr).getValue();
5780 auto inBoundsAttrName = TransferWriteOp::getInBoundsAttrName(
result.name);
5781 Attribute inBoundsAttr =
result.attributes.get(inBoundsAttrName);
5782 if (!inBoundsAttr) {
5783 result.addAttribute(inBoundsAttrName,
5791 if (hasMask.succeeded()) {
5792 if (llvm::dyn_cast<VectorType>(shapedType.getElementType()))
5794 maskInfo.
location,
"does not support masks with vector element type");
5797 "expected the same rank for the vector and the "
5798 "results of the permutation map");
5804 result.addAttribute(TransferWriteOp::getOperandSegmentSizeAttr(),
5806 {1, 1, static_cast<int32_t>(indexInfo.size()),
5807 static_cast<int32_t>(hasMask.succeeded())}));
5808 return failure(llvm::isa<RankedTensorType>(shapedType) &&
5812void TransferWriteOp::print(OpAsmPrinter &p) {
5815 p <<
", " << getMask();
5820LogicalResult TransferWriteOp::verify() {
5822 ShapedType shapedType = getShapedType();
5824 VectorType maskType = getMaskType();
5825 auto permutationMap = getPermutationMap();
5826 VectorType inferredMaskType =
5830 if (llvm::size(
getIndices()) != shapedType.getRank())
5831 return emitOpError(
"requires ") << shapedType.getRank() <<
" indices";
5835 if (hasBroadcastDim())
5836 return emitOpError(
"should not have broadcast dimensions");
5839 shapedType, vectorType, maskType,
5840 inferredMaskType, permutationMap, getInBounds())))
5853Type TransferWriteOp::getExpectedMaskType() {
5860Value TransferWriteOp::getVector() {
return getOperand(0); }
5861VectorType TransferWriteOp::getVectorType() {
5862 return cast<VectorType>(getValueToStore().
getType());
5885static LogicalResult foldReadInitWrite(TransferWriteOp write,
5886 ArrayRef<Attribute>,
5887 SmallVectorImpl<OpFoldResult> &results) {
5889 if (write.getTransferRank() == 0)
5891 auto rankedTensorType =
5892 llvm::dyn_cast<RankedTensorType>(write.getBase().getType());
5894 if (!rankedTensorType)
5897 auto read = write.getVector().getDefiningOp<vector::TransferReadOp>();
5901 if (read.getTransferRank() == 0)
5904 if (!read.getPermutationMap().isMinorIdentity() ||
5905 !write.getPermutationMap().isMinorIdentity())
5908 if (read.getTransferRank() != write.getTransferRank())
5911 if (read.hasOutOfBoundsDim() || write.hasOutOfBoundsDim())
5914 if (read.getMask() || write.getMask())
5917 if (read.getBase().getType() != rankedTensorType)
5920 if (read.getVectorType() != write.getVectorType())
5923 if (read.getVectorType().getShape() != rankedTensorType.getShape())
5926 auto isNotConstantZero = [](Value v) {
5928 return !cstOp.has_value() || cstOp.value() != 0;
5930 if (llvm::any_of(read.getIndices(), isNotConstantZero) ||
5931 llvm::any_of(write.getIndices(), isNotConstantZero))
5934 results.push_back(read.getBase());
5938static bool checkSameValueWAR(vector::TransferReadOp read,
5939 vector::TransferWriteOp write) {
5940 return read.getBase() == write.getBase() &&
5941 read.getIndices() == write.getIndices() &&
5942 read.getPermutationMap() == write.getPermutationMap() &&
5943 read.getVectorType() == write.getVectorType() && !read.getMask() &&
5960static LogicalResult foldWAR(TransferWriteOp write,
5961 SmallVectorImpl<OpFoldResult> &results) {
5962 if (!llvm::isa<RankedTensorType>(write.getBase().getType()))
5964 auto read = write.getVector().getDefiningOp<vector::TransferReadOp>();
5968 if (!checkSameValueWAR(read, write))
5970 results.push_back(read.getBase());
5974LogicalResult TransferWriteOp::fold(FoldAdaptor adaptor,
5975 SmallVectorImpl<OpFoldResult> &results) {
5976 if (succeeded(foldReadInitWrite(*
this, adaptor.getOperands(), results)))
5978 if (succeeded(foldWAR(*
this, results)))
5992std::optional<SmallVector<int64_t, 4>> TransferWriteOp::getShapeForUnroll() {
5996void TransferWriteOp::getEffects(
5997 SmallVectorImpl<SideEffects::EffectInstance<MemoryEffects::Effect>>
5999 if (llvm::isa<MemRefType>(getShapedType()))
6000 effects.emplace_back(MemoryEffects::Write::get(), &getBaseMutable(),
6001 SideEffects::DefaultResource::get());
6005 if (hasPureTensorSemantics())
6035class FoldWaw final :
public OpRewritePattern<TransferWriteOp> {
6038 LogicalResult matchAndRewrite(TransferWriteOp writeOp,
6039 PatternRewriter &rewriter)
const override {
6040 if (!llvm::isa<RankedTensorType>(writeOp.getShapedType()))
6042 vector::TransferWriteOp writeToModify = writeOp;
6044 auto defWrite = writeOp.getBase().getDefiningOp<vector::TransferWriteOp>();
6048 writeToModify.getBaseMutable().assign(defWrite.getBase());
6053 cast<VectorTransferOpInterface>(defWrite.getOperation()),
6054 cast<VectorTransferOpInterface>(writeOp.getOperation())))
6058 if (!defWrite->hasOneUse())
6060 writeToModify = defWrite;
6061 defWrite = defWrite.getBase().getDefiningOp<vector::TransferWriteOp>();
6090struct SwapExtractSliceOfTransferWrite
6091 :
public OpRewritePattern<tensor::InsertSliceOp> {
6095 LogicalResult matchAndRewrite(tensor::InsertSliceOp insertOp,
6096 PatternRewriter &rewriter)
const override {
6097 if (!insertOp.hasUnitStride())
6100 insertOp.getSource().getDefiningOp<tensor::ExtractSliceOp>();
6101 if (!extractOp || !extractOp.hasUnitStride() || !extractOp->hasOneUse())
6103 auto transferOp = extractOp.getSource().getDefiningOp<TransferWriteOp>();
6104 if (!transferOp || !transferOp->hasOneUse())
6109 if (insertOp.getSourceType().getRank() != transferOp.getTransferRank()) {
6111 "use-def chain is rank-reducing");
6115 if (!extractOp.hasZeroOffset()) {
6117 "ExtractSliceOp has non-zero offset");
6121 if (!llvm::all_of(transferOp.getIndices(), [](Value value) {
6122 return getConstantIntValue(value) == static_cast<int64_t>(0);
6125 "TranferWriteOp has non-zero offset");
6129 if (insertOp.getMixedSizes().size() != extractOp.getMixedSizes().size()) {
6131 insertOp,
"InsertSliceOp and ExtractSliceOp ranks differ");
6134 for (
auto [insertSize, extractSize] :
6135 llvm::zip_equal(insertOp.getMixedSizes(), extractOp.getMixedSizes())) {
6138 insertOp,
"InsertSliceOp and ExtractSliceOp sizes differ");
6143 assert(transferOp.getVectorType().hasStaticShape() &&
6144 "expected vector to have a static shape");
6145 ArrayRef<int64_t>
vectorShape = transferOp.getVectorType().getShape();
6147 transferOp.getPermutationMap(), transferOp.getShapedType().getShape());
6148 if (transferOp.getMask() || !
vectorShape.equals(resultShape)) {
6150 insertOp,
"TransferWriteOp may not write the full tensor.");
6155 SmallVector<bool> newInBounds(
vectorShape.size(),
false);
6156 auto newExtractOp = tensor::ExtractSliceOp::create(
6157 rewriter, extractOp.getLoc(), insertOp.getSourceType(),
6158 insertOp.getDest(), insertOp.getMixedOffsets(),
6159 insertOp.getMixedSizes(), insertOp.getMixedStrides());
6160 auto newTransferWriteOp = TransferWriteOp::create(
6161 rewriter, transferOp.getLoc(), transferOp.getVector(),
6162 newExtractOp.getResult(), transferOp.getIndices(),
6163 transferOp.getPermutationMapAttr(),
6166 insertOp.getSourceMutable().assign(newTransferWriteOp.getResult());
6174void TransferWriteOp::getCanonicalizationPatterns(RewritePatternSet &results,
6175 MLIRContext *context) {
6176 results.
add<FoldWaw, SwapExtractSliceOfTransferWrite>(context);
6179FailureOr<std::optional<SmallVector<Value>>>
6180TransferWriteOp::bubbleDownCasts(OpBuilder &builder) {
6181 if (!hasPureBufferSemantics())
6195 result = dyn_cast<BoolAttr>(attr);
6198 "expected boolean attribute");
6202static void printBoolAttr(OpAsmPrinter &printer, Operation *, BoolAttr attr) {
6206static LogicalResult verifyLoadStoreMemRefLayout(Operation *op,
6208 MemRefType memRefTy) {
6211 if (!vecTy.isScalable() &&
6212 (vecTy.getRank() == 0 || vecTy.getNumElements() == 1))
6215 if (!memRefTy.isLastDimUnitStride())
6216 return op->
emitOpError(
"most minor memref dim must have unit stride");
6220LogicalResult vector::LoadOp::verify() {
6224 if (
failed(verifyLoadStoreMemRefLayout(*
this, resVecTy, memRefTy)))
6231 return emitOpError(
"memref strides must be non-negative");
6233 if (memRefTy.getRank() < resVecTy.getRank())
6235 "destination memref has lower rank than the result vector");
6238 Type memElemTy = memRefTy.getElementType();
6239 if (
auto memVecTy = llvm::dyn_cast<VectorType>(memElemTy)) {
6240 if (memVecTy != resVecTy)
6241 return emitOpError(
"base memref and result vector types should match");
6242 memElemTy = memVecTy.getElementType();
6245 if (resVecTy.getElementType() != memElemTy)
6246 return emitOpError(
"base and result element types should match");
6247 if (llvm::size(
getIndices()) != memRefTy.getRank())
6248 return emitOpError(
"requires ") << memRefTy.getRank() <<
" indices";
6252OpFoldResult LoadOp::fold(FoldAdaptor) {
6255 return OpFoldResult();
6258std::optional<SmallVector<int64_t, 4>> LoadOp::getShapeForUnroll() {
6262FailureOr<std::optional<SmallVector<Value>>>
6263LoadOp::bubbleDownCasts(OpBuilder &builder) {
6272LogicalResult vector::StoreOp::verify() {
6276 if (
failed(verifyLoadStoreMemRefLayout(*
this, valueVecTy, memRefTy)))
6283 return emitOpError(
"memref strides must be non-negative");
6285 if (memRefTy.getRank() < valueVecTy.getRank())
6286 return emitOpError(
"source memref has lower rank than the vector to store");
6289 Type memElemTy = memRefTy.getElementType();
6290 if (
auto memVecTy = llvm::dyn_cast<VectorType>(memElemTy)) {
6291 if (memVecTy != valueVecTy)
6293 "base memref and valueToStore vector types should match");
6294 memElemTy = memVecTy.getElementType();
6297 if (valueVecTy.getElementType() != memElemTy)
6298 return emitOpError(
"base and valueToStore element type should match");
6299 if (llvm::size(
getIndices()) != memRefTy.getRank())
6300 return emitOpError(
"requires ") << memRefTy.getRank() <<
" indices";
6304LogicalResult StoreOp::fold(FoldAdaptor adaptor,
6305 SmallVectorImpl<OpFoldResult> &results) {
6309std::optional<SmallVector<int64_t, 4>> StoreOp::getShapeForUnroll() {
6313FailureOr<std::optional<SmallVector<Value>>>
6314StoreOp::bubbleDownCasts(OpBuilder &builder) {
6323LogicalResult MaskedLoadOp::verify() {
6324 VectorType maskVType = getMaskVectorType();
6325 VectorType passVType = getPassThruVectorType();
6329 if (
failed(verifyLoadStoreMemRefLayout(*
this, resVType, memType)))
6336 return emitOpError(
"memref strides must be non-negative");
6341 if (llvm::size(
getIndices()) != memType.getRank())
6342 return emitOpError(
"requires ") << memType.getRank() <<
" indices";
6343 if (resVType.getShape() != maskVType.getShape())
6344 return emitOpError(
"expected result shape to match mask shape");
6345 if (resVType != passVType)
6346 return emitOpError(
"expected pass_thru of same type as result type");
6351class MaskedLoadFolder final :
public OpRewritePattern<MaskedLoadOp> {
6354 LogicalResult matchAndRewrite(MaskedLoadOp
load,
6355 PatternRewriter &rewriter)
const override {
6367 llvm_unreachable(
"Unexpected 1DMaskFormat on MaskedLoad");
6372void MaskedLoadOp::getCanonicalizationPatterns(RewritePatternSet &results,
6373 MLIRContext *context) {
6374 results.
add<MaskedLoadFolder>(context);
6377OpFoldResult MaskedLoadOp::fold(FoldAdaptor) {
6380 return OpFoldResult();
6383FailureOr<std::optional<SmallVector<Value>>>
6384MaskedLoadOp::bubbleDownCasts(OpBuilder &builder) {
6393LogicalResult MaskedStoreOp::verify() {
6394 VectorType maskVType = getMaskVectorType();
6398 if (
failed(verifyLoadStoreMemRefLayout(*
this, valueVType, memType)))
6405 return emitOpError(
"memref strides must be non-negative");
6410 if (llvm::size(
getIndices()) != memType.getRank())
6411 return emitOpError(
"requires ") << memType.getRank() <<
" indices";
6412 if (valueVType.getShape() != maskVType.getShape())
6413 return emitOpError(
"expected valueToStore shape to match mask shape");
6418class MaskedStoreFolder final :
public OpRewritePattern<MaskedStoreOp> {
6421 LogicalResult matchAndRewrite(MaskedStoreOp store,
6422 PatternRewriter &rewriter)
const override {
6426 store, store.getValueToStore(), store.getBase(), store.getIndices());
6434 llvm_unreachable(
"Unexpected 1DMaskFormat on MaskedStore");
6439void MaskedStoreOp::getCanonicalizationPatterns(RewritePatternSet &results,
6440 MLIRContext *context) {
6441 results.
add<MaskedStoreFolder>(context);
6444LogicalResult MaskedStoreOp::fold(FoldAdaptor adaptor,
6445 SmallVectorImpl<OpFoldResult> &results) {
6449FailureOr<std::optional<SmallVector<Value>>>
6450MaskedStoreOp::bubbleDownCasts(OpBuilder &builder) {
6459LogicalResult GatherOp::verify() {
6460 VectorType indVType = getIndexVectorType();
6461 VectorType maskVType = getMaskVectorType();
6463 ShapedType baseType = getBaseType();
6465 if (!llvm::isa<MemRefType, RankedTensorType>(baseType))
6466 return emitOpError(
"requires base to be a memref or ranked tensor type");
6471 if (
auto memRefType = dyn_cast<MemRefType>(baseType))
6473 return emitOpError(
"memref strides must be non-negative");
6478 if (llvm::size(getOffsets()) != baseType.getRank())
6479 return emitOpError(
"requires ") << baseType.getRank() <<
" indices";
6480 if (resVType.getShape() != indVType.getShape())
6481 return emitOpError(
"expected result dim to match indices dim");
6482 if (resVType.getShape() != maskVType.getShape())
6483 return emitOpError(
"expected result dim to match mask dim");
6484 if (resVType != getPassThruVectorType())
6485 return emitOpError(
"expected pass_thru of same type as result type");
6486 if (getAlignmentAttr() && !isa<MemRefType>(baseType)) {
6488 "alignment is only supported for memref bases, not tensor bases");
6497Type GatherOp::getExpectedMaskType() {
6498 auto vecType = this->getIndexVectorType();
6499 return VectorType::get(vecType.getShape(),
6500 IntegerType::get(vecType.getContext(), 1),
6501 vecType.getScalableDims());
6504std::optional<SmallVector<int64_t, 4>> GatherOp::getShapeForUnroll() {
6509static LogicalResult isZeroBasedContiguousSeq(Value indexVec) {
6510 auto vecType = dyn_cast<VectorType>(indexVec.
getType());
6511 if (!vecType || vecType.getRank() != 1 || vecType.isScalable())
6517 DenseIntElementsAttr elements;
6522 llvm::equal(elements, llvm::seq<int64_t>(0, vecType.getNumElements())));
6526class GatherFolder final :
public OpRewritePattern<GatherOp> {
6529 LogicalResult matchAndRewrite(GatherOp gather,
6530 PatternRewriter &rewriter)
const override {
6535 rewriter.
replaceOp(gather, gather.getPassThru());
6540 llvm_unreachable(
"Unexpected 1DMaskFormat on GatherFolder");
6546class FoldContiguousGather final :
public OpRewritePattern<GatherOp> {
6549 LogicalResult matchAndRewrite(GatherOp op,
6550 PatternRewriter &rewriter)
const override {
6551 if (!isa<MemRefType>(op.getBase().getType()))
6554 if (
failed(isZeroBasedContiguousSeq(op.getIndices())))
6558 op.getOffsets(), op.getMask(),
6565void GatherOp::getCanonicalizationPatterns(RewritePatternSet &results,
6566 MLIRContext *context) {
6567 results.
add<GatherFolder, FoldContiguousGather>(context);
6570FailureOr<std::optional<SmallVector<Value>>>
6571GatherOp::bubbleDownCasts(OpBuilder &builder) {
6580LogicalResult ScatterOp::verify() {
6581 VectorType indVType = getIndexVectorType();
6582 VectorType maskVType = getMaskVectorType();
6584 ShapedType baseType = getBaseType();
6586 if (!llvm::isa<MemRefType, RankedTensorType>(baseType))
6587 return emitOpError(
"requires base to be a memref or ranked tensor type");
6592 if (
auto memRefType = dyn_cast<MemRefType>(baseType))
6594 return emitOpError(
"memref strides must be non-negative");
6599 if (llvm::size(getOffsets()) != baseType.getRank())
6600 return emitOpError(
"requires ") << baseType.getRank() <<
" indices";
6601 if (valueVType.getShape() != indVType.getShape())
6602 return emitOpError(
"expected valueToStore dim to match indices dim");
6603 if (valueVType.getShape() != maskVType.getShape())
6604 return emitOpError(
"expected valueToStore dim to match mask dim");
6605 if (getAlignmentAttr() && !isa<MemRefType>(baseType)) {
6607 "alignment is only supported for memref bases, not tensor bases");
6612class ScatterFolder final :
public OpRewritePattern<ScatterOp> {
6615 LogicalResult matchAndRewrite(ScatterOp scatter,
6616 PatternRewriter &rewriter)
const override {
6617 ShapedType baseType = scatter.getBaseType();
6618 bool isMemRef = isa<MemRefType>(baseType);
6619 if (!isMemRef && !isa<RankedTensorType>(baseType))
6632 rewriter.
replaceOp(scatter, scatter.getBase());
6637 llvm_unreachable(
"Unexpected 1DMaskFormat on ScatterFolder");
6643class FoldContiguousScatter final :
public OpRewritePattern<ScatterOp> {
6646 LogicalResult matchAndRewrite(ScatterOp op,
6647 PatternRewriter &rewriter)
const override {
6650 if (!isa<MemRefType>(op.getBase().getType()))
6653 if (
failed(isZeroBasedContiguousSeq(op.getIndices())))
6657 op, op.getBase(), op.getOffsets(), op.getMask(), op.getValueToStore());
6663void ScatterOp::getCanonicalizationPatterns(RewritePatternSet &results,
6664 MLIRContext *context) {
6665 results.
add<ScatterFolder, FoldContiguousScatter>(context);
6668FailureOr<std::optional<SmallVector<Value>>>
6669ScatterOp::bubbleDownCasts(OpBuilder &builder) {
6678LogicalResult ExpandLoadOp::verify() {
6679 VectorType maskVType = getMaskVectorType();
6680 VectorType passVType = getPassThruVectorType();
6684 if (
failed(verifyLoadStoreMemRefLayout(*
this, resVType, memType)))
6691 return emitOpError(
"memref strides must be non-negative");
6696 if (llvm::size(
getIndices()) != memType.getRank())
6697 return emitOpError(
"requires ") << memType.getRank() <<
" indices";
6698 if (resVType.getShape() != maskVType.getShape())
6699 return emitOpError(
"expected result shape to match mask shape");
6700 if (resVType.getScalableDims() != maskVType.getScalableDims())
6702 "expected result scalable dims to match mask scalable dims");
6703 if (resVType != passVType)
6704 return emitOpError(
"expected pass_thru of same type as result type");
6709class ExpandLoadFolder final :
public OpRewritePattern<ExpandLoadOp> {
6712 LogicalResult matchAndRewrite(ExpandLoadOp expand,
6713 PatternRewriter &rewriter)
const override {
6717 expand, expand.getType(), expand.getBase(), expand.getIndices());
6720 rewriter.
replaceOp(expand, expand.getPassThru());
6725 llvm_unreachable(
"Unexpected 1DMaskFormat on ExpandLoadFolder");
6730void ExpandLoadOp::getCanonicalizationPatterns(RewritePatternSet &results,
6731 MLIRContext *context) {
6732 results.
add<ExpandLoadFolder>(context);
6735FailureOr<std::optional<SmallVector<Value>>>
6736ExpandLoadOp::bubbleDownCasts(OpBuilder &builder) {
6745LogicalResult CompressStoreOp::verify() {
6746 VectorType maskVType = getMaskVectorType();
6750 if (
failed(verifyLoadStoreMemRefLayout(*
this, valueVType, memType)))
6757 return emitOpError(
"memref strides must be non-negative");
6762 if (llvm::size(
getIndices()) != memType.getRank())
6763 return emitOpError(
"requires ") << memType.getRank() <<
" indices";
6764 if (valueVType.getShape() != maskVType.getShape())
6765 return emitOpError(
"expected valueToStore shape to match mask shape");
6766 if (valueVType.getScalableDims() != maskVType.getScalableDims())
6768 "expected valueToStore scalable dims to match mask scalable dims");
6773class CompressStoreFolder final :
public OpRewritePattern<CompressStoreOp> {
6776 LogicalResult matchAndRewrite(CompressStoreOp compress,
6777 PatternRewriter &rewriter)
const override {
6781 compress, compress.getValueToStore(), compress.getBase(),
6782 compress.getIndices());
6790 llvm_unreachable(
"Unexpected 1DMaskFormat on CompressStoreFolder");
6795void CompressStoreOp::getCanonicalizationPatterns(RewritePatternSet &results,
6796 MLIRContext *context) {
6797 results.
add<CompressStoreFolder>(context);
6800FailureOr<std::optional<SmallVector<Value>>>
6801CompressStoreOp::bubbleDownCasts(OpBuilder &builder) {
6810void ShapeCastOp::inferResultRanges(ArrayRef<ConstantIntRanges> argRanges,
6812 setResultRanges(getResult(), argRanges.front());
6815std::optional<SmallVector<int64_t, 4>> ShapeCastOp::getShapeForUnroll() {
6816 return llvm::to_vector<4>(getResultVectorType().
getShape());
6819LogicalResult ShapeCastOp::verify() {
6821 VectorType sourceType = getSourceVectorType();
6822 VectorType resultType = getResultVectorType();
6830 int64_t sourceNElms = sourceType.getNumElements();
6831 int64_t resultNElms = resultType.getNumElements();
6832 if (sourceNElms != resultNElms) {
6833 return emitOpError() <<
"has different number of elements at source ("
6834 << sourceNElms <<
") and result (" << resultNElms
6839 int64_t sourceNScalableDims = sourceType.getNumScalableDims();
6840 int64_t resultNScalableDims = resultType.getNumScalableDims();
6841 if (sourceNScalableDims != resultNScalableDims)
6842 return emitOpError() <<
"has different number of scalable dims at source ("
6843 << sourceNScalableDims <<
") and result ("
6844 << resultNScalableDims <<
")";
6855bool ShapeCastOp::isBroadcastLike() {
6856 auto srcType = getSourceVectorType();
6857 auto resType = getResultVectorType();
6860 std::pair<VectorDim, VectorDim> mismatchingDims;
6862 BroadcastableToResult::Success)
6869 size_t rankDiff = resType.getRank() - srcType.getRank();
6874 if (!llvm::all_of(resType.getShape().take_front(rankDiff),
6875 [](int64_t dim) { return dim == 1; }))
6879 return resType.getShape().take_back(srcType.getRank()) == srcType.getShape();
6886static bool isOrderPreserving(TransposeOp transpose) {
6887 ArrayRef<int64_t> permutation = transpose.getPermutation();
6888 VectorType sourceType = transpose.getSourceVectorType();
6889 ArrayRef<int64_t> inShape = sourceType.getShape();
6890 ArrayRef<bool> inDimIsScalable = sourceType.getScalableDims();
6891 auto isNonScalableUnitDim = [&](int64_t dim) {
6892 return inShape[dim] == 1 && !inDimIsScalable[dim];
6894 int64_t current = 0;
6895 for (
auto p : permutation) {
6896 if (!isNonScalableUnitDim(p)) {
6906OpFoldResult ShapeCastOp::fold(FoldAdaptor adaptor) {
6908 VectorType resultType =
getType();
6911 if (getSource().
getType() == resultType)
6915 if (
auto precedingShapeCast = getSource().getDefiningOp<ShapeCastOp>()) {
6916 setOperand(precedingShapeCast.getSource());
6921 if (
auto transpose = getSource().getDefiningOp<TransposeOp>()) {
6922 if (isOrderPreserving(transpose)) {
6923 setOperand(transpose.getVector());
6931 if (
auto bcastOp = getSource().getDefiningOp<BroadcastOp>()) {
6932 if (bcastOp.getSourceType() == resultType)
6933 return bcastOp.getSource();
6937 if (
auto denseAttr =
6938 dyn_cast_if_present<DenseElementsAttr>(adaptor.getSource()))
6939 return denseAttr.reshape(
getType());
6955static VectorType trimTrailingOneDims(VectorType oldType) {
6956 ArrayRef<int64_t> oldShape = oldType.getShape();
6957 ArrayRef<int64_t> newShape = oldShape;
6959 ArrayRef<bool> oldScalableDims = oldType.getScalableDims();
6960 ArrayRef<bool> newScalableDims = oldScalableDims;
6962 while (!newShape.empty() && newShape.back() == 1 && !newScalableDims.back()) {
6963 newShape = newShape.drop_back(1);
6964 newScalableDims = newScalableDims.drop_back(1);
6969 if (newShape.empty()) {
6970 newShape = oldShape.take_back();
6971 newScalableDims = oldScalableDims.take_back();
6974 return VectorType::get(newShape, oldType.getElementType(), newScalableDims);
6989class ShapeCastCreateMaskFolderTrailingOneDim final
6990 :
public OpRewritePattern<ShapeCastOp> {
6994 LogicalResult matchAndRewrite(ShapeCastOp shapeOp,
6995 PatternRewriter &rewriter)
const override {
6996 Value shapeOpSrc = shapeOp->getOperand(0);
6997 auto createMaskOp = shapeOpSrc.
getDefiningOp<vector::CreateMaskOp>();
6998 auto constantMaskOp = shapeOpSrc.
getDefiningOp<vector::ConstantMaskOp>();
6999 if (!createMaskOp && !constantMaskOp)
7002 VectorType shapeOpResTy = shapeOp.getResultVectorType();
7003 VectorType shapeOpSrcTy = shapeOp.getSourceVectorType();
7005 VectorType newVecType = trimTrailingOneDims(shapeOpSrcTy);
7006 if (newVecType != shapeOpResTy)
7009 auto numDimsToDrop =
7010 shapeOpSrcTy.getShape().size() - shapeOpResTy.getShape().size();
7017 auto maskOperands = createMaskOp.getOperands();
7018 auto numMaskOperands = maskOperands.size();
7021 for (
size_t i = numMaskOperands - 1; i >= numMaskOperands - numDimsToDrop;
7023 auto constant = maskOperands[i].getDefiningOp<arith::ConstantIndexOp>();
7024 if (!constant || (constant.value() != 1))
7027 SmallVector<Value> newMaskOperands =
7028 maskOperands.drop_back(numDimsToDrop);
7035 if (constantMaskOp) {
7036 auto maskDimSizes = constantMaskOp.getMaskDimSizes();
7037 auto numMaskOperands = maskDimSizes.size();
7040 for (
size_t i = numMaskOperands - 1; i >= numMaskOperands - numDimsToDrop;
7042 if (maskDimSizes[i] != 1)
7046 auto newMaskOperands = maskDimSizes.drop_back(numDimsToDrop);
7059int64_t getBroadcastStretchingFactor(ArrayRef<int64_t> srcShape,
7060 ArrayRef<int64_t> dstShape) {
7061 int stretchingFactor = 1;
7062 int numLeadingDims = dstShape.size() - srcShape.size();
7063 for (
int i = 0, e = srcShape.size(); i < e; i++) {
7064 int64_t dstDim = dstShape[numLeadingDims + i];
7065 if (srcShape[i] == 1 && dstDim != 1) {
7066 stretchingFactor *= dstDim;
7069 return stretchingFactor;
7073class ShapeCastBroadcastFolder final :
public OpRewritePattern<ShapeCastOp> {
7077 LogicalResult matchAndRewrite(ShapeCastOp shapeCastOp,
7078 PatternRewriter &rewriter)
const override {
7080 shapeCastOp.getSource().getDefiningOp<vector::BroadcastOp>();
7084 auto srcVectorType = dyn_cast<VectorType>(broadcastOp.getSourceType());
7085 bool srcIsScalar = !srcVectorType;
7093 VectorType dstVectorType = shapeCastOp.getResultVectorType();
7094 ArrayRef<int64_t> dstShape = dstVectorType.getShape();
7095 ArrayRef<int64_t> srcShape =
7096 srcIsScalar ? ArrayRef<int64_t>{} : srcVectorType.getShape();
7097 ArrayRef<int64_t> broadcastShape =
7098 broadcastOp.getResultVectorType().getShape();
7102 BroadcastableToResult::Success) {
7110 if (srcVectorType.getNumElements() != 1) {
7111 if (getBroadcastStretchingFactor(srcShape, dstShape) !=
7112 getBroadcastStretchingFactor(srcShape, broadcastShape)) {
7119 broadcastOp.getSource());
7138class FoldShapeCastOfFromElements final :
public OpRewritePattern<ShapeCastOp> {
7142 LogicalResult matchAndRewrite(ShapeCastOp shapeCastOp,
7143 PatternRewriter &rewriter)
const override {
7144 auto fromElements = shapeCastOp.getSource().getDefiningOp<FromElementsOp>();
7149 shapeCastOp, shapeCastOp.getResultVectorType(),
7150 fromElements.getElements());
7157void ShapeCastOp::getCanonicalizationPatterns(RewritePatternSet &results,
7158 MLIRContext *context) {
7159 results.
add<ShapeCastCreateMaskFolderTrailingOneDim, ShapeCastBroadcastFolder,
7160 FoldShapeCastOfFromElements>(context);
7167LogicalResult BitCastOp::verify() {
7168 auto sourceVectorType = getSourceVectorType();
7169 auto resultVectorType = getResultVectorType();
7171 for (int64_t i = 0, e = sourceVectorType.getRank() - 1; i < e; i++) {
7172 if (sourceVectorType.getDimSize(i) != resultVectorType.getDimSize(i))
7173 return emitOpError(
"dimension size mismatch at: ") << i;
7176 DataLayout dataLayout = DataLayout::closest(*
this);
7177 auto sourceElementBits =
7179 auto resultElementBits =
7182 if (sourceVectorType.getRank() == 0) {
7183 if (sourceElementBits != resultElementBits)
7184 return emitOpError(
"source/result bitwidth of the 0-D vector element "
7185 "types must be equal");
7186 }
else if (sourceElementBits * sourceVectorType.getShape().back() !=
7187 resultElementBits * resultVectorType.getShape().back()) {
7189 "source/result bitwidth of the minor 1-D vectors must be equal");
7195OpFoldResult BitCastOp::fold(FoldAdaptor adaptor) {
7201 if (
auto otherOp = getSource().getDefiningOp<BitCastOp>()) {
7202 if (getResult().
getType() == otherOp.getSource().getType())
7203 return otherOp.getSource();
7205 setOperand(otherOp.getSource());
7209 Attribute sourceConstant = adaptor.getSource();
7210 if (!sourceConstant)
7213 Type srcElemType = getSourceVectorType().getElementType();
7214 Type dstElemType = getResultVectorType().getElementType();
7216 if (
auto floatPack = llvm::dyn_cast<DenseFPElementsAttr>(sourceConstant)) {
7217 if (floatPack.isSplat()) {
7218 auto splat = floatPack.getSplatValue<FloatAttr>();
7221 if (srcElemType.
isF16() && dstElemType.
isF32()) {
7222 uint32_t bits =
static_cast<uint32_t
>(
7223 splat.getValue().bitcastToAPInt().getZExtValue());
7225 bits = (bits << 16) | (bits & 0xffff);
7226 APInt intBits(32, bits);
7227 APFloat floatBits(llvm::APFloat::IEEEsingle(), intBits);
7233 if (
auto intPack = llvm::dyn_cast<DenseIntElementsAttr>(sourceConstant)) {
7234 if (intPack.isSplat()) {
7235 auto splat = intPack.getSplatValue<IntegerAttr>();
7237 if (llvm::isa<IntegerType>(dstElemType) && srcElemType.
isIntOrFloat()) {
7242 if (dstBitWidth > srcBitWidth && dstBitWidth % srcBitWidth == 0) {
7243 APInt intBits = splat.getValue().zext(dstBitWidth);
7246 for (uint64_t i = 0; i < dstBitWidth / srcBitWidth - 1; i++)
7247 intBits = (intBits << srcBitWidth) | intBits;
7257std::optional<SmallVector<int64_t, 4>> BitCastOp::getShapeForUnroll() {
7258 return llvm::to_vector<4>(getResultVectorType().
getShape());
7265static SmallVector<int64_t, 8> extractShape(MemRefType memRefType) {
7266 auto vectorType = llvm::dyn_cast<VectorType>(memRefType.getElementType());
7267 SmallVector<int64_t, 8> res(memRefType.getShape());
7269 res.append(vectorType.getShape().begin(), vectorType.getShape().end());
7275void TypeCastOp::build(OpBuilder &builder, OperationState &
result,
7277 result.addOperands(source);
7278 MemRefType memRefType = llvm::cast<MemRefType>(source.
getType());
7279 VectorType vectorType =
7280 VectorType::get(extractShape(memRefType),
7282 result.addTypes(MemRefType::get({}, vectorType, MemRefLayoutAttrInterface(),
7283 memRefType.getMemorySpace()));
7286LogicalResult TypeCastOp::verify() {
7287 MemRefType canonicalType =
getMemRefType().canonicalizeStridedLayout();
7288 if (!canonicalType.getLayout().isIdentity())
7289 return emitOpError(
"expects operand to be a memref with identity layout");
7290 if (!getResultMemRefType().getLayout().isIdentity())
7291 return emitOpError(
"expects result to be a memref with identity layout");
7292 if (getResultMemRefType().getMemorySpace() !=
7294 return emitOpError(
"expects result in same memory space");
7297 auto resultType = getResultMemRefType();
7301 "expects result and operand with same underlying scalar type: ")
7303 if (extractShape(sourceType) != extractShape(resultType))
7305 "expects concatenated result and operand shapes to be equal: ")
7314void vector::TransposeOp::build(OpBuilder &builder, OperationState &
result,
7315 Value vector, ArrayRef<int64_t> permutation) {
7316 VectorType vt = llvm::cast<VectorType>(vector.
getType());
7317 SmallVector<int64_t, 4> transposedShape(vt.getRank());
7318 SmallVector<bool, 4> transposedScalableDims(vt.getRank());
7319 for (
unsigned i = 0; i < permutation.size(); ++i) {
7320 transposedShape[i] = vt.getShape()[permutation[i]];
7321 transposedScalableDims[i] = vt.getScalableDims()[permutation[i]];
7324 result.addOperands(vector);
7325 result.addTypes(VectorType::get(transposedShape, vt.getElementType(),
7326 transposedScalableDims));
7327 result.addAttribute(TransposeOp::getPermutationAttrName(
result.name),
7331OpFoldResult vector::TransposeOp::fold(FoldAdaptor adaptor) {
7334 llvm::dyn_cast_if_present<SplatElementsAttr>(adaptor.getVector()))
7335 return splat.reshape(getResultVectorType());
7352 if (getSourceVectorType() == getResultVectorType() &&
7353 isOrderPreserving(*
this))
7359LogicalResult vector::TransposeOp::verify() {
7360 VectorType vectorType = getSourceVectorType();
7361 VectorType resultType = getResultVectorType();
7362 int64_t rank = resultType.getRank();
7363 if (vectorType.getRank() != rank)
7364 return emitOpError(
"vector result rank mismatch: ") << rank;
7366 ArrayRef<int64_t> perm = getPermutation();
7367 int64_t size = perm.size();
7369 return emitOpError(
"transposition length mismatch: ") << size;
7370 SmallVector<bool, 8> seen(rank,
false);
7371 for (
const auto &ta : llvm::enumerate(perm)) {
7372 if (ta.value() < 0 || ta.value() >= rank)
7373 return emitOpError(
"transposition index out of range: ") << ta.value();
7374 if (seen[ta.value()])
7375 return emitOpError(
"duplicate position index: ") << ta.value();
7376 seen[ta.value()] =
true;
7377 if (resultType.getDimSize(ta.index()) != vectorType.getDimSize(ta.value()))
7378 return emitOpError(
"dimension size mismatch at: ") << ta.value();
7383std::optional<SmallVector<int64_t, 4>> TransposeOp::getShapeForUnroll() {
7384 return llvm::to_vector<4>(getResultVectorType().
getShape());
7387void TransposeOp::inferResultRanges(ArrayRef<ConstantIntRanges> argRanges,
7389 setResultRanges(getResult(), argRanges.front());
7395class TransposeFolder final :
public OpRewritePattern<vector::TransposeOp> {
7399 LogicalResult matchAndRewrite(vector::TransposeOp transposeOp,
7400 PatternRewriter &rewriter)
const override {
7402 auto composePermutations = [](ArrayRef<int64_t> permutation1,
7403 ArrayRef<int64_t> permutation2) {
7404 SmallVector<int64_t, 4>
result;
7405 for (
auto index : permutation2)
7406 result.push_back(permutation1[index]);
7411 vector::TransposeOp parentTransposeOp =
7412 transposeOp.getVector().getDefiningOp<vector::TransposeOp>();
7413 if (!parentTransposeOp)
7416 SmallVector<int64_t, 4> permutation = composePermutations(
7417 parentTransposeOp.getPermutation(), transposeOp.getPermutation());
7420 transposeOp, transposeOp.getResult().
getType(),
7421 parentTransposeOp.getVector(), permutation);
7427class FoldTransposeSplat final :
public OpRewritePattern<TransposeOp> {
7431 LogicalResult matchAndRewrite(TransposeOp transposeOp,
7432 PatternRewriter &rewriter)
const override {
7433 Value splat = getScalarSplatSource(transposeOp.getVector());
7438 transposeOp, transposeOp.getResultVectorType(), splat);
7444class FoldTransposeCreateMask final :
public OpRewritePattern<TransposeOp> {
7448 LogicalResult matchAndRewrite(TransposeOp transpOp,
7449 PatternRewriter &rewriter)
const override {
7450 Value transposeSrc = transpOp.getVector();
7451 auto createMaskOp = transposeSrc.
getDefiningOp<vector::CreateMaskOp>();
7452 auto constantMaskOp = transposeSrc.
getDefiningOp<vector::ConstantMaskOp>();
7453 if (!createMaskOp && !constantMaskOp)
7458 ArrayRef<int64_t> permutation = transpOp.getPermutation();
7461 auto maskOperands = createMaskOp.getOperands();
7462 SmallVector<Value> newOperands(maskOperands.begin(), maskOperands.end());
7466 transpOp, transpOp.getResultVectorType(), newOperands);
7471 auto maskDimSizes = constantMaskOp.getMaskDimSizes();
7475 transpOp, transpOp.getResultVectorType(), newMaskDimSizes);
7481class FoldTransposeShapeCast final :
public OpRewritePattern<TransposeOp> {
7485 LogicalResult matchAndRewrite(TransposeOp transposeOp,
7486 PatternRewriter &rewriter)
const override {
7488 transposeOp.getVector().getDefiningOp<vector::ShapeCastOp>();
7491 if (!isOrderPreserving(transposeOp))
7494 VectorType resultType = transposeOp.getType();
7501 shapeCastOp.getSource());
7520class FoldTransposeFromElements final :
public OpRewritePattern<TransposeOp> {
7523 LogicalResult matchAndRewrite(vector::TransposeOp transposeOp,
7524 PatternRewriter &rewriter)
const override {
7525 auto fromElementsOp =
7526 transposeOp.getVector().getDefiningOp<vector::FromElementsOp>();
7527 if (!fromElementsOp)
7530 VectorType srcTy = fromElementsOp.getDest().getType();
7531 VectorType dstTy = transposeOp.getType();
7533 ArrayRef<int64_t> permutation = transposeOp.getPermutation();
7534 int64_t rank = srcTy.getRank();
7537 SmallVector<int64_t> inversePerm(rank, 0);
7538 for (int64_t i = 0; i < rank; ++i)
7539 inversePerm[permutation[i]] = i;
7541 ArrayRef<int64_t> srcShape = srcTy.getShape();
7542 ArrayRef<int64_t> dstShape = dstTy.getShape();
7543 SmallVector<int64_t> srcIdx(rank, 0);
7544 SmallVector<int64_t> dstIdx(rank, 0);
7548 auto elementsOld = fromElementsOp.getElements();
7549 SmallVector<Value> elementsNew;
7550 int64_t dstNumElements = dstTy.getNumElements();
7551 elementsNew.reserve(dstNumElements);
7555 for (int64_t linearIdx = 0; linearIdx < dstNumElements; ++linearIdx) {
7559 for (int64_t j = 0; j < rank; ++j)
7560 srcIdx[j] = dstIdx[inversePerm[j]];
7562 int64_t srcLin =
linearize(srcIdx, srcStrides);
7564 elementsNew.push_back(elementsOld[srcLin]);
7598class FoldTransposeBroadcast :
public OpRewritePattern<vector::TransposeOp> {
7601 FoldTransposeBroadcast(MLIRContext *context, PatternBenefit benefit = 1)
7602 : OpRewritePattern<vector::TransposeOp>(context, benefit) {}
7604 LogicalResult matchAndRewrite(vector::TransposeOp transpose,
7605 PatternRewriter &rewriter)
const override {
7611 "not preceded by a broadcast");
7614 auto inputType = dyn_cast<VectorType>(
broadcast.getSourceType());
7615 VectorType outputType = transpose.getResultVectorType();
7618 bool inputIsScalar = !inputType;
7619 if (inputIsScalar) {
7625 ArrayRef<int64_t> permutation = transpose.getPermutation();
7626 ArrayRef<int64_t> inputShape = inputType.getShape();
7627 int64_t inputRank = inputType.getRank();
7628 int64_t outputRank = transpose.getType().getRank();
7629 int64_t deltaRank = outputRank - inputRank;
7632 for (
int inputIndex = 0; inputIndex < inputRank; ++inputIndex) {
7633 bool notOne = inputShape[inputIndex] != 1;
7634 bool prevNotOne = (inputIndex != 0 && inputShape[inputIndex - 1] != 1);
7635 bool groupEndFound = notOne || prevNotOne;
7636 if (groupEndFound) {
7637 int high = inputIndex + deltaRank;
7641 for (
int i = low; i < high; ++i) {
7642 if (permutation[i] < low || permutation[i] >= high) {
7644 transpose,
"permutation not local to group");
7658 vector::BroadcastableToResult::Success &&
7659 "not broadcastable directly to transpose output");
7670void vector::TransposeOp::getCanonicalizationPatterns(
7671 RewritePatternSet &results, MLIRContext *context) {
7672 results.
add<FoldTransposeCreateMask, FoldTransposeShapeCast, TransposeFolder,
7673 FoldTransposeSplat, FoldTransposeFromElements,
7674 FoldTransposeBroadcast>(context);
7681void ConstantMaskOp::build(OpBuilder &builder, OperationState &
result,
7683 assert(kind == ConstantMaskKind::AllTrue ||
7684 kind == ConstantMaskKind::AllFalse);
7685 build(builder,
result, type,
7686 kind == ConstantMaskKind::AllTrue
7688 : SmallVector<int64_t>(type.getRank(), 0));
7691LogicalResult ConstantMaskOp::verify() {
7692 auto resultType = llvm::cast<VectorType>(getResult().
getType());
7694 if (resultType.getRank() == 0) {
7695 if (getMaskDimSizes().size() != 1)
7696 return emitError(
"array attr must have length 1 for 0-D vectors");
7697 auto dim = getMaskDimSizes()[0];
7698 if (dim != 0 && dim != 1)
7699 return emitError(
"mask dim size must be either 0 or 1 for 0-D vectors");
7704 if (
static_cast<int64_t
>(getMaskDimSizes().size()) != resultType.getRank())
7706 "must specify array attr of size equal vector result rank");
7709 auto resultShape = resultType.getShape();
7710 auto resultScalableDims = resultType.getScalableDims();
7711 ArrayRef<int64_t> maskDimSizes = getMaskDimSizes();
7712 for (
const auto [index, maskDimSize] : llvm::enumerate(maskDimSizes)) {
7713 if (maskDimSize < 0 || maskDimSize > resultShape[index])
7715 "array attr of size out of bounds of vector result dimension size");
7716 if (resultScalableDims[index] && maskDimSize != 0 &&
7717 maskDimSize != resultShape[index])
7719 "only supports 'none set' or 'all set' scalable dimensions");
7723 bool anyZeros = llvm::is_contained(maskDimSizes, 0);
7724 bool allZeros = llvm::all_of(maskDimSizes, [](int64_t s) {
return s == 0; });
7725 if (anyZeros && !allZeros)
7726 return emitOpError(
"expected all mask dim sizes to be zeros, "
7727 "as a result of conjunction with zero mask dim");
7731bool ConstantMaskOp::isAllOnesMask() {
7734 if (resultType.getRank() == 0) {
7735 assert(getMaskDimSizes().size() == 1 &&
"invalid sizes for zero rank mask");
7736 return getMaskDimSizes()[0] == 1;
7738 for (
const auto [resultSize, maskDimSize] :
7739 llvm::zip_equal(resultType.getShape(), getMaskDimSizes())) {
7740 if (maskDimSize < resultSize)
7746OpFoldResult ConstantMaskOp::fold(FoldAdaptor adaptor) {
7747 ArrayRef<int64_t> bounds = getMaskDimSizes();
7750 auto createBoolSplat = [&](
bool x) {
7756 if (vectorSizes.empty()) {
7757 assert(bounds.size() == 1 &&
"invalid sizes for zero rank mask");
7758 return createBoolSplat(bounds[0] == 1);
7761 if (bounds == vectorSizes)
7762 return createBoolSplat(
true);
7763 if (llvm::all_of(bounds, [](int64_t x) {
return x == 0; }))
7764 return createBoolSplat(
false);
7765 return OpFoldResult();
7772void CreateMaskOp::build(OpBuilder &builder, OperationState &
result,
7774 ArrayRef<OpFoldResult> mixedOperands) {
7775 SmallVector<Value> operands =
7777 build(builder,
result, type, operands);
7780LogicalResult CreateMaskOp::verify() {
7781 auto vectorType = llvm::cast<VectorType>(getResult().
getType());
7783 if (vectorType.getRank() == 0) {
7784 if (getNumOperands() != 1)
7786 "must specify exactly one operand for 0-D create_mask");
7787 }
else if (getNumOperands() !=
7788 llvm::cast<VectorType>(getResult().
getType()).getRank()) {
7790 "must specify an operand for each result vector dimension");
7820class CreateMaskFolder final :
public OpRewritePattern<CreateMaskOp> {
7824 LogicalResult matchAndRewrite(CreateMaskOp createMaskOp,
7825 PatternRewriter &rewriter)
const override {
7826 VectorType maskType = createMaskOp.getVectorType();
7827 ArrayRef<int64_t> maskTypeDimSizes = maskType.getShape();
7828 ArrayRef<bool> maskTypeDimScalableFlags = maskType.getScalableDims();
7831 constexpr std::array<int64_t, 1> rankZeroShape{1};
7832 constexpr std::array<bool, 1> rankZeroScalableDims{
false};
7833 if (maskType.getRank() == 0) {
7834 maskTypeDimSizes = rankZeroShape;
7835 maskTypeDimScalableFlags = rankZeroScalableDims;
7840 SmallVector<int64_t, 4> constantDims;
7841 for (
auto [i, dimSize] : llvm::enumerate(createMaskOp.getOperands())) {
7846 if (maskTypeDimScalableFlags[i] && intSize >= 0)
7848 constantDims.push_back(*intSize);
7852 if (vscaleMultiplier < maskTypeDimSizes[i])
7854 constantDims.push_back(*vscaleMultiplier);
7861 for (
auto [value, maskDimSize] : llvm::zip(constantDims, maskTypeDimSizes))
7862 value = std::clamp<int64_t>(value, 0, maskDimSize);
7865 if (llvm::is_contained(constantDims, 0))
7866 constantDims.assign(constantDims.size(), 0);
7877void CreateMaskOp::getCanonicalizationPatterns(RewritePatternSet &results,
7878 MLIRContext *context) {
7879 results.
add<CreateMaskFolder>(context);
7887 OpBuilder &builder, OperationState &
result, Value mask,
7888 Operation *maskableOp,
7889 function_ref<
void(OpBuilder &, Operation *)> maskRegionBuilder) {
7890 assert(maskRegionBuilder &&
7891 "builder callback for 'maskRegion' must be present");
7893 result.addOperands(mask);
7894 OpBuilder::InsertionGuard guard(builder);
7895 Region *maskRegion =
result.addRegion();
7897 maskRegionBuilder(builder, maskableOp);
7902 Value mask, Operation *maskableOp,
7903 function_ref<
void(OpBuilder &, Operation *)> maskRegionBuilder) {
7904 build(builder,
result, resultTypes, mask, Value(), maskableOp,
7910 Value mask, Value passthru, Operation *maskableOp,
7911 function_ref<
void(OpBuilder &, Operation *)> maskRegionBuilder) {
7912 build(builder,
result, mask, maskableOp, maskRegionBuilder);
7914 result.addOperands(passthru);
7915 result.addTypes(resultTypes);
7918ParseResult MaskOp::parse(OpAsmParser &parser, OperationState &
result) {
7920 result.regions.reserve(1);
7921 Region &maskRegion = *
result.addRegion();
7926 OpAsmParser::UnresolvedOperand mask;
7931 OpAsmParser::UnresolvedOperand passthru;
7933 if (parsePassthru.succeeded() && parser.
parseOperand(passthru))
7940 MaskOp::ensureTerminator(maskRegion, builder,
result.location);
7951 SmallVector<Type> resultTypes;
7954 result.types.append(resultTypes);
7960 if (parsePassthru.succeeded()) {
7961 if (resultTypes.empty())
7964 "expects a result if passthru operand is provided");
7973void mlir::vector::MaskOp::print(OpAsmPrinter &p) {
7974 p <<
" " << getMask();
7976 p <<
", " << getPassthru();
7980 Block *singleBlock = &getMaskRegion().getBlocks().front();
7987 p <<
" : " << getMask().getType();
7988 if (getNumResults() > 0)
7989 p <<
" -> " << getResultTypes();
7992void MaskOp::ensureTerminator(Region ®ion, Builder &builder, Location loc) {
7995 OpTrait::SingleBlockImplicitTerminator<vector::YieldOp>::Impl<
7996 MaskOp>::ensureTerminator(region, builder, loc);
8002 if (isa<vector::YieldOp>(block.
back()))
8010 OpTrait::SingleBlockImplicitTerminator<vector::YieldOp>::Impl<
8011 MaskOp>::ensureTerminator(region, builder, loc);
8017 Operation *maskedOp = &block.
front();
8018 opBuilder.setInsertionPointToEnd(&block);
8019 vector::YieldOp::create(opBuilder, loc, maskedOp->
getResults());
8022LogicalResult MaskOp::verify() {
8024 Block &block = getMaskRegion().getBlocks().
front();
8026 return emitOpError(
"expects a terminator within the mask region");
8029 if (numMaskRegionOps > 2)
8030 return emitOpError(
"expects only one operation to mask");
8033 auto terminator = dyn_cast<vector::YieldOp>(block.
back());
8035 return emitOpError(
"expects a terminator within the mask region");
8037 if (terminator->getNumOperands() != getNumResults())
8039 "expects number of results to match mask region yielded values");
8042 if (numMaskRegionOps == 1)
8045 auto maskableOp = dyn_cast<MaskableOpInterface>(block.
front());
8047 return emitOpError(
"expects a MaskableOpInterface within the mask region");
8051 return emitOpError(
"expects number of results to match maskable operation "
8052 "number of results");
8054 if (!llvm::equal(maskableOp->
getResults(), terminator.getOperands()))
8055 return emitOpError(
"expects all the results from the MaskableOpInterface "
8056 "to match all the values returned by the terminator");
8058 if (!llvm::equal(maskableOp->
getResultTypes(), getResultTypes()))
8060 "expects result type to match maskable operation result type");
8063 [](Type t) { return llvm::isa<VectorType>(t); }) > 1)
8064 return emitOpError(
"multiple vector results not supported");
8067 Type expectedMaskType = maskableOp.getExpectedMaskType();
8068 if (getMask().
getType() != expectedMaskType)
8070 << expectedMaskType <<
" mask for the maskable operation";
8073 Value passthru = getPassthru();
8075 if (!maskableOp.supportsPassthru())
8077 "doesn't expect a passthru argument for this maskable operation");
8080 return emitOpError(
"expects result when passthru argument is provided");
8083 return emitOpError(
"expects passthru type to match result type");
8103static LogicalResult foldEmptyMaskOp(MaskOp maskOp, MaskOp::FoldAdaptor adaptor,
8104 SmallVectorImpl<OpFoldResult> &results) {
8105 if (!maskOp.isEmpty() || maskOp.hasPassthru())
8108 Block *block = maskOp.getMaskBlock();
8109 auto terminator = cast<vector::YieldOp>(block->
front());
8110 if (terminator.getNumOperands() == 0)
8114 llvm::append_range(results, terminator.getOperands());
8118LogicalResult MaskOp::fold(FoldAdaptor adaptor,
8119 SmallVectorImpl<OpFoldResult> &results) {
8120 if (succeeded(foldEmptyMaskOp(*
this, adaptor, results)))
8130 Operation *maskableOp = getMaskableOp();
8136 llvm::append_range(results, maskableOp->
getResults());
8152class CanonializeEmptyMaskOp :
public OpRewritePattern<MaskOp> {
8155 LogicalResult matchAndRewrite(MaskOp maskOp,
8156 PatternRewriter &rewriter)
const override {
8157 if (!maskOp.isEmpty())
8160 if (!maskOp.hasPassthru())
8167 VectorType maskType = maskOp.getMask().getType();
8168 for (Type resultType : maskOp.getResultTypes()) {
8169 auto vecResultType = dyn_cast<VectorType>(resultType);
8170 if (!vecResultType || vecResultType.getShape() != maskType.getShape())
8174 Block *block = maskOp.getMaskBlock();
8175 auto terminator = cast<vector::YieldOp>(block->
front());
8176 assert(terminator.getNumOperands() == 1 &&
8177 "expected one result when passthru is provided");
8180 maskOp, maskOp.getResultTypes(), maskOp.getMask(),
8181 terminator.getOperand(0), maskOp.getPassthru());
8187void MaskOp::getCanonicalizationPatterns(RewritePatternSet &results,
8188 MLIRContext *context) {
8189 results.
add<CanonializeEmptyMaskOp>(context);
8195Operation *MaskOp::getMaskableOp() {
8196 Block *block = getMaskBlock();
8200 return &block->
front();
8204bool MaskOp::hasPassthru() {
return getPassthru() != Value(); }
8210LogicalResult ScanOp::verify() {
8211 VectorType srcType = getSourceType();
8212 VectorType initialType = getInitialValueType();
8214 int64_t srcRank = srcType.getRank();
8215 int64_t reductionDim = getReductionDim();
8216 if (reductionDim >= srcRank)
8218 << reductionDim <<
" has to be less than " << srcRank;
8221 int64_t initialValueRank = initialType.getRank();
8222 if (initialValueRank != srcRank - 1)
8224 << initialValueRank <<
" has to be equal to " << srcRank - 1;
8227 ArrayRef<int64_t> srcShape = srcType.getShape();
8228 ArrayRef<int64_t> initialValueShapes = initialType.getShape();
8229 SmallVector<int64_t> expectedShape;
8230 for (
int i = 0; i < srcRank; i++) {
8231 if (i != reductionDim)
8232 expectedShape.push_back(srcShape[i]);
8234 if (!llvm::equal(initialValueShapes, expectedShape)) {
8235 return emitOpError(
"incompatible input/initial value shapes");
8239 Type eltType = getDestType().getElementType();
8242 << eltType <<
" for kind '" << stringifyCombiningKind(getKind())
8249 RewritePatternSet &patterns, PatternBenefit benefit) {
8251 .
add<CreateMaskFolder, MaskedLoadFolder, MaskedStoreFolder, GatherFolder,
8252 ScatterFolder, ExpandLoadFolder, CompressStoreFolder,
8253 StridedSliceConstantMaskFolder, TransposeFolder>(
8258 CombiningKind kind, Value v1, Value acc,
8259 arith::FastMathFlagsAttr fastmath,
8266 case CombiningKind::ADD:
8268 result =
b.createOrFold<arith::AddIOp>(loc, v1, acc);
8269 else if (llvm::isa<FloatType>(t1) && llvm::isa<FloatType>(tAcc))
8270 result =
b.createOrFold<arith::AddFOp>(loc, v1, acc, fastmath);
8272 llvm_unreachable(
"invalid value types for ADD reduction");
8274 case CombiningKind::AND:
8276 result =
b.createOrFold<arith::AndIOp>(loc, v1, acc);
8278 case CombiningKind::MAXNUMF:
8279 assert(llvm::isa<FloatType>(t1) && llvm::isa<FloatType>(tAcc) &&
8280 "expected float values");
8281 result =
b.createOrFold<arith::MaxNumFOp>(loc, v1, acc, fastmath);
8283 case CombiningKind::MAXIMUMF:
8284 assert(llvm::isa<FloatType>(t1) && llvm::isa<FloatType>(tAcc) &&
8285 "expected float values");
8286 result =
b.createOrFold<arith::MaximumFOp>(loc, v1, acc, fastmath);
8288 case CombiningKind::MINNUMF:
8289 assert(llvm::isa<FloatType>(t1) && llvm::isa<FloatType>(tAcc) &&
8290 "expected float values");
8291 result =
b.createOrFold<arith::MinNumFOp>(loc, v1, acc, fastmath);
8293 case CombiningKind::MINIMUMF:
8294 assert(llvm::isa<FloatType>(t1) && llvm::isa<FloatType>(tAcc) &&
8295 "expected float values");
8296 result =
b.createOrFold<arith::MinimumFOp>(loc, v1, acc, fastmath);
8298 case CombiningKind::MAXSI:
8300 result =
b.createOrFold<arith::MaxSIOp>(loc, v1, acc);
8302 case CombiningKind::MINSI:
8304 result =
b.createOrFold<arith::MinSIOp>(loc, v1, acc);
8306 case CombiningKind::MAXUI:
8308 result =
b.createOrFold<arith::MaxUIOp>(loc, v1, acc);
8310 case CombiningKind::MINUI:
8312 result =
b.createOrFold<arith::MinUIOp>(loc, v1, acc);
8314 case CombiningKind::MUL:
8316 result =
b.createOrFold<arith::MulIOp>(loc, v1, acc);
8317 else if (llvm::isa<FloatType>(t1) && llvm::isa<FloatType>(tAcc))
8318 result =
b.createOrFold<arith::MulFOp>(loc, v1, acc, fastmath);
8320 llvm_unreachable(
"invalid value types for MUL reduction");
8322 case CombiningKind::OR:
8324 result =
b.createOrFold<arith::OrIOp>(loc, v1, acc);
8326 case CombiningKind::XOR:
8328 result =
b.createOrFold<arith::XOrIOp>(loc, v1, acc);
8332 assert(
result &&
"unknown CombiningKind");
8340void StepOp::inferResultRanges(ArrayRef<ConstantIntRanges> argRanges,
8342 auto resultType = cast<VectorType>(
getType());
8343 if (resultType.isScalable()) {
8349 uint64_t maxIndex = resultType.getDimSize(0) - 1;
8350 APInt umin = APInt::getZero(bitwidth);
8351 APInt umax = APInt::getMaxValue(bitwidth).ugt(maxIndex)
8352 ? APInt(bitwidth, maxIndex)
8353 : APInt::getMaxValue(bitwidth);
8384struct StepCompareFolder :
public OpRewritePattern<StepOp> {
8387 LogicalResult matchAndRewrite(StepOp stepOp,
8388 PatternRewriter &rewriter)
const override {
8389 const int64_t stepSize = stepOp.getResult().getType().getNumElements();
8391 for (OpOperand &use : stepOp.getResult().getUses()) {
8392 auto cmpiOp = dyn_cast<arith::CmpIOp>(use.getOwner());
8397 const unsigned stepOperandNumber = use.getOperandNumber();
8398 if (stepOperandNumber != 0)
8402 unsigned constOperandNumber = 1;
8403 Value otherOperand = cmpiOp.getOperand(constOperandNumber);
8404 std::optional<int64_t> maybeConstValue =
8406 if (!maybeConstValue.has_value())
8409 int64_t constValue = maybeConstValue.value();
8410 arith::CmpIPredicate pred = cmpiOp.getPredicate();
8412 auto maybeSplat = [&]() -> std::optional<bool> {
8414 if ((pred == arith::CmpIPredicate::ult ||
8415 pred == arith::CmpIPredicate::uge) &&
8416 stepSize <= constValue)
8417 return pred == arith::CmpIPredicate::ult;
8420 if ((pred == arith::CmpIPredicate::ule ||
8421 pred == arith::CmpIPredicate::ugt) &&
8422 stepSize - 1 <= constValue) {
8423 return pred == arith::CmpIPredicate::ule;
8427 if ((pred == arith::CmpIPredicate::eq ||
8428 pred == arith::CmpIPredicate::ne) &&
8429 stepSize <= constValue)
8430 return pred == arith::CmpIPredicate::ne;
8432 return std::nullopt;
8435 if (!maybeSplat.has_value())
8440 auto type = dyn_cast<VectorType>(cmpiOp.getResult().getType());
8445 Value splat = mlir::arith::ConstantOp::create(rewriter, cmpiOp.getLoc(),
8457void StepOp::getCanonicalizationPatterns(RewritePatternSet &results,
8458 MLIRContext *context) {
8459 results.
add<StepCompareFolder>(context);
8469 Operation *maskableOp) {
8470 assert(maskableOp->
getBlock() &&
"MaskableOp must be inserted into a block");
8482 Operation *maskableOp, Value mask,
8487 return MaskOp::create(builder, maskableOp->
getLoc(),
8490 return MaskOp::create(builder, maskableOp->
getLoc(),
8503 Value newValue, Value passthru) {
8507 return arith::SelectOp::create(builder, newValue.
getLoc(), newValue.
getType(),
8508 mask, newValue, passthru);
8519struct InterleaveDeinterleaveFolder :
public OpRewritePattern<InterleaveOp> {
8522 LogicalResult matchAndRewrite(InterleaveOp interleaveOp,
8523 PatternRewriter &rewriter)
const override {
8524 auto lhsDefOp = interleaveOp.getLhs().getDefiningOp<DeinterleaveOp>();
8525 auto rhsDefOp = interleaveOp.getRhs().getDefiningOp<DeinterleaveOp>();
8526 if (!lhsDefOp || !rhsDefOp || lhsDefOp != rhsDefOp)
8528 for (
auto [idx, operand] : llvm::enumerate(interleaveOp.getOperands())) {
8529 if (cast<OpResult>(operand).getResultNumber() != idx)
8532 rewriter.
replaceOp(interleaveOp, lhsDefOp.getSource());
8538void InterleaveOp::getCanonicalizationPatterns(RewritePatternSet &results,
8539 MLIRContext *context) {
8540 results.
add<InterleaveDeinterleaveFolder>(context);
8543OpFoldResult InterleaveOp::fold(FoldAdaptor adaptor) {
8545 auto splat = dyn_cast_if_present<SplatElementsAttr>(adaptor.getLhs());
8546 if (!splat || adaptor.getLhs() != adaptor.getRhs())
8548 return SplatElementsAttr::get(getResultVectorType(),
8549 splat.getSplatValue<Attribute>());
8552std::optional<SmallVector<int64_t, 4>> InterleaveOp::getShapeForUnroll() {
8553 return llvm::to_vector<4>(getResultVectorType().
getShape());
8560std::optional<SmallVector<int64_t, 4>> DeinterleaveOp::getShapeForUnroll() {
8561 return llvm::to_vector<4>(getResultVectorType().
getShape());
8568#define GET_ATTRDEF_CLASSES
8569#include "mlir/Dialect/Vector/IR/VectorAttributes.cpp.inc"
8571#define GET_OP_CLASSES
8572#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 SmallVector< unsigned > extractPosition(ArrayRef< int64_t > indices)
Convert the value of a DenseI64ArrayAttr to a vector of unsigned indices.
*if copies could not be generated due to yet unimplemented cases *copyInPlacementStart and copyOutPlacementStart in copyPlacementBlock *specify the insertion points where the incoming copies and outgoing should be inserted(the insertion happens right before the *insertion point). Since `begin` can itself be invalidated due to the memref *rewriting done from this method
static ParseResult parseBoolAttr(OpAsmParser &parser, BoolAttr &result)
static void printBoolAttr(OpAsmPrinter &printer, Operation *, BoolAttr attr)
static std::optional< VectorShape > vectorShape(Type type)
static Value max(ImplicitLocOpBuilder &builder, Value value, Value bound)
static Value min(ImplicitLocOpBuilder &builder, Value value, Value bound)
static void contract(RootOrderingGraph &graph, ArrayRef< Value > cycle, const DenseMap< Value, unsigned > &parentDepths, DenseMap< Value, Value > &actualSource, DenseMap< Value, Value > &actualTarget)
Contracts the specified cycle in the given graph in-place.
static Type getElementType(Type type, ArrayRef< int32_t > indices, function_ref< InFlightDiagnostic(StringRef)> emitErrorFn)
Walks the given type hierarchy with the given indices, potentially down to component granularity,...
static Value broadcast(Location loc, Value toBroadcast, unsigned numElements, const TypeConverter &typeConverter, ConversionPatternRewriter &rewriter)
Broadcasts the value to vector with numElements number of elements.
static VectorType getVectorType(Type scalarTy, const VectorizationStrategy *strategy)
Returns the vector type resulting from applying the provided vectorization strategy on the scalar typ...
static ArrayRef< int64_t > getShape(Type type)
Returns the shape of the given type.
static MaskFormat getMaskFormat(Value mask)
Helper method to classify a mask value.
static OpFoldResult foldShuffleIdentityMask(ShuffleOp op)
Fold shuffle V1, V2, [0, 1, 2, 3] : <4xi32>, <2xi32> -> V1.
static LogicalResult foldExtractOpFromExtractChain(ExtractOp extractOp)
Fold the result of chains of ExtractOp in place by simply concatenating the positions.
static OpFoldResult foldFromElementsToElements(FromElementsOp fromElementsOp)
Folds vector.from_elements(vector.to_elements(vector)) into vector.
static bool hasZeroDimVectors(Operation *op)
Returns true if the operation has a 0-D vector type operand or result.
static void printTransferAttrs(OpAsmPrinter &p, VectorTransferOpInterface op)
static Value foldScalarExtractFromFromElements(ExtractOp extractOp)
Try to fold the extraction of a scalar from a vector defined by vector.from_elements.
static Attribute convertNumericAttr(Attribute attr, Type expectedType)
Converts numeric attributes to the expected type.
static Value foldExtractFromExtractStrided(ExtractOp extractOp)
Fold an ExtractOp from ExtractStridedSliceOp.
static llvm::SetVector< int64_t > computeBroadcastedUnitDims(ArrayRef< int64_t > srcShape, ArrayRef< int64_t > dstShape)
Return the dimensions of the result vector that were formerly ones in the source tensor and thus corr...
static Value foldExtractFromBroadcast(ExtractOp extractOp)
Fold extract(broadcast(X)) to either extract(X) or just X.
static LogicalResult foldToElementsFromElements(ToElementsOp toElementsOp, SmallVectorImpl< OpFoldResult > &results)
Folds vector.to_elements(vector.from_elements(e0, e1, ...)) into (e0, e1, ...).
static Attribute foldPoisonSrcExtractOp(Attribute srcAttr)
Fold a vector extract from is a poison source.
static LogicalResult foldBroadcastOfShapeCast(BroadcastOp broadcastOp)
static OpFoldResult foldShufflePoisonInputs(MLIRContext *context, Attribute v1Attr, Attribute v2Attr)
Fold shuffle poison, poison -> poison.
static bool isSupportedCombiningKind(CombiningKind combiningKind, Type elementType)
static Attribute foldPoisonIndexInsertExtractOp(MLIRContext *context, ArrayRef< int64_t > staticPos, int64_t poisonVal)
Fold an insert or extract operation into an poison value when a poison index is found at any dimensio...
MaskFormat
Helper enum to classify mask value.
static ArrayAttr makeI64ArrayAttr(ArrayRef< int64_t > values, MLIRContext *context)
static unsigned getEffectiveVectorRankForXferOp(ShapedType shapedType, VectorType vectorType)
Returns the effective rank of the vector to read/write for Xfer Ops.
static OpFoldResult foldFromElementsToConstant(FromElementsOp fromElementsOp, ArrayRef< Attribute > elements)
Fold vector.from_elements to a constant when all operands are constants.
static LogicalResult incSlicePosition(MutableArrayRef< int64_t > position, ArrayRef< int64_t > shape, ArrayRef< int64_t > offsets)
static Value extractInsertFoldConstantOp(OpType op, AdaptorType adaptor, SmallVectorImpl< Value > &operands)
If the dynamic indices of extractOp or insertOp are in fact constants, then fold it.
static LogicalResult foldToElementsOfBroadcast(ToElementsOp toElementsOp, SmallVectorImpl< OpFoldResult > &results)
Folds vector.to_elements(vector.broadcast(x)) for the scalar case only.
static bool isStepIndexArray(ArrayRef< T > idxArr, uint64_t begin, size_t width)
static LogicalResult isIntegerArrayAttrConfinedToRange(OpType op, ArrayAttr arrayAttr, int64_t min, int64_t max, StringRef attrName, bool halfOpen=true)
static bool haveSameDefiningOp(OperandRange operands, Operation *defOp)
Returns true if all the operands are defined by defOp.
static int64_t getResultIndex(AffineMap map, AffineExpr targetExpr)
static LogicalResult isIntegerArrayAttrConfinedToShape(OpType op, ArrayAttr arrayAttr, ArrayRef< int64_t > shape, StringRef attrName, bool halfOpen=true, int64_t min=0)
static bool isSplatWriteConsistentWithMaskedRead(vector::TransferWriteOp write, vector::TransferReadOp read)
Check if write is of a constant splat and the masked read is padded with the same splat value – meani...
static LogicalResult isSumOfIntegerArrayAttrConfinedToShape(OpType op, ArrayAttr arrayAttr1, ArrayAttr arrayAttr2, ArrayRef< int64_t > shape, StringRef attrName1, StringRef attrName2, bool halfOpen=true, int64_t min=1)
static Attribute foldDenseElementsAttrDestInsertOp(InsertOp insertOp, Attribute srcAttr, Attribute dstAttr, int64_t maxVectorSizeFoldThreshold)
static LogicalResult foldTransferFullMask(TransferOp op)
static SmallVector< IntType > extractVector(ArrayAttr arrayAttr)
static std::vector< std::pair< int64_t, int64_t > > getDimMap(ArrayRef< AffineMap > indexingMaps, ArrayAttr iteratorTypes, IteratorType targetIteratorType, MLIRContext *context)
static bool isValidPositiveIndexOrPoison(int64_t index, int64_t poisonValue, int64_t maxIndex)
static OpFoldResult foldShuffleConstantInputs(ShuffleOp op, Attribute v1Attr, Attribute v2Attr)
Fold a shuffle of constant 1-D inputs by evaluating the mask.
static OpFoldResult foldExtractStridedSliceNonSplatConstant(ExtractStridedSliceOp op, Attribute foldInput)
static LogicalResult verifyPermutationMap(AffineMap permutationMap, EmitFun emitOpError)
static LogicalResult rewriteFromElementsAsBroadcast(FromElementsOp fromElementsOp, PatternRewriter &rewriter)
Rewrite vector.from_elements as vector.broadcast if the elements are the same.
static Value foldInsertUseChain(InsertOp insertOp)
Folder to replace the dest operand of the insert op with the root dest of the insert op use chain.
static bool isBroadcastLike(Operation *op)
All BroadcastOps, as well as ShapeCastOps that only prepend 1s, are considered to be 'broadcastlike'.
static LogicalResult isIntegerArrayAttrSmallerThanShape(OpType op, ArrayAttr arrayAttr, ArrayRef< int64_t > shape, StringRef attrName)
static Value foldExtractFromShapeCast(ExtractOp extractOp)
static LogicalResult verifyTransferOp(VectorTransferOpInterface op, ShapedType shapedType, VectorType vectorType, VectorType maskType, VectorType inferredMaskType, AffineMap permutationMap, ArrayAttr inBounds)
static bool isInBounds(TransferOp op, int64_t resultIdx, int64_t indicesIdx)
static LogicalResult verifyOutputShape(ContractionOp op, VectorType lhsType, VectorType rhsType, Type accType, Type resType, const std::vector< std::pair< int64_t, int64_t > > &contractingDimMap, const std::vector< std::pair< int64_t, int64_t > > &batchDimMap)
static bool verifyDimMap(VectorType lhsType, VectorType rhsType, const std::vector< std::pair< int64_t, int64_t > > &map)
static Type inferStridedSliceOpResultType(VectorType vectorType, ArrayAttr offsets, ArrayAttr sizes, ArrayAttr strides)
static OpFoldResult foldShufflePoisonOperandToMask(ShuffleOp op)
If a shuffle operand is poison, replace all mask indices that reference it with kPoisonIndex.
static LogicalResult foldSize1TransferPermutationMap(TransferOp op)
When the vector type is vector<1xT>, the permutation map is irrelevant: the single vector lane always...
static Value foldExtractFromShuffle(ExtractOp extractOp)
Fold extractOp coming from ShuffleOp.
static LogicalResult foldTransferInBoundsAttribute(TransferOp op)
static Value foldExtractStridedOpFromInsertChain(ExtractOp extractOp)
Fold extract_op fed from a chain of insertStridedSlice ops.
static int64_t calculateInsertPosition(VectorType destTy, ArrayRef< int64_t > positions)
static Attribute foldDenseElementsAttrSrcExtractOp(ExtractOp extractOp, Attribute srcAttr)
Fold a vector extract extracting from a DenseElementsAttr.
static void populateFromInt64AttrArray(ArrayAttr arrayAttr, SmallVectorImpl< int64_t > &results)
Rewrite from_elements on multiple scalar extracts as a shape_cast on a single extract.
Base type for affine expression.
A multi-dimensional affine map Affine map's are immutable like Type's, and they are uniqued.
static AffineMap getMinorIdentityMap(unsigned dims, unsigned results, MLIRContext *context)
Returns an identity affine map (d0, ..., dn) -> (dp, ..., dn) on the most minor dimensions.
MLIRContext * getContext() const
bool isMinorIdentity() const
Returns true if this affine map is a minor identity, i.e.
unsigned getDimPosition(unsigned idx) const
Extracts the position of the dimensional expression at the given result, when the caller knows it is ...
static AffineMap get(MLIRContext *context)
Returns a zero result affine map with no dimensions or symbols: () -> ().
bool isProjectedPermutation(bool allowZeroInResults=false) const
Returns true if the AffineMap represents a subset (i.e.
unsigned getNumSymbols() const
unsigned getNumDims() const
ArrayRef< AffineExpr > getResults() const
unsigned getNumResults() const
static SmallVector< AffineMap, 4 > inferFromExprList(ArrayRef< ArrayRef< AffineExpr > > exprsList, MLIRContext *context)
Returns a vector of AffineMaps; each with as many results as exprs.size(), as many dims as the larges...
unsigned getNumInputs() const
AffineExpr getResult(unsigned idx) const
static AffineMap getPermutationMap(ArrayRef< unsigned > permutation, MLIRContext *context)
Returns an AffineMap representing a permutation.
SmallVector< unsigned > getBroadcastDims() const
Returns the list of broadcast dimensions (i.e.
AffineMap compose(AffineMap map) const
Returns the AffineMap resulting from composing this with map.
@ Square
Square brackets surrounding zero or more operands.
virtual ParseResult parseColonTypeList(SmallVectorImpl< Type > &result)=0
Parse a colon followed by a type list, which must have at least one type.
virtual Builder & getBuilder() const =0
Return a builder which provides useful access to MLIRContext, global objects like types and attribute...
virtual ParseResult parseOptionalAttrDict(NamedAttrList &result)=0
Parse a named dictionary into 'result' if it is present.
MLIRContext * getContext() const
virtual InFlightDiagnostic emitError(SMLoc loc, const Twine &message={})=0
Emit a diagnostic at the specified location and return failure.
ParseResult addTypeToList(Type type, SmallVectorImpl< Type > &result)
Add the specified type to the end of the specified type list and return success.
virtual ParseResult parseColonType(Type &result)=0
Parse a colon followed by a type.
virtual SMLoc getCurrentLocation()=0
Get the location of the next token and store it into the argument.
virtual ParseResult parseOptionalComma()=0
Parse a , token if present.
virtual SMLoc getNameLoc() const =0
Return the location of the original name token.
virtual ParseResult parseType(Type &result)=0
Parse a type.
virtual ParseResult parseComma()=0
Parse a , token.
virtual ParseResult parseOptionalArrowTypeList(SmallVectorImpl< Type > &result)=0
Parse an optional arrow followed by a type list.
ParseResult parseKeywordType(const char *keyword, Type &result)
Parse a keyword followed by a type.
virtual ParseResult parseAttribute(Attribute &result, Type type={})=0
Parse an arbitrary attribute of a given type and return it in result.
virtual void printAttribute(Attribute attr)
Base storage class appearing in an attribute.
Attributes are known-constant values of operations.
Dialect & getDialect() const
Get the dialect this attribute is registered to.
OpListType & getOperations()
static BoolAttr get(MLIRContext *context, bool value)
This class is a general helper class for creating context-global objects like types,...
IntegerAttr getIndexAttr(int64_t value)
DenseI32ArrayAttr getDenseI32ArrayAttr(ArrayRef< int32_t > values)
IntegerAttr getIntegerAttr(Type type, int64_t value)
DenseI64ArrayAttr getDenseI64ArrayAttr(ArrayRef< int64_t > values)
IntegerAttr getI64IntegerAttr(int64_t value)
IntegerType getIntegerType(unsigned width)
TypedAttr getZeroAttr(Type type)
ArrayAttr getArrayAttr(ArrayRef< Attribute > value)
MLIRContext * getContext() const
ArrayAttr getI64ArrayAttr(ArrayRef< int64_t > values)
ArrayAttr getBoolArrayAttr(ArrayRef< bool > values)
ArrayAttr getAffineMapArrayAttr(ArrayRef< AffineMap > values)
static ConstantIntRanges fromUnsigned(const APInt &umin, const APInt &umax)
Create an ConstantIntRanges with the unsigned minimum and maximum equal to umin and umax and the sign...
static unsigned getStorageBitwidth(Type type)
Return the bitwidth that should be used for integer ranges describing type.
The main mechanism for performing data layout queries.
static DataLayout closest(Operation *op)
Returns the layout of the closest parent operation carrying layout info.
llvm::TypeSize getTypeSizeInBits(Type t) const
Returns the size in bits of the given type in the current scope.
An attribute that represents a reference to a dense vector or tensor object.
std::enable_if_t<!std::is_base_of< Attribute, T >::value||std::is_same< Attribute, T >::value, T > getSplatValue() const
Return the splat value for this attribute.
bool isSplat() const
Returns true if this attribute corresponds to a splat, i.e.
static DenseElementsAttr get(ShapedType type, ArrayRef< Attribute > values)
Constructs a dense elements attribute from an array of element values.
virtual Operation * materializeConstant(OpBuilder &builder, Attribute value, Type type, Location loc)
Registered hook to materialize a single constant operation from a given attribute value with the desi...
This is a utility class for mapping one set of IR entities to another.
void map(Value from, Value to)
Inserts a new mapping for 'from' to 'to'.
This class defines the main interface for locations in MLIR and acts as a non-nullable wrapper around...
MLIRContext is the top-level object for a collection of MLIR operations.
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)