43#include "llvm/ADT/ArrayRef.h"
44#include "llvm/ADT/Repeated.h"
45#include "llvm/ADT/STLExtras.h"
46#include "llvm/ADT/SmallVector.h"
47#include "llvm/ADT/SmallVectorExtras.h"
48#include "llvm/ADT/StringSet.h"
49#include "llvm/Support/Casting.h"
55#include "mlir/Dialect/Vector/IR/VectorDialect.cpp.inc"
57#include "mlir/Dialect/Vector/IR/VectorEnums.cpp.inc"
74 if (
auto c = mask.getDefiningOp<arith::ConstantOp>()) {
78 if (
auto denseElts = llvm::dyn_cast<DenseIntElementsAttr>(c.getValue())) {
80 for (
bool b : denseElts.getValues<
bool>())
83 else if (!
b && val <= 0)
92 }
else if (
auto m = mask.getDefiningOp<ConstantMaskOp>()) {
97 auto shape = m.getType().getShape();
100 for (
auto [maskIdx, dimSize] : llvm::zip_equal(masks,
shape)) {
101 if (maskIdx < dimSize)
110 }
else if (
auto m = mask.getDefiningOp<CreateMaskOp>()) {
114 auto maskOperands = m.getOperands();
115 for (
Value operand : maskOperands) {
116 if (
auto constantOp = operand.getDefiningOp<arith::ConstantOp>()) {
118 llvm::cast<IntegerAttr>(constantOp.getValue()).getInt();
131 vector::YieldOp::create(builder, loc);
137 switch (combiningKind) {
138 case CombiningKind::ADD:
139 case CombiningKind::MUL:
141 case CombiningKind::MINUI:
142 case CombiningKind::MINSI:
143 case CombiningKind::MAXUI:
144 case CombiningKind::MAXSI:
145 case CombiningKind::AND:
146 case CombiningKind::OR:
147 case CombiningKind::XOR:
149 case CombiningKind::MINNUMF:
150 case CombiningKind::MAXNUMF:
151 case CombiningKind::MINIMUMF:
152 case CombiningKind::MAXIMUMF:
153 case CombiningKind::MINIMUMNUMF:
154 case CombiningKind::MAXIMUMNUMF:
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) {
246 if (defWrite.isMasked() || read.isMasked())
248 return !defWrite.hasOutOfBoundsDim() &&
249 defWrite.getIndices() == read.getIndices() &&
250 defWrite.getVectorType() == read.getVectorType() &&
251 defWrite.getPermutationMap() == read.getPermutationMap() &&
252 ((!defWrite.getMask() && !read.getMask()) ||
257 vector::TransferWriteOp priorWrite) {
265 if (write.isMasked())
267 return priorWrite.getIndices() == write.getIndices() &&
268 priorWrite.getMask() == write.getMask() &&
269 priorWrite.getVectorType() == write.getVectorType() &&
270 priorWrite.getPermutationMap() == write.getPermutationMap();
274 VectorTransferOpInterface transferA, VectorTransferOpInterface transferB,
275 bool testDynamicValueUsingBounds) {
277 if (transferA.getVectorType() != transferB.getVectorType())
279 unsigned rankOffset = transferA.getLeadingShapedRank();
280 for (
unsigned i = 0, e = transferA.getIndices().size(); i < e; i++) {
281 Value indexA = transferA.getIndices()[i];
282 Value indexB = transferB.getIndices()[i];
286 if (i < rankOffset) {
289 if (cstIndexA.has_value() && cstIndexB.has_value()) {
290 if (*cstIndexA != *cstIndexB)
294 if (testDynamicValueUsingBounds) {
297 FailureOr<uint64_t> delta =
299 if (succeeded(delta) && *delta != 0)
302 FailureOr<bool> testEqual =
304 if (succeeded(testEqual) && !testEqual.value())
310 int64_t vectorDim = transferA.getVectorType().getDimSize(i - rankOffset);
311 if (cstIndexA.has_value() && cstIndexB.has_value()) {
312 int64_t distance = std::abs(*cstIndexA - *cstIndexB);
313 if (distance >= vectorDim)
317 if (testDynamicValueUsingBounds) {
320 FailureOr<int64_t> delta =
322 if (succeeded(delta) && std::abs(*delta) >= vectorDim)
325 FailureOr<int64_t> computeDelta =
327 if (succeeded(computeDelta)) {
328 if (std::abs(computeDelta.value()) >= vectorDim)
338 VectorTransferOpInterface transferB,
339 bool testDynamicValueUsingBounds) {
340 if (transferA.getBase() != transferB.getBase())
343 testDynamicValueUsingBounds);
353 for (
auto [posInDim, dimSize, offsetInDim] :
354 llvm::reverse(llvm::zip_equal(position,
shape, offsets))) {
356 if (posInDim < dimSize + offsetInDim)
360 posInDim = offsetInDim;
370 llvm::transform(values, std::back_inserter(ints), [](
Value value) {
372 assert(constOp &&
"Unexpected non-constant index");
373 return constOp.value();
383 foldResults, std::back_inserter(ints), [](
OpFoldResult foldResult) {
384 assert(isa<Attribute>(foldResult) &&
"Unexpected non-constant index");
385 return cast<IntegerAttr>(cast<Attribute>(foldResult)).getInt();
395 llvm::transform(foldResults, std::back_inserter(values),
397 if (
auto attr = dyn_cast<Attribute>(foldResult))
399 builder, loc, cast<IntegerAttr>(attr).getInt())
402 return cast<Value>(foldResult);
413 auto lhs =
mul.getLhs();
414 auto rhs =
mul.getRhs();
415 if (lhs.getDefiningOp<vector::VectorScaleOp>())
417 if (rhs.getDefiningOp<vector::VectorScaleOp>())
427 if (
auto intAttr = dyn_cast<IntegerAttr>(attr)) {
428 if (
auto intType = dyn_cast<IntegerType>(expectedType)) {
429 if (intAttr.getType() != expectedType)
430 return IntegerAttr::get(expectedType, intAttr.getInt());
436 if (
auto floatAttr = dyn_cast<FloatAttr>(attr)) {
437 auto intType = dyn_cast<IntegerType>(expectedType);
441 APFloat floatVal = floatAttr.getValue();
442 APInt intVal = floatVal.bitcastToAPInt();
443 return IntegerAttr::get(expectedType, intVal);
452 Type srcType, VectorType dstVectorType,
453 std::pair<VectorDim, VectorDim> *mismatchingDims) {
455 if (isa<VectorElementTypeInterface>(srcType) && dstVectorType &&
459 VectorType srcVectorType = llvm::dyn_cast<VectorType>(srcType);
463 int64_t srcRank = srcVectorType.getRank();
464 int64_t dstRank = dstVectorType.getRank();
465 if (srcRank > dstRank)
469 int64_t lead = dstRank - srcRank;
470 for (
int64_t dimIdx = 0; dimIdx < srcRank; ++dimIdx) {
473 bool foundMismatchingDims =
false;
476 int64_t srcDim = srcVectorType.getDimSize(dimIdx);
477 int64_t dstDim = dstVectorType.getDimSize(lead + dimIdx);
478 if (srcDim != 1 && srcDim != dstDim)
479 foundMismatchingDims =
true;
482 bool srcDimScalableFlag = srcVectorType.getScalableDims()[dimIdx];
483 bool dstDimScalableFlag = dstVectorType.getScalableDims()[lead + dimIdx];
484 if ((srcDim == 1 && srcDimScalableFlag && dstDim != 1) ||
487 (srcDimScalableFlag != dstDimScalableFlag &&
488 (srcDim != 1 || srcDimScalableFlag)))
489 foundMismatchingDims =
true;
491 if (foundMismatchingDims) {
492 if (mismatchingDims !=
nullptr) {
493 mismatchingDims->first.dim = srcDim;
494 mismatchingDims->first.isScalable = srcDimScalableFlag;
496 mismatchingDims->second.dim = dstDim;
497 mismatchingDims->second.isScalable = dstDimScalableFlag;
539struct VectorInlinerInterface :
public DialectInlinerInterface {
540 using DialectInlinerInterface::DialectInlinerInterface;
549void VectorDialect::initialize() {
551#define GET_ATTRDEF_LIST
552#include "mlir/Dialect/Vector/IR/VectorAttributes.cpp.inc"
557#include "mlir/Dialect/Vector/IR/VectorOps.cpp.inc"
560 addInterfaces<VectorInlinerInterface>();
562 declarePromisedInterfaces<memref::IndexedAccessOpInterface, LoadOp, StoreOp,
563 MaskedLoadOp, MaskedStoreOp, ExpandLoadOp,
565 declarePromisedInterfaces<bufferization::BufferizableOpInterface,
566 TransferReadOp, TransferWriteOp, GatherOp, MaskOp,
568 declarePromisedInterfaces<SubsetOpInterface, TransferReadOp,
570 declarePromisedInterface<SubsetExtractionOpInterface, TransferReadOp>();
571 declarePromisedInterface<SubsetInsertionOpInterface, TransferWriteOp>();
572 declarePromisedInterface<ConvertToLLVMPatternInterface, VectorDialect>();
583 return arith::ConstantOp::materialize(builder, value, type, loc);
599void vector::MultiDimReductionOp::build(
OpBuilder &builder,
603 arith::FastMathFlags fastMathFlags) {
605 for (
const auto &en : llvm::enumerate(reductionMask))
607 reductionDims.push_back(en.index());
608 build(builder,
result, kind, source,
acc, reductionDims, fastMathFlags);
611OpFoldResult MultiDimReductionOp::fold(FoldAdaptor adaptor) {
613 if (getReductionDims().empty())
618std::optional<SmallVector<int64_t, 4>>
619MultiDimReductionOp::getIterationShapeToUnroll() {
620 return llvm::to_vector<4>(getSourceVectorType().
getShape());
623LogicalResult MultiDimReductionOp::verify() {
625 int64_t sourceRank = getSourceVectorType().getRank();
627 for (
int64_t dim : getReductionDims()) {
628 if (dim < 0 || dim >= sourceRank)
629 return emitOpError(
"reduction dimension out of range: ") << dim;
631 return emitOpError(
"duplicate reduction dimension: ") << dim;
632 isReduced[dim] =
true;
637 Type inferredReturnType;
638 auto sourceScalableDims = getSourceVectorType().getScalableDims();
639 for (
auto [dimIdx, dimSize] :
640 llvm::enumerate(getSourceVectorType().
getShape()))
641 if (!isReduced[dimIdx]) {
642 targetShape.push_back(dimSize);
643 scalableDims.push_back(sourceScalableDims[dimIdx]);
646 if (targetShape.empty())
647 inferredReturnType = getSourceVectorType().getElementType();
649 inferredReturnType = VectorType::get(
650 targetShape, getSourceVectorType().
getElementType(), scalableDims);
651 if (
getType() != inferredReturnType)
652 return emitOpError() <<
"destination type " <<
getType()
653 <<
" is incompatible with source type "
654 << getSourceVectorType();
660Type MultiDimReductionOp::getExpectedMaskType() {
661 auto vecType = getSourceVectorType();
662 return VectorType::get(vecType.getShape(),
663 IntegerType::get(vecType.getContext(), 1),
664 vecType.getScalableDims());
673struct ElideUnitDimsInMultiDimReduction
677 LogicalResult matchAndRewrite(MultiDimReductionOp reductionOp,
678 PatternRewriter &rewriter)
const override {
679 ArrayRef<int64_t> shape = reductionOp.getSourceVectorType().getShape();
680 for (
const auto &dim :
enumerate(shape)) {
681 if (reductionOp.isReducedDim(dim.index()) && dim.value() != 1)
686 OpBuilder::InsertionGuard guard(rewriter);
689 if (reductionOp.isMasked()) {
691 rootOp = reductionOp.getMaskingOp();
692 mask = reductionOp.getMaskingOp().getMask();
694 rootOp = reductionOp;
697 Location loc = reductionOp.getLoc();
698 Value acc = reductionOp.getAcc();
700 if (
auto dstVecType = dyn_cast<VectorType>(reductionOp.getDestType())) {
702 VectorType newMaskType =
703 VectorType::get(dstVecType.getShape(), rewriter.
getI1Type(),
704 dstVecType.getScalableDims());
705 mask = vector::ShapeCastOp::create(rewriter, loc, newMaskType, mask);
707 cast = vector::ShapeCastOp::create(
708 rewriter, loc, reductionOp.getDestType(), reductionOp.getSource());
713 mask = vector::ExtractOp::create(rewriter, loc, mask);
714 cast = vector::ExtractOp::create(rewriter, loc, reductionOp.getSource());
719 cast, reductionOp.getFastmathAttr(), mask);
726void MultiDimReductionOp::getCanonicalizationPatterns(
728 results.
add<ElideUnitDimsInMultiDimReduction>(context);
737 arith::FastMathFlags fastMathFlags) {
743 arith::FastMathFlags fastMathFlags) {
745 llvm::cast<VectorType>(
vector.getType()).getElementType(), kind,
vector,
749LogicalResult ReductionOp::verify() {
751 int64_t rank = getSourceVectorType().getRank();
753 return emitOpError(
"unsupported reduction rank: ") << rank;
756 Type eltType = getDest().getType();
758 return emitOpError(
"unsupported reduction type '")
759 << eltType <<
"' for kind '" << stringifyCombiningKind(getKind())
768Type ReductionOp::getExpectedMaskType() {
769 auto vecType = getSourceVectorType();
770 return VectorType::get(vecType.getShape(),
771 IntegerType::get(vecType.getContext(), 1),
772 vecType.getScalableDims());
779 case arith::AtomicRMWKind::addf:
780 case arith::AtomicRMWKind::addi:
781 return vector::ReductionOp::create(builder,
vector.getLoc(),
782 CombiningKind::ADD,
vector);
783 case arith::AtomicRMWKind::mulf:
784 case arith::AtomicRMWKind::muli:
785 return vector::ReductionOp::create(builder,
vector.getLoc(),
786 CombiningKind::MUL,
vector);
787 case arith::AtomicRMWKind::minimumf:
788 return vector::ReductionOp::create(builder,
vector.getLoc(),
789 CombiningKind::MINIMUMF,
vector);
790 case arith::AtomicRMWKind::mins:
791 return vector::ReductionOp::create(builder,
vector.getLoc(),
792 CombiningKind::MINSI,
vector);
793 case arith::AtomicRMWKind::minu:
794 return vector::ReductionOp::create(builder,
vector.getLoc(),
795 CombiningKind::MINUI,
vector);
796 case arith::AtomicRMWKind::maximumf:
797 return vector::ReductionOp::create(builder,
vector.getLoc(),
798 CombiningKind::MAXIMUMF,
vector);
799 case arith::AtomicRMWKind::maxs:
800 return vector::ReductionOp::create(builder,
vector.getLoc(),
801 CombiningKind::MAXSI,
vector);
802 case arith::AtomicRMWKind::maxu:
803 return vector::ReductionOp::create(builder,
vector.getLoc(),
804 CombiningKind::MAXUI,
vector);
805 case arith::AtomicRMWKind::andi:
806 return vector::ReductionOp::create(builder,
vector.getLoc(),
807 CombiningKind::AND,
vector);
808 case arith::AtomicRMWKind::ori:
809 return vector::ReductionOp::create(builder,
vector.getLoc(),
810 CombiningKind::OR,
vector);
811 case arith::AtomicRMWKind::minnumf:
812 return vector::ReductionOp::create(builder,
vector.getLoc(),
813 CombiningKind::MINNUMF,
vector);
814 case arith::AtomicRMWKind::maxnumf:
815 return vector::ReductionOp::create(builder,
vector.getLoc(),
816 CombiningKind::MAXNUMF,
vector);
817 case arith::AtomicRMWKind::xori:
818 return vector::ReductionOp::create(builder,
vector.getLoc(),
819 CombiningKind::XOR,
vector);
827std::optional<SmallVector<int64_t, 4>>
828ReductionOp::getIterationShapeToUnroll() {
829 return llvm::to_vector<4>(getSourceVectorType().
getShape());
836 LogicalResult matchAndRewrite(ReductionOp reductionOp,
841 cast<vector::MaskableOpInterface>(reductionOp.getOperation());
844 if (maskableOp.isMasked()) {
846 rootOp = maskableOp.getMaskingOp();
847 mask = maskableOp.getMaskingOp().getMask();
849 rootOp = reductionOp;
852 auto vectorType = reductionOp.getSourceVectorType();
853 if (vectorType.getRank() != 0 && vectorType.getDimSize(0) != 1)
856 Location loc = reductionOp.getLoc();
858 mask = ExtractOp::create(rewriter, loc, mask);
859 Value
result = ExtractOp::create(rewriter, loc, reductionOp.getVector());
861 if (Value acc = reductionOp.getAcc())
864 reductionOp.getFastmathAttr(), mask);
874 results.
add<ElideSingleElementReduction>(context);
888 getIndexingMapsAttrName(
result.name),
892 getIteratorTypesAttrName(
result.name),
895 return IteratorTypeAttr::get(builder.getContext(), t);
903 build(builder,
result, lhs, rhs,
acc, indexingMaps, iteratorTypes,
904 ContractionOp::getDefaultKind());
910 ArrayAttr iteratorTypes, CombiningKind kind,
911 arith::FastMathFlags fastMathFlags) {
914 result.addAttribute(getIndexingMapsAttrName(
result.name), indexingMaps);
915 result.addAttribute(getIteratorTypesAttrName(
result.name), iteratorTypes);
917 CombiningKindAttr::get(builder.
getContext(), kind));
918 if (fastMathFlags != arith::FastMathFlags::none)
920 getFastmathAttrName(
result.name),
921 arith::FastMathFlagsAttr::get(builder.
getContext(), fastMathFlags));
932 DictionaryAttr dictAttr;
946 result.attributes.append(dictAttr.getValue().begin(),
947 dictAttr.getValue().end());
953 auto iteratorTypes = dyn_cast_or_null<ArrayAttr>(
954 result.attributes.get(getIteratorTypesAttrName(
result.name)));
955 if (!iteratorTypes) {
957 <<
"expected " << getIteratorTypesAttrName(
result.name)
958 <<
" array attribute";
963 for (StringRef s : iteratorTypes.getAsValueRange<StringAttr>()) {
964 auto maybeIteratorType = symbolizeIteratorType(s);
965 if (!maybeIteratorType.has_value())
966 return parser.
emitError(loc) <<
"unexpected iterator_type (" << s <<
")";
968 iteratorTypeAttrs.push_back(
969 IteratorTypeAttr::get(parser.
getContext(), maybeIteratorType.value()));
971 result.attributes.set(getIteratorTypesAttrName(
result.name),
974 if (!
result.attributes.get(getKindAttrName(
result.name))) {
976 getKindAttrName(
result.name),
977 CombiningKindAttr::get(
result.getContext(),
978 ContractionOp::getDefaultKind()));
980 if (masksInfo.empty())
982 if (masksInfo.size() != 2)
984 "expected zero or exactly 2 vector mask operands");
985 auto lhsType = llvm::cast<VectorType>(types[0]);
986 auto rhsType = llvm::cast<VectorType>(types[1]);
988 std::array<VectorType, 2> maskTypes = {
998 auto attrNames = getTraitAttrNames();
1000 traitAttrsSet.insert_range(attrNames);
1001 NamedAttrList allAttrs(getOperation()->getRawDictionaryAttrs());
1002 getOperation()->getName().walkInherentAttrs(
1004 [&](StringRef name,
Attribute &attr) { allAttrs.append(name, attr); });
1006 for (
auto attr : allAttrs) {
1007 if (attr.getName() == getIteratorTypesAttrName()) {
1008 auto iteratorTypes =
1009 llvm::cast<ArrayAttr>(attr.getValue())
1010 .getAsValueRange<IteratorTypeAttr, IteratorType>();
1016 llvm::map_to_vector(iteratorTypes, [&](IteratorType t) ->
Attribute {
1017 return StringAttr::get(
getContext(), stringifyIteratorType(t));
1020 attrs.emplace_back(getIteratorTypesAttrName(),
1021 ArrayAttr::get(
getContext(), iteratorTypeNames));
1022 }
else if (traitAttrsSet.count(attr.getName().strref()) > 0) {
1024 if (attr.getName() == getFastmathAttrName() &&
1025 llvm::cast<arith::FastMathFlagsAttr>(attr.getValue()).getValue() ==
1026 arith::FastMathFlags::none)
1028 attrs.push_back(attr);
1032 auto dictAttr = DictionaryAttr::get(
getContext(), attrs);
1033 p <<
" " << dictAttr <<
" " << getLhs() <<
", ";
1034 p << getRhs() <<
", " << getAcc();
1037 p <<
" : " << getLhs().getType() <<
", " << getRhs().getType() <<
" into "
1042 const std::vector<std::pair<int64_t, int64_t>> &map) {
1043 for (
auto &dimPair : map) {
1044 if (dimPair.first < 0 || dimPair.first >= lhsType.getRank() ||
1045 dimPair.second < 0 || dimPair.second >= rhsType.getRank() ||
1046 lhsType.getDimSize(dimPair.first) != rhsType.getDimSize(dimPair.second))
1053 ContractionOp op, VectorType lhsType, VectorType rhsType,
Type accType,
1055 const std::vector<std::pair<int64_t, int64_t>> &contractingDimMap,
1056 const std::vector<std::pair<int64_t, int64_t>> &batchDimMap) {
1059 for (
auto &dimPair : contractingDimMap) {
1060 lhsContractingDimSet.insert(dimPair.first);
1061 rhsContractingDimSet.insert(dimPair.second);
1064 llvm::make_second_range(batchDimMap));
1068 for (
int64_t i = 0, e = lhsType.getRank(); i < e; ++i) {
1069 if (lhsContractingDimSet.count(i) > 0)
1071 expectedResultDims.push_back(lhsType.getDimSize(i));
1075 for (
int64_t i = 0, e = rhsType.getRank(); i < e; ++i) {
1076 if (rhsContractingDimSet.count(i) > 0 || rhsBatchDimSet.count(i) > 0)
1078 expectedResultDims.push_back(rhsType.getDimSize(i));
1082 if (expectedResultDims.empty()) {
1084 if (llvm::isa<VectorType>(resType) || llvm::isa<VectorType>(accType))
1085 return op.emitOpError(
"invalid accumulator/result vector shape");
1088 auto resVectorType = llvm::dyn_cast<VectorType>(resType);
1089 auto accVectorType = llvm::dyn_cast<VectorType>(accType);
1090 if (!resVectorType || !accVectorType)
1091 return op.emitOpError(
"invalid accumulator/result vector shape");
1097 AffineMap lhsMap = op.getIndexingMapsArray()[0];
1098 AffineMap rhsMap = op.getIndexingMapsArray()[1];
1100 return op.emitOpError(
1101 "expected all dimensions to be either a LHS or a RHS dimension");
1104 {std::make_pair(lhsType, lhsMap), std::make_pair(rhsType, rhsMap)}) {
1105 VectorType v = pair.first;
1106 auto map = pair.second;
1107 for (
unsigned idx = 0, e = v.getRank(); idx < e; ++idx) {
1108 unsigned pos = map.getDimPosition(idx);
1113 if (!llvm::all_of(extents, [](
AffineExpr e) {
return e; }))
1114 return op.emitOpError(
"expected all dimensions to get an extent as "
1115 "either a LHS or a RHS dimension");
1117 AffineMap resMap = op.getIndexingMapsArray()[2];
1122 assert(llvm::all_of(expectedMap.
getResults(),
1123 llvm::IsaPred<AffineConstantExpr>) &&
1124 "expected constant extent along all dimensions.");
1126 auto expectedShape =
1128 return cast<AffineConstantExpr>(e).getValue();
1131 VectorType::get(expectedShape, resVectorType.getElementType(),
1132 resVectorType.getScalableDims());
1133 if (resVectorType != expected || accVectorType != expected)
1134 return op.emitOpError(
1135 "invalid accumulator/result vector shape, expected: ")
1143static std::optional<int64_t>
1145 auto maps =
contract->getIndexingMapsArray();
1149 for (
auto [dim, attr] : llvm::enumerate(
contract->getIteratorTypes())) {
1150 auto iteratorType = cast<vector::IteratorTypeAttr>(attr).getValue();
1151 if (iteratorType == vector::IteratorType::reduction &&
1156 return std::nullopt;
1159LogicalResult ContractionOp::verify() {
1160 VectorType lhsType = getLhsType();
1161 VectorType rhsType = getRhsType();
1162 Type accType = getAccType();
1163 Type resType = getResultType();
1165 if (llvm::isa<IntegerType>(lhsType.getElementType())) {
1166 if (!lhsType.getElementType().isSignlessInteger())
1167 return emitOpError(
"only supports signless integer types");
1171 if (getIndexingMapsArray().size() != 3)
1172 return emitOpError(
"expected an indexing map for each vector operand");
1177 unsigned numIterators = getIteratorTypes().getValue().size();
1178 for (
const auto &it : llvm::enumerate(getIndexingMapsArray())) {
1179 auto index = it.index();
1180 auto map = it.value();
1181 if (map.getNumSymbols() != 0)
1182 return emitOpError(
"expected indexing map ")
1183 <<
index <<
" to have no symbols";
1184 auto vectorType = llvm::dyn_cast<VectorType>(getOperand(
index).
getType());
1185 unsigned rank = vectorType ? vectorType.getShape().size() : 0;
1188 if (map.getNumDims() != numIterators)
1189 return emitOpError(
"expected indexing map ")
1190 <<
index <<
" to have " << numIterators <<
" number of inputs";
1191 if (map.getNumResults() != rank)
1192 return emitOpError(
"expected indexing map ")
1193 <<
index <<
" to have " << rank <<
" number of outputs";
1194 if (!map.isProjectedPermutation())
1195 return emitOpError(
"expected indexing map ")
1196 <<
index <<
" to be a projected permutation of its inputs";
1201 if (dimIdx.has_value())
1202 return emitOpError(
"reduction dim at index ")
1203 << dimIdx.value() <<
" must appear in both LHS and RHS";
1205 auto contractingDimMap = getContractingDimMap();
1206 auto batchDimMap = getBatchDimMap();
1209 if (contractingDimMap.empty())
1210 return emitOpError(
"expected at least one contracting dimension pair");
1213 if (!
verifyDimMap(lhsType, rhsType, contractingDimMap))
1214 return emitOpError(
"invalid contracting dimension map");
1218 return emitOpError(
"invalid batch dimension map");
1222 contractingDimMap, batchDimMap)))
1225 if (!getKindAttr()) {
1226 return emitOpError(
"expected 'kind' attribute of type CombiningKind (e.g. "
1227 "'vector.kind<add>')");
1231 auto vectorType = llvm::dyn_cast<VectorType>(resType);
1232 auto elementType = vectorType ? vectorType.getElementType() : resType;
1234 return emitOpError(
"unsupported contraction type");
1237 return cast<IndexingMapOpInterface>(this->getOperation()).verifyImpl();
1244Type ContractionOp::getExpectedMaskType() {
1245 auto indexingMaps = this->getIndexingMapsArray();
1248 VectorType lhsType = this->getLhsType();
1249 VectorType rhsType = this->getRhsType();
1251 unsigned numVecDims = lhsIdxMap.
getNumDims();
1257 for (
auto [dimIdx, dimSize] : llvm::enumerate(lhsType.getShape())) {
1260 lhsType.getScalableDims()[dimIdx];
1262 for (
auto [dimIdx, dimSize] : llvm::enumerate(rhsType.getShape())) {
1265 rhsType.getScalableDims()[dimIdx];
1268 assert(ShapedType::isStaticShape(maskShape) &&
1269 "Mask shape couldn't be computed");
1271 return VectorType::get(maskShape,
1272 IntegerType::get(lhsType.getContext(), 1),
1273 maskShapeScalableDims);
1278 getIteratorTypesAttrName(), getKindAttrName(),
1279 getFastmathAttrName()};
1294static std::vector<std::pair<int64_t, int64_t>>
1298 std::vector<std::pair<int64_t, int64_t>> dimMap;
1299 for (
const auto &it : llvm::enumerate(iteratorTypes)) {
1300 auto iteratorType = llvm::cast<IteratorTypeAttr>(it.value()).getValue();
1301 if (iteratorType != targetIteratorType)
1307 if (lhsDim >= 0 && rhsDim >= 0)
1308 dimMap.emplace_back(lhsDim, rhsDim);
1313std::vector<std::pair<int64_t, int64_t>> ContractionOp::getContractingDimMap() {
1317std::vector<std::pair<int64_t, int64_t>> ContractionOp::getBatchDimMap() {
1321void ContractionOp::getIterationBounds(
1323 auto lhsShape = getLhsType().getShape();
1324 auto resVectorType = llvm::dyn_cast<VectorType>(getResultType());
1326 for (
const auto &it : llvm::enumerate(getIteratorTypes())) {
1329 auto iteratorType = llvm::cast<IteratorTypeAttr>(it.value()).getValue();
1330 if (iteratorType == IteratorType::reduction) {
1333 assert(lhsDimIndex >= 0);
1334 iterationBounds.push_back(lhsShape[lhsDimIndex]);
1339 assert(resDimIndex >= 0);
1340 assert(resVectorType !=
nullptr);
1341 iterationBounds.push_back(resVectorType.getShape()[resDimIndex]);
1345void ContractionOp::getIterationIndexMap(
1347 unsigned numMaps = getIndexingMapsArray().size();
1348 iterationIndexMap.resize(numMaps);
1349 for (
const auto &it : llvm::enumerate(getIndexingMapsArray())) {
1350 auto index = it.index();
1351 auto map = it.value();
1352 for (
unsigned i = 0, e = map.getNumResults(); i < e; ++i) {
1353 auto dim = cast<AffineDimExpr>(map.getResult(i));
1354 iterationIndexMap[
index][dim.getPosition()] = i;
1359std::optional<SmallVector<int64_t, 4>>
1360ContractionOp::getIterationShapeToUnroll() {
1362 getIterationBounds(
shape);
1384template <
typename AddOpType>
1390 auto canonicalize = [&](
Value maybeContraction,
1391 Value otherOperand) -> vector::ContractionOp {
1392 vector::ContractionOp contractionOp =
1393 dyn_cast_or_null<vector::ContractionOp>(
1396 return vector::ContractionOp();
1397 if (
auto maybeZero = dyn_cast_or_null<arith::ConstantOp>(
1398 contractionOp.getAcc().getDefiningOp())) {
1399 if (maybeZero.getValue() ==
1400 rewriter.
getZeroAttr(contractionOp.getAcc().getType())) {
1402 bvm.
map(contractionOp.getAcc(), otherOperand);
1403 auto newContraction =
1404 cast<vector::ContractionOp>(rewriter.
clone(*contractionOp, bvm));
1405 rewriter.
replaceOp(addOp, newContraction.getResult());
1406 return newContraction;
1409 return vector::ContractionOp();
1412 Value a = addOp->getOperand(0),
b = addOp->getOperand(1);
1413 vector::ContractionOp
contract = canonicalize(a,
b);
1438 setResultRanges(getResult(), argRanges.front());
1443 auto vectorTy = cast<VectorType>(source.
getType());
1468 build(builder,
result, source, dynamicPos,
1473ExtractOp::inferReturnTypes(
MLIRContext *, std::optional<Location>,
1474 ExtractOp::Adaptor adaptor,
1476 auto vectorType = llvm::cast<VectorType>(adaptor.getSource().getType());
1477 if (
static_cast<int64_t>(adaptor.getStaticPosition().size()) ==
1478 vectorType.getRank()) {
1479 inferredReturnTypes.push_back(vectorType.getElementType());
1481 auto n = std::min<size_t>(adaptor.getStaticPosition().size(),
1482 vectorType.getRank());
1483 inferredReturnTypes.push_back(VectorType::get(
1484 vectorType.getShape().drop_front(n), vectorType.getElementType(),
1485 vectorType.getScalableDims().drop_front(n)));
1490LogicalResult vector::ExtractOp::verify() {
1491 if (
auto resTy = dyn_cast<VectorType>(getResult().
getType()))
1492 if (resTy.getRank() == 0)
1494 "expected a scalar instead of a 0-d vector as the result type");
1497 auto dynamicMarkersCount =
1498 llvm::count_if(getStaticPosition(), ShapedType::isDynamic);
1499 if (
static_cast<size_t>(dynamicMarkersCount) != getDynamicPosition().size())
1501 "mismatch between dynamic and static positions (kDynamic marker but no "
1502 "corresponding dynamic position) -- this can only happen due to an "
1503 "incorrect fold/rewrite");
1504 auto position = getMixedPosition();
1505 if (position.size() >
static_cast<unsigned>(getSourceVectorType().getRank()))
1507 "expected position attribute of rank no greater than vector rank");
1508 for (
auto [idx, pos] : llvm::enumerate(position)) {
1509 if (
auto attr = dyn_cast<Attribute>(pos)) {
1510 int64_t constIdx = cast<IntegerAttr>(attr).getInt();
1512 constIdx, kPoisonIndex, getSourceVectorType().getDimSize(idx))) {
1513 return emitOpError(
"expected position attribute #")
1515 <<
" to be a non-negative integer smaller than the "
1516 "corresponding vector dimension or poison (-1)";
1523template <
typename IntType>
1525 return llvm::map_to_vector<4>(
1526 arrayAttr.getAsRange<IntegerAttr>(),
1527 [](IntegerAttr attr) { return static_cast<IntType>(attr.getInt()); });
1544 if (!extractOp.getSource().getDefiningOp<ExtractOp>())
1548 ExtractOp currentOp = extractOp;
1549 while (ExtractOp nextOp = currentOp.getSource().getDefiningOp<ExtractOp>()) {
1552 globalPosition.insert(globalPosition.begin(), pos.begin(), pos.end());
1555 Value source = currentOp.getSource();
1556 extractOp->setOperands(
1557 llvm::to_vector(llvm::concat<Value>(
ValueRange(source), dynPos)));
1558 extractOp.setStaticPosition(staticPos);
1570class ExtractFromInsertTransposeChainState {
1572 ExtractFromInsertTransposeChainState(ExtractOp e);
1581 template <
typename ContainerA,
typename ContainerB>
1582 bool isContainedWithin(
const ContainerA &a,
const ContainerB &
b) {
1583 return a.size() <=
b.size() &&
1584 std::equal(a.begin(), a.begin() + a.size(),
b.begin());
1591 template <
typename ContainerA,
typename ContainerB>
1592 bool intersectsWhereNonNegative(
const ContainerA &a,
const ContainerB &
b) {
1593 for (
auto [elemA, elemB] : llvm::zip(a,
b)) {
1594 if (elemA < 0 || elemB < 0)
1605 return (sentinels == ArrayRef(extractPosition).drop_front(extractedRank));
1609 void updateStateForNextIteration(Value v) {
1616 LogicalResult handleTransposeOp();
1619 LogicalResult handleInsertOpWithMatchingPos(Value &res);
1634 LogicalResult handleInsertOpWithPrefixPos(Value &res);
1639 Value tryToFoldExtractOpInPlace(Value source);
1641 ExtractOp extractOp;
1643 int64_t extractedRank;
1645 InsertOp nextInsertOp;
1646 TransposeOp nextTransposeOp;
1656 SmallVector<int64_t> sentinels;
1657 SmallVector<int64_t> extractPosition;
1661ExtractFromInsertTransposeChainState::ExtractFromInsertTransposeChainState(
1663 : extractOp(e), vectorRank(extractOp.getSourceVectorType().getRank()),
1664 extractedRank(extractOp.getNumIndices()) {
1665 assert(vectorRank >= extractedRank &&
"Extracted position overflow");
1666 sentinels.reserve(vectorRank - extractedRank);
1667 for (
int64_t i = 0, e = vectorRank - extractedRank; i < e; ++i)
1668 sentinels.push_back(-(i + 1));
1670 extractOp.getStaticPosition().end());
1676LogicalResult ExtractFromInsertTransposeChainState::handleTransposeOp() {
1678 if (extractOp.hasDynamicPosition())
1681 if (!nextTransposeOp)
1684 if (nextTransposeOp.getResultVectorType().getRank() == 0)
1688 nextTransposeOp.getPermutation(), extractOp.getContext()));
1695ExtractFromInsertTransposeChainState::handleInsertOpWithMatchingPos(
1698 if (extractOp.hasDynamicPosition() || nextInsertOp.hasDynamicPosition())
1701 ArrayRef<int64_t> insertedPos = nextInsertOp.getStaticPosition();
1702 if (insertedPos != llvm::ArrayRef(
extractPosition).take_front(extractedRank))
1705 res = nextInsertOp.getValueToStore();
1714ExtractFromInsertTransposeChainState::handleInsertOpWithPrefixPos(Value &res) {
1716 if (extractOp.hasDynamicPosition() || nextInsertOp.hasDynamicPosition())
1719 ArrayRef<int64_t> insertedPos = nextInsertOp.getStaticPosition();
1729 res = nextInsertOp.getValueToStore();
1737Value ExtractFromInsertTransposeChainState::tryToFoldExtractOpInPlace(
1740 if (extractOp.hasDynamicPosition())
1744 bool nothingToFold = (source == extractOp.getSource());
1745 if (nothingToFold || !canFold())
1749 OpBuilder
b(extractOp.getContext());
1750 extractOp.setStaticPosition(
1752 extractOp.getSourceMutable().assign(source);
1753 return extractOp.getResult();
1757Value ExtractFromInsertTransposeChainState::fold() {
1759 if (extractOp.hasDynamicPosition())
1762 Value valueToExtractFrom = extractOp.getSource();
1763 updateStateForNextIteration(valueToExtractFrom);
1764 while (nextInsertOp || nextTransposeOp) {
1767 if (succeeded(handleTransposeOp())) {
1768 valueToExtractFrom = nextTransposeOp.getVector();
1769 updateStateForNextIteration(valueToExtractFrom);
1775 if (succeeded(handleInsertOpWithMatchingPos(
result)))
1780 if (succeeded(handleInsertOpWithPrefixPos(
result)))
1781 return tryToFoldExtractOpInPlace(
result);
1785 ArrayRef<int64_t> insertedPos = nextInsertOp.getStaticPosition();
1791 valueToExtractFrom = nextInsertOp.getDest();
1792 updateStateForNextIteration(valueToExtractFrom);
1795 return tryToFoldExtractOpInPlace(valueToExtractFrom);
1800 auto hasZeroDimVectorType = [](
Type type) ->
bool {
1801 auto vecType = dyn_cast<VectorType>(type);
1802 return vecType && vecType.getRank() == 0;
1812 if (isa<BroadcastOp>(op))
1815 auto shapeCast = dyn_cast<ShapeCastOp>(op);
1823 VectorType srcType = shapeCast.getSourceVectorType();
1825 uint64_t srcRank = srcType.getRank();
1827 return dstShape.size() >= srcRank && dstShape.take_back(srcRank) == srcShape;
1853 Operation *defOp = extractOp.getSource().getDefiningOp();
1860 if (extractOp.getType() == input.
getType())
1866 auto inputType = llvm::dyn_cast<VectorType>(input.
getType());
1867 auto extractType = llvm::dyn_cast<VectorType>(extractOp.getType());
1868 unsigned inputRank = inputType ? inputType.getRank() : 0;
1869 unsigned broadcastRank = extractOp.getSourceVectorType().getRank();
1870 unsigned extractRank = extractType ? extractType.getRank() : 0;
1873 if (extractRank > inputRank)
1877 assert(inputType &&
"input must be a vector type because of previous checks");
1886 extractType.getShape() != inputShape.take_back(extractRank))
1891 unsigned deltaOverall = inputRank - extractRank;
1892 unsigned deltaBroadcast = broadcastRank - inputRank;
1896 for (
auto [i, size] : llvm::enumerate(inputShape.take_front(deltaOverall))) {
1897 newPositions[i] = size == 1 ? zero : oldPositions[i + deltaBroadcast];
1900 extractOp->setOperands(
1901 llvm::to_vector(llvm::concat<Value>(
ValueRange(input), dynPos)));
1902 extractOp.setStaticPosition(staticPos);
1903 return extractOp.getResult();
1919 if (extractOp.hasDynamicPosition())
1922 auto shuffleOp = extractOp.getSource().getDefiningOp<ShuffleOp>();
1927 if (shuffleOp.getResultVectorType().getRank() != 1)
1930 int64_t inputVecSize = shuffleOp.getV1().getType().getShape()[0];
1931 auto shuffleMask = shuffleOp.getMask();
1932 int64_t extractIdx = extractOp.getStaticPosition()[0];
1933 int64_t shuffleIdx = shuffleMask[extractIdx];
1936 if (shuffleIdx < inputVecSize) {
1937 extractOp.setOperand(0, shuffleOp.getV1());
1938 extractOp.setStaticPosition({shuffleIdx});
1940 extractOp.setOperand(0, shuffleOp.getV2());
1941 extractOp.setStaticPosition({shuffleIdx - inputVecSize});
1944 return extractOp.getResult();
1950 if (extractOp.hasDynamicPosition())
1953 auto shapeCastOp = extractOp.getSource().getDefiningOp<vector::ShapeCastOp>();
1958 auto getDimReverse = [](VectorType type,
int64_t n) {
1959 return type.getShape().take_back(n + 1).front();
1962 llvm::isa<VectorType>(extractOp.getType())
1963 ? llvm::cast<VectorType>(extractOp.getType()).getRank()
1965 if (destinationRank > shapeCastOp.getSourceVectorType().getRank())
1967 if (destinationRank > 0) {
1968 auto destinationType =
1969 llvm::cast<VectorType>(extractOp.getResult().getType());
1970 for (
int64_t i = 0; i < destinationRank; i++) {
1974 if (getDimReverse(shapeCastOp.getSourceVectorType(), i) !=
1975 getDimReverse(destinationType, i))
1982 std::reverse(extractedPos.begin(), extractedPos.end());
1985 for (
int64_t i = 0, e = extractedPos.size(); i < e; i++) {
1986 strides.push_back(stride);
1988 getDimReverse(extractOp.getSourceVectorType(), i + destinationRank);
1996 shapeCastOp.getSourceVectorType().getRank() - destinationRank;
1998 for (
int64_t i = 0; i < numDimension; i++) {
1999 newStrides.push_back(stride);
2001 getDimReverse(shapeCastOp.getSourceVectorType(), i + destinationRank);
2003 std::reverse(newStrides.begin(), newStrides.end());
2007 extractOp.setStaticPosition(newPosition);
2008 extractOp.setOperand(0, shapeCastOp.getSource());
2009 return extractOp.getResult();
2015 if (extractOp.hasDynamicPosition())
2018 auto extractStridedSliceOp =
2019 extractOp.getSource().getDefiningOp<vector::ExtractStridedSliceOp>();
2020 if (!extractStridedSliceOp)
2029 if (extractStridedSliceOp.hasNonUnitStrides())
2035 while (!sliceOffsets.empty()) {
2036 size_t lastOffset = sliceOffsets.size() - 1;
2037 if (sliceOffsets.back() != 0 ||
2038 extractStridedSliceOp.getType().getDimSize(lastOffset) !=
2039 extractStridedSliceOp.getSourceVectorType().getDimSize(lastOffset))
2041 sliceOffsets.pop_back();
2043 unsigned destinationRank = 0;
2044 if (
auto vecType = llvm::dyn_cast<VectorType>(extractOp.getType()))
2045 destinationRank = vecType.getRank();
2048 if (destinationRank > extractStridedSliceOp.getSourceVectorType().getRank() -
2049 sliceOffsets.size())
2053 assert(extractedPos.size() >= sliceOffsets.size());
2054 for (
size_t i = 0, e = sliceOffsets.size(); i < e; i++)
2055 extractedPos[i] = extractedPos[i] + sliceOffsets[i];
2056 extractOp.getSourceMutable().assign(extractStridedSliceOp.getSource());
2060 extractOp.setStaticPosition(extractedPos);
2061 return extractOp.getResult();
2067 if (extractOp.hasDynamicPosition())
2071 llvm::isa<VectorType>(extractOp.getType())
2072 ? llvm::cast<VectorType>(extractOp.getType()).getRank()
2074 auto insertOp = extractOp.getSource().getDefiningOp<InsertStridedSliceOp>();
2084 int64_t insertRankDiff = insertOp.getDestVectorType().getRank() -
2085 insertOp.getSourceVectorType().getRank();
2086 if (destinationRank > insertOp.getSourceVectorType().getRank())
2091 if (llvm::any_of(insertOp.getStrides(), [](
Attribute attr) {
2092 return llvm::cast<IntegerAttr>(attr).getInt() != 1;
2095 bool disjoint =
false;
2097 for (
unsigned dim = 0, e = extractOffsets.size(); dim < e; ++dim) {
2098 int64_t start = insertOffsets[dim];
2100 (dim < insertRankDiff)
2102 : insertOp.getSourceVectorType().getDimSize(dim - insertRankDiff);
2104 int64_t offset = extractOffsets[dim];
2106 if (start <= offset && offset < end) {
2107 if (dim >= insertRankDiff)
2108 offsetDiffs.push_back(offset - start);
2119 insertOp.getSourceVectorType().getRank() - destinationRank;
2120 for (
int64_t i = 0; i < destinationRank; i++) {
2121 if (insertOp.getSourceVectorType().getDimSize(i + srcRankDiff) !=
2122 insertOp.getDestVectorType().getDimSize(i + srcRankDiff +
2126 extractOp.getSourceMutable().assign(insertOp.getValueToStore());
2129 extractOp.setStaticPosition(offsetDiffs);
2130 return extractOp.getResult();
2134 insertOp = insertOp.getDest().getDefiningOp<InsertStridedSliceOp>();
2147 if (extractOp.hasDynamicPosition())
2151 auto fromElementsOp = extractOp.getSource().
getDefiningOp<FromElementsOp>();
2152 if (!fromElementsOp)
2156 auto vecType = llvm::cast<VectorType>(fromElementsOp.getType());
2157 if (vecType.isScalable())
2161 int64_t rank = vecType.getRank();
2163 if (extractOp.getType() != vecType.getElementType())
2166 "unexpected number of indices");
2171 for (
int i = rank - 1; i >= 0; --i) {
2172 flatIndex +=
indices[i] * stride;
2173 stride *= vecType.getDimSize(i);
2175 return fromElementsOp.getElements()[flatIndex];
2180template <
typename OpType,
typename AdaptorType>
2183 std::vector<int64_t> staticPosition = op.getStaticPosition().vec();
2184 OperandRange dynamicPosition = op.getDynamicPosition();
2187 if constexpr (std::is_same_v<OpType, ExtractOp>)
2188 vectorShape = op.getSourceVectorType().getShape();
2193 if (!dynamicPosition.size())
2200 bool opChange =
false;
2201 for (
unsigned i = 0, e = staticPosition.size(); i < e; ++i) {
2202 if (ShapedType::isStatic(staticPosition[i]))
2206 if (
auto attr = mlir::dyn_cast_if_present<IntegerAttr>(positionAttr)) {
2207 int64_t value = attr.getInt();
2211 staticPosition[i] = attr.getInt();
2216 operands.push_back(position);
2220 op.setStaticPosition(staticPosition);
2221 op.getOperation()->setOperands(operands);
2223 return op.getResult();
2233 if (!is_contained(staticPos, poisonVal))
2236 return ub::PoisonAttr::get(context);
2250 auto denseAttr = dyn_cast_if_present<DenseElementsAttr>(srcAttr);
2255 if (denseAttr.isSplat()) {
2257 if (
auto vecDstType = dyn_cast<VectorType>(extractOp.getType()))
2262 auto vecTy = cast<VectorType>(extractOp.getSourceVectorType());
2263 if (vecTy.isScalable())
2266 if (extractOp.hasDynamicPosition()) {
2281 copy(extractOp.getStaticPosition(), completePositions.begin());
2284 auto denseValuesBegin = denseAttr.value_begin<TypedAttr>() + startPos;
2287 if (
auto resVecTy = dyn_cast<VectorType>(extractOp.getType())) {
2289 denseValuesBegin, denseValuesBegin + resVecTy.getNumElements());
2292 newAttr = *denseValuesBegin;
2298OpFoldResult ExtractOp::fold(FoldAdaptor adaptor) {
2302 if (getNumIndices() == 0 && getSource().
getType() == getResult().
getType())
2309 SmallVector<Value> operands = {getSource()};
2313 getContext(), adaptor.getStaticPosition(), kPoisonIndex))
2319 if (
auto res = ExtractFromInsertTransposeChainState(*this).fold())
2334 return inplaceFolded;
2340class ExtractOpFromBroadcast final :
public OpRewritePattern<ExtractOp> {
2344 LogicalResult matchAndRewrite(ExtractOp extractOp,
2345 PatternRewriter &rewriter)
const override {
2348 VectorType outType = dyn_cast<VectorType>(extractOp.getType());
2354 BroadcastableToResult::Success)
2363class ExtractOpFromCreateMask final :
public OpRewritePattern<ExtractOp> {
2367 LogicalResult matchAndRewrite(ExtractOp extractOp,
2368 PatternRewriter &rewriter)
const override {
2370 extractOp.getSource().getDefiningOp<vector::CreateMaskOp>();
2374 VectorType extractedMaskType =
2375 llvm::dyn_cast<VectorType>(extractOp.getResult().getType());
2377 if (!extractedMaskType)
2380 auto maskOperands = createMaskOp.getOperands();
2381 ArrayRef<int64_t> extractOpPos = extractOp.getStaticPosition();
2382 VectorType maskType = createMaskOp.getVectorType();
2384 bool containsUnknownDims =
false;
2387 for (
size_t dimIdx = 0; !allFalse && dimIdx < extractOpPos.size();
2389 int64_t pos = extractOpPos[dimIdx];
2390 Value operand = maskOperands[dimIdx];
2391 auto constantOp = operand.
getDefiningOp<arith::ConstantOp>();
2394 containsUnknownDims =
true;
2398 int64_t createMaskBound =
2399 llvm::cast<IntegerAttr>(constantOp.getValue()).getInt();
2401 if (pos != ShapedType::kDynamic) {
2404 allFalse |= pos >= createMaskBound;
2405 }
else if (createMaskBound < maskType.getDimSize(dimIdx)) {
2409 containsUnknownDims =
true;
2416 }
else if (!containsUnknownDims) {
2418 extractOp, extractedMaskType,
2419 maskOperands.drop_front(extractOpPos.size()));
2428class ExtractOpFromConstantMask final :
public OpRewritePattern<ExtractOp> {
2432 LogicalResult matchAndRewrite(ExtractOp extractOp,
2433 PatternRewriter &rewriter)
const override {
2434 auto constantMaskOp =
2435 extractOp.getSource().getDefiningOp<vector::ConstantMaskOp>();
2436 if (!constantMaskOp)
2439 Type resultType = extractOp.getResult().getType();
2440 auto extractedMaskType = dyn_cast<VectorType>(resultType);
2442 ArrayRef<int64_t> extractOpPos = extractOp.getStaticPosition();
2443 ArrayRef<int64_t> maskDimSizes = constantMaskOp.getMaskDimSizes();
2445 VectorType maskType = constantMaskOp.getVectorType();
2448 for (
size_t dimIdx = 0; dimIdx < extractOpPos.size(); dimIdx++) {
2449 int64_t pos = extractOpPos[dimIdx];
2450 if (pos == ShapedType::kDynamic) {
2453 if (maskDimSizes[dimIdx] == maskType.getDimSize(dimIdx))
2462 if (pos >= maskDimSizes[dimIdx]) {
2463 if (extractedMaskType) {
2475 if (extractedMaskType) {
2479 extractOp, extractedMaskType,
2480 maskDimSizes.drop_front(extractOpPos.size()));
2493LogicalResult foldExtractFromShapeCastToShapeCast(ExtractOp extractOp,
2494 PatternRewriter &rewriter) {
2495 auto castOp = extractOp.getSource().getDefiningOp<ShapeCastOp>();
2499 VectorType sourceType = castOp.getSourceVectorType();
2500 auto targetType = dyn_cast<VectorType>(extractOp.getResult().getType());
2504 if (sourceType.getNumElements() != targetType.getNumElements())
2508 castOp.getSource());
2518LogicalResult foldExtractFromFromElements(ExtractOp extractOp,
2519 PatternRewriter &rewriter) {
2521 if (extractOp.hasDynamicPosition())
2525 auto resultType = dyn_cast<VectorType>(extractOp.getType());
2530 auto fromElementsOp = extractOp.getSource().getDefiningOp<FromElementsOp>();
2531 if (!fromElementsOp)
2533 VectorType inputType = fromElementsOp.getType();
2536 if (resultType.isScalable() || inputType.isScalable())
2541 SmallVector<int64_t> firstElementPos =
2542 llvm::to_vector(extractOp.getStaticPosition());
2543 firstElementPos.append(resultType.getRank(), 0);
2546 for (int64_t i = inputType.getRank() - 1; i >= 0; --i) {
2547 flatIndex += firstElementPos[i] * stride;
2548 stride *= inputType.getDimSize(i);
2553 extractOp, resultType,
2554 fromElementsOp.getElements().
slice(flatIndex,
2555 resultType.getNumElements()));
2567struct ExtractToShapeCast final : OpRewritePattern<vector::ExtractOp> {
2569 LogicalResult matchAndRewrite(vector::ExtractOp extractOp,
2570 PatternRewriter &rewriter)
const override {
2571 VectorType sourceType = extractOp.getSourceVectorType();
2572 VectorType outType = dyn_cast<VectorType>(extractOp.getType());
2576 if (sourceType.getNumElements() != outType.getNumElements())
2578 extractOp,
"extract to vector with fewer elements");
2582 if (llvm::any_of(extractOp.getMixedPosition(),
2583 [](OpFoldResult v) { return !isConstantIntValue(v, 0); }))
2585 "leaving for extract poison folder");
2588 extractOp.getSource());
2609struct FoldExtractFromInsertUnitDim final
2610 : OpRewritePattern<vector::ExtractOp> {
2613 LogicalResult matchAndRewrite(vector::ExtractOp extractOp,
2614 PatternRewriter &rewriter)
const override {
2615 if (extractOp.hasDynamicPosition())
2618 auto insertOp = extractOp.getSource().getDefiningOp<vector::InsertOp>();
2619 if (!insertOp || insertOp.hasDynamicPosition())
2622 ArrayRef<int64_t> extractPos = extractOp.getStaticPosition();
2623 ArrayRef<int64_t> insertPos = insertOp.getStaticPosition();
2626 if (extractPos.size() >= insertPos.size() ||
2627 extractPos != insertPos.take_front(extractPos.size()))
2633 auto srcVecType = extractOp.getSourceVectorType();
2634 for (int64_t i = extractPos.size(), e = srcVecType.getRank(); i < e; ++i)
2635 if (srcVecType.getDimSize(i) != 1)
2638 Value
inserted = insertOp.getValueToStore();
2639 Type extractedType = extractOp.getResult().getType();
2640 if (isa<VectorType>(
inserted.getType())) {
2647 extractOp, extractOp.getResult().
getType(),
2648 insertOp.getValueToStore());
2656void ExtractOp::getCanonicalizationPatterns(RewritePatternSet &results,
2657 MLIRContext *context) {
2658 results.
add<ExtractOpFromBroadcast, ExtractOpFromCreateMask,
2659 ExtractOpFromConstantMask, ExtractToShapeCast,
2660 FoldExtractFromInsertUnitDim>(context);
2661 results.
add(foldExtractFromShapeCastToShapeCast);
2662 results.
add(foldExtractFromFromElements);
2667 for (
auto attr : arrayAttr)
2668 results.push_back(llvm::cast<IntegerAttr>(attr).getInt());
2675std::optional<SmallVector<int64_t, 4>> FMAOp::getIterationShapeToUnroll() {
2686 if (operands.empty())
2689 return llvm::all_of(operands, [&](
Value operand) {
2691 return currentDef == defOp;
2709 auto fromElementsOp =
2710 toElementsOp.getSource().getDefiningOp<FromElementsOp>();
2711 if (!fromElementsOp)
2714 llvm::append_range(results, fromElementsOp.getElements());
2731 auto bcastOp = toElementsOp.getSource().getDefiningOp<BroadcastOp>();
2735 if (isa<VectorType>(bcastOp.getSource().getType()))
2738 auto resultVecType = cast<VectorType>(toElementsOp.getSource().getType());
2740 Value scalar = bcastOp.getSource();
2741 results.assign(resultVecType.getNumElements(), scalar);
2745LogicalResult ToElementsOp::fold(FoldAdaptor adaptor,
2746 SmallVectorImpl<OpFoldResult> &results) {
2751 if (
auto shapeCast = getSource().getDefiningOp<ShapeCastOp>()) {
2752 setOperand(shapeCast.getSource());
2760ToElementsOp::inferReturnTypes(MLIRContext *ctx, std::optional<Location> loc,
2761 ToElementsOp::Adaptor adaptor,
2762 SmallVectorImpl<Type> &inferredReturnTypes) {
2763 auto vecType = cast<VectorType>(adaptor.getSource().getType());
2764 Type elType = vecType.getElementType();
2765 inferredReturnTypes.append(vecType.getNumElements(), elType);
2787 auto bcastOp = toElementsOp.getSource().getDefiningOp<BroadcastOp>();
2792 auto srcType = dyn_cast<VectorType>(bcastOp.getSource().getType());
2796 auto dstType = cast<VectorType>(toElementsOp.getSource().getType());
2801 int64_t dstRank = dstShape.size();
2802 int64_t srcRank = srcShape.size();
2805 auto srcElems = vector::ToElementsOp::create(
2806 rewriter, toElementsOp.getLoc(), bcastOp.getSource());
2808 int64_t dstCount = llvm::product_of(dstShape);
2811 replacements.reserve(dstCount);
2836 for (
int64_t lin = 0; lin < dstCount; ++lin) {
2839 for (
int64_t k = 0; k < srcRank; ++k)
2840 srcIdx[k] = (srcShape[k] == 1) ? 0 : dstIdx[dstRank - srcRank + k];
2843 replacements.push_back(srcElems.getResult(srcLin));
2846 rewriter.
replaceOp(toElementsOp, replacements);
2851void ToElementsOp::getCanonicalizationPatterns(RewritePatternSet &results,
2852 MLIRContext *context) {
2853 results.
add<ToElementsOfBroadcast>(context);
2873 OperandRange fromElemsOperands = fromElementsOp.getElements();
2874 if (fromElemsOperands.empty())
2877 auto toElementsOp = fromElemsOperands[0].getDefiningOp<ToElementsOp>();
2885 Value toElementsInput = toElementsOp.getSource();
2886 if (fromElementsOp.getType() == toElementsInput.
getType() &&
2887 llvm::equal(fromElemsOperands, toElementsOp.getResults())) {
2888 return toElementsInput;
2908 if (llvm::any_of(elements, [](
Attribute attr) {
2914 auto destVecType = fromElementsOp.getDest().getType();
2915 auto destEltType = destVecType.getElementType();
2916 if (!destEltType.isIntOrIndexOrFloat() && !isa<ComplexType>(destEltType))
2921 auto convertedElements = llvm::map_to_vector(elements, [&](
Attribute attr) {
2928OpFoldResult FromElementsOp::fold(FoldAdaptor adaptor) {
2945 if (!llvm::all_equal(fromElementsOp.getElements()))
2948 fromElementsOp, fromElementsOp.getType(),
2949 fromElementsOp.getElements().front());
2977 LogicalResult matchAndRewrite(FromElementsOp fromElements,
2981 if (fromElements.getType().getNumElements() == 1)
2992 for (
auto [insertIndex, element] :
2993 llvm::enumerate(fromElements.getElements())) {
2996 auto extractOp = element.getDefiningOp<vector::ExtractOp>();
2999 "element not from vector.extract");
3004 if (insertIndex == 0) {
3005 source = extractOp.getSource();
3006 }
else if (extractOp.getSource() != source) {
3008 "element from different vector");
3012 int64_t rank = position.size();
3013 assert(rank == source.getType().getRank() &&
3014 "scalar extract must have full rank position");
3025 if (insertIndex == 0) {
3026 const int64_t numElms = fromElements.getType().getNumElements();
3029 while (
index > 0 && position[
index - 1] == 0 &&
3030 numSuffixElms < numElms) {
3031 numSuffixElms *= source.getType().getDimSize(
index - 1);
3034 if (numSuffixElms != numElms) {
3036 fromElements,
"elements do not form a suffix of source");
3038 expectedPosition = llvm::to_vector(position);
3039 combinedPosition = position.drop_back(rank -
index);
3043 else if (expectedPosition != position) {
3045 fromElements,
"elements not in ascending order (static order)");
3047 increment(expectedPosition, source.getType().getShape());
3050 auto extracted = rewriter.
createOrFold<vector::ExtractOp>(
3051 fromElements.getLoc(), source, combinedPosition);
3054 fromElements, fromElements.getType(), extracted);
3062 for (
int dim : llvm::reverse(llvm::seq<int>(0,
indices.size()))) {
3081void BroadcastOp::inferResultRanges(ArrayRef<ConstantIntRanges> argRanges,
3083 setResultRanges(getResult(), argRanges.front());
3086std::optional<SmallVector<int64_t, 4>>
3087BroadcastOp::getIterationShapeToUnroll() {
3088 return llvm::to_vector<4>(getResultVectorType().
getShape());
3093static llvm::SetVector<int64_t>
3096 int64_t rankDiff = dstShape.size() - srcShape.size();
3099 for (
auto [s1, s2] :
3100 llvm::zip_equal(srcShape, dstShape.drop_front(rankDiff))) {
3102 assert(s1 == 1 &&
"expected \"dim-1\" broadcasting");
3110llvm::SetVector<int64_t> BroadcastOp::computeBroadcastedUnitDims() {
3112 auto srcVectorType = llvm::dyn_cast<VectorType>(getSourceType());
3115 return ::computeBroadcastedUnitDims(srcVectorType.getShape(),
3131Value BroadcastOp::createOrFoldBroadcastOp(
3132 OpBuilder &
b, Value value, ArrayRef<int64_t> dstShape,
3133 const llvm::SetVector<int64_t> &broadcastedDims) {
3134 assert(!dstShape.empty() &&
"unexpected empty dst shape");
3137 SmallVector<int64_t> checkShape;
3138 for (
int i = 0, e = dstShape.size(); i < e; ++i) {
3139 if (broadcastedDims.contains(i))
3141 checkShape.push_back(dstShape[i]);
3143 assert(broadcastedDims.size() == dstShape.size() - checkShape.size() &&
3144 "ill-formed broadcastedDims contains values not confined to "
3147 Location loc = value.
getLoc();
3149 VectorType srcVectorType = llvm::dyn_cast<VectorType>(value.
getType());
3150 VectorType dstVectorType = VectorType::get(dstShape, elementType);
3153 if (!srcVectorType) {
3154 assert(checkShape.empty() &&
3155 "ill-formed createOrFoldBroadcastOp arguments");
3156 return b.createOrFold<vector::BroadcastOp>(loc, dstVectorType, value);
3159 assert(srcVectorType.getShape().equals(checkShape) &&
3160 "ill-formed createOrFoldBroadcastOp arguments");
3170 SmallVector<int64_t> broadcastShape, permutation(dstShape.size(), -1);
3171 broadcastShape.reserve(dstShape.size());
3187 int64_t nextSrcShapeDim = broadcastedDims.size();
3188 for (int64_t i = 0, e = dstShape.size(); i < e; ++i) {
3189 if (broadcastedDims.contains(i)) {
3194 broadcastShape.push_back(dstShape[i]);
3195 permutation[i] = broadcastShape.size() - 1;
3201 permutation[i] = nextSrcShapeDim++;
3205 llvm::append_range(broadcastShape, srcVectorType.getShape());
3210 "unexpected \"dim-1\" broadcast");
3212 VectorType broadcastType = VectorType::get(broadcastShape, elementType);
3214 vector::BroadcastableToResult::Success &&
3215 "must be broadcastable");
3216 Value res =
b.createOrFold<vector::BroadcastOp>(loc, broadcastType, value);
3219 for (int64_t i = 0, e = permutation.size(); i < e; ++i)
3220 if (permutation[i] != i)
3221 return b.createOrFold<vector::TransposeOp>(loc, res, permutation);
3226LogicalResult BroadcastOp::verify() {
3227 std::pair<VectorDim, VectorDim> mismatchingDims;
3229 getSourceType(), getResultVectorType(), &mismatchingDims);
3230 if (res == BroadcastableToResult::Success)
3232 if (res == BroadcastableToResult::SourceRankHigher)
3233 return emitOpError(
"source rank higher than destination rank");
3234 if (res == BroadcastableToResult::DimensionMismatch) {
3235 return emitOpError(
"dimension mismatch (")
3236 << (mismatchingDims.first.isScalable ?
"[" :
"")
3237 << mismatchingDims.first.dim
3238 << (mismatchingDims.first.isScalable ?
"]" :
"") <<
" vs. "
3239 << (mismatchingDims.second.isScalable ?
"[" :
"")
3240 << mismatchingDims.second.dim
3241 << (mismatchingDims.second.isScalable ?
"]" :
"") <<
")";
3243 if (res == BroadcastableToResult::SourceTypeNotAVector)
3244 return emitOpError(
"source type is not a vector");
3245 llvm_unreachable(
"unexpected vector.broadcast op error");
3252 auto srcShapeCast = broadcastOp.getSource().getDefiningOp<ShapeCastOp>();
3256 VectorType srcType = srcShapeCast.getSourceVectorType();
3257 VectorType destType = broadcastOp.getResultVectorType();
3265 srcShapeCast.getResultVectorType().getShape();
3268 unsigned numTrailingDims = std::min(srcShape.size(), shapecastShape.size());
3269 if (!llvm::equal(srcShape.take_back(numTrailingDims),
3270 shapecastShape.take_back(numTrailingDims)))
3273 assert(all_of(srcShape.drop_back(numTrailingDims),
3274 [](
int64_t E) { return E == 1; }) &&
3275 all_of(shapecastShape.drop_back(numTrailingDims),
3276 [](
int64_t E) { return E == 1; }) &&
3277 "ill-formed shape_cast");
3279 broadcastOp.getSourceMutable().assign(srcShapeCast.getSource());
3283OpFoldResult BroadcastOp::fold(FoldAdaptor adaptor) {
3284 if (getSourceType() == getResultVectorType())
3289 if (!adaptor.getSource())
3291 auto vectorType = getResultVectorType();
3292 if (
auto attr = llvm::dyn_cast<IntegerAttr>(adaptor.getSource())) {
3293 if (vectorType.getElementType() != attr.getType())
3297 if (
auto attr = llvm::dyn_cast<FloatAttr>(adaptor.getSource())) {
3298 if (vectorType.getElementType() != attr.getType())
3302 if (
auto attr = llvm::dyn_cast<SplatElementsAttr>(adaptor.getSource()))
3312struct BroadcastFolder :
public OpRewritePattern<BroadcastOp> {
3315 LogicalResult matchAndRewrite(BroadcastOp broadcastOp,
3316 PatternRewriter &rewriter)
const override {
3317 auto srcBroadcast = broadcastOp.getSource().getDefiningOp<BroadcastOp>();
3321 broadcastOp.getResultVectorType(),
3322 srcBroadcast.getSource());
3335struct BroadcastToShapeCast final
3336 :
public OpRewritePattern<vector::BroadcastOp> {
3338 LogicalResult matchAndRewrite(vector::BroadcastOp
broadcast,
3339 PatternRewriter &rewriter)
const override {
3341 auto sourceType = dyn_cast<VectorType>(
broadcast.getSourceType());
3344 broadcast,
"source is a scalar, shape_cast doesn't support scalar");
3348 if (sourceType.getNumElements() != outType.getNumElements()) {
3350 broadcast,
"broadcast to a greater number of elements");
3360void BroadcastOp::getCanonicalizationPatterns(RewritePatternSet &results,
3361 MLIRContext *context) {
3362 results.
add<BroadcastFolder, BroadcastToShapeCast>(context);
3369LogicalResult ShuffleOp::verify() {
3370 VectorType resultType = getResultVectorType();
3371 VectorType v1Type = getV1VectorType();
3372 VectorType v2Type = getV2VectorType();
3374 int64_t resRank = resultType.getRank();
3375 int64_t v1Rank = v1Type.getRank();
3376 int64_t v2Rank = v2Type.getRank();
3377 bool wellFormed0DCase = v1Rank == 0 && v2Rank == 0 && resRank == 1;
3378 bool wellFormedNDCase = v1Rank == resRank && v2Rank == resRank;
3379 if (!wellFormed0DCase && !wellFormedNDCase)
3380 return emitOpError(
"rank mismatch");
3383 for (int64_t r = 1; r < v1Rank; ++r) {
3384 int64_t resDim = resultType.getDimSize(r);
3385 int64_t v1Dim = v1Type.getDimSize(r);
3386 int64_t v2Dim = v2Type.getDimSize(r);
3387 if (resDim != v1Dim || v1Dim != v2Dim)
3388 return emitOpError(
"dimension mismatch");
3391 ArrayRef<int64_t> mask = getMask();
3392 int64_t maskLength = mask.size();
3393 if (maskLength <= 0)
3394 return emitOpError(
"invalid mask length");
3395 if (maskLength != resultType.getDimSize(0))
3396 return emitOpError(
"mask length mismatch");
3398 int64_t indexSize = (v1Type.getRank() == 0 ? 1 : v1Type.getDimSize(0)) +
3399 (v2Type.getRank() == 0 ? 1 : v2Type.getDimSize(0));
3400 for (
auto [idx, maskPos] : llvm::enumerate(mask)) {
3402 return emitOpError(
"mask index #") << (idx + 1) <<
" out of range";
3408ShuffleOp::inferReturnTypes(MLIRContext *, std::optional<Location> loc,
3409 ShuffleOp::Adaptor adaptor,
3410 SmallVectorImpl<Type> &inferredReturnTypes) {
3411 auto v1Type = llvm::dyn_cast<VectorType>(adaptor.getV1().getType());
3415 auto v1Rank = v1Type.getRank();
3418 SmallVector<int64_t, 4> shape;
3419 shape.reserve(v1Rank);
3420 shape.push_back(std::max<size_t>(1, adaptor.getMask().size()));
3423 llvm::append_range(shape, v1Type.getShape().drop_front());
3424 inferredReturnTypes.push_back(
3425 VectorType::get(shape, v1Type.getElementType()));
3429template <
typename T>
3432 return idxArr.size() == width && llvm::all_of(idxArr, [&expected](T value) {
3433 return value == expected++;
3440 auto v1Type = op.getV1VectorType();
3441 auto v2Type = op.getV2VectorType();
3442 auto mask = op.getMask();
3455 if (!isV1Poison && !isV2Poison)
3458 int64_t v1Size = op.getV1VectorType().getDimSize(0);
3459 bool changed =
false;
3461 for (
int64_t &idx : newMask) {
3462 if (idx == ShuffleOp::kPoisonIndex)
3464 if ((isV1Poison && idx < v1Size) || (isV2Poison && idx >= v1Size)) {
3465 idx = ShuffleOp::kPoisonIndex;
3473 op.setMask(newMask);
3474 return op.getResult();
3483 return ub::PoisonAttr::get(context);
3490 auto v1Type = op.getV1VectorType();
3491 if (v1Type.getRank() != 1)
3503 auto v2DenseAttr = dyn_cast<DenseElementsAttr>(v2Attr);
3506 v2Elements = to_vector(v2DenseAttr.getValues<
Attribute>());
3507 poisonElement = v2Elements[0];
3510 auto v1DenseAttr = dyn_cast<DenseElementsAttr>(v1Attr);
3513 v1Elements = to_vector(v1DenseAttr.getValues<
Attribute>());
3514 poisonElement = v1Elements[0];
3519 int64_t v1Size = v1Type.getDimSize(0);
3520 for (
int64_t maskIdx : mask) {
3523 if (maskIdx == ShuffleOp::kPoisonIndex) {
3524 indexedElm = poisonElement;
3526 if (maskIdx < v1Size)
3527 indexedElm = isV1Poison ? poisonElement : v1Elements[maskIdx];
3529 indexedElm = isV2Poison ? poisonElement : v2Elements[maskIdx - v1Size];
3532 results.push_back(indexedElm);
3538OpFoldResult vector::ShuffleOp::fold(FoldAdaptor adaptor) {
3539 auto v1Type = getV1VectorType();
3541 assert(!v1Type.isScalable() && !getV2VectorType().isScalable() &&
3542 "Vector shuffle does not support scalable vectors");
3546 if (v1Type.getRank() == 0)
3554 Attribute v1Attr = adaptor.getV1(), v2Attr = adaptor.getV2();
3555 if (!v1Attr || !v2Attr)
3570struct Canonicalize0DShuffleOp :
public OpRewritePattern<ShuffleOp> {
3573 LogicalResult matchAndRewrite(ShuffleOp shuffleOp,
3574 PatternRewriter &rewriter)
const override {
3575 VectorType v1VectorType = shuffleOp.getV1VectorType();
3576 ArrayRef<int64_t> mask = shuffleOp.getMask();
3577 if (v1VectorType.getRank() > 0)
3579 if (mask.size() != 1)
3581 VectorType resType = VectorType::Builder(v1VectorType).setShape({1});
3599static Value getScalarSplatSource(Value value) {
3605 auto broadcast = dyn_cast<vector::BroadcastOp>(defOp);
3612 if (isa<VectorType>(
broadcast.getSourceType()))
3620class ShuffleSplat final :
public OpRewritePattern<ShuffleOp> {
3624 LogicalResult matchAndRewrite(ShuffleOp op,
3625 PatternRewriter &rewriter)
const override {
3626 Value splat = getScalarSplatSource(op.getV1());
3627 if (!splat || getScalarSplatSource(op.getV2()) != splat)
3637class ShuffleInterleave :
public OpRewritePattern<ShuffleOp> {
3641 LogicalResult matchAndRewrite(ShuffleOp op,
3642 PatternRewriter &rewriter)
const override {
3643 VectorType resultType = op.getResultVectorType();
3644 if (resultType.isScalable())
3646 op,
"ShuffleOp can't represent a scalable interleave");
3648 if (resultType.getRank() != 1)
3650 op,
"ShuffleOp can't represent an n-D interleave");
3652 VectorType sourceType = op.getV1VectorType();
3653 if (sourceType != op.getV2VectorType() ||
3654 sourceType.getNumElements() * 2 != resultType.getNumElements()) {
3656 op,
"ShuffleOp types don't match an interleave");
3659 ArrayRef<int64_t> shuffleMask = op.getMask();
3660 int64_t resultVectorSize = resultType.getNumElements();
3661 for (
int i = 0, e = resultVectorSize / 2; i < e; ++i) {
3662 int64_t maskValueA = shuffleMask[i * 2];
3663 int64_t maskValueB = shuffleMask[(i * 2) + 1];
3664 if (maskValueA != i || maskValueB != (resultVectorSize / 2) + i)
3666 "ShuffleOp mask not interleaving");
3682class FoldUnusedShuffleOperand final :
public OpRewritePattern<ShuffleOp> {
3686 LogicalResult matchAndRewrite(ShuffleOp op,
3687 PatternRewriter &rewriter)
const override {
3689 if (llvm::all_of(op.getMask(), [](int64_t mask) {
3690 return mask == ShuffleOp::kPoisonIndex;
3697 auto replaceOperandWithPoison = [&](OpOperand &operand) {
3700 Value poison = ub::PoisonOp::create(rewriter, op.getLoc(),
3709 int64_t leadingV1Size = op.getV1VectorType().getRank() > 0
3710 ? op.getV1VectorType().getDimSize(0)
3712 bool isV1Used = llvm::any_of(op.getMask(), [&](int64_t mask) {
3713 return mask != ShuffleOp::kPoisonIndex && mask < leadingV1Size;
3715 if (!isV1Used && succeeded(replaceOperandWithPoison(op.getV1Mutable())))
3719 bool isV2Used = llvm::any_of(op.getMask(), [&](int64_t mask) {
3720 return mask != ShuffleOp::kPoisonIndex && mask >= leadingV1Size;
3722 if (!isV2Used && succeeded(replaceOperandWithPoison(op.getV2Mutable())))
3730void ShuffleOp::getCanonicalizationPatterns(RewritePatternSet &results,
3731 MLIRContext *context) {
3732 results.
add<ShuffleSplat, ShuffleInterleave, Canonicalize0DShuffleOp,
3733 FoldUnusedShuffleOperand>(context);
3740void vector::InsertOp::inferResultRanges(ArrayRef<ConstantIntRanges> argRanges,
3742 setResultRanges(getResult(), argRanges[0].rangeUnion(argRanges[1]));
3745void vector::InsertOp::build(OpBuilder &builder, OperationState &
result,
3746 Value source, Value dest) {
3747 auto vectorTy = cast<VectorType>(dest.
getType());
3748 build(builder,
result, source, dest,
3749 SmallVector<int64_t>(vectorTy.getRank(), 0));
3752void vector::InsertOp::build(OpBuilder &builder, OperationState &
result,
3753 Value source, Value dest, int64_t position) {
3754 build(builder,
result, source, dest, ArrayRef<int64_t>{position});
3757void vector::InsertOp::build(OpBuilder &builder, OperationState &
result,
3758 Value source, Value dest, OpFoldResult position) {
3759 build(builder,
result, source, dest, ArrayRef<OpFoldResult>{position});
3762void vector::InsertOp::build(OpBuilder &builder, OperationState &
result,
3763 Value source, Value dest,
3764 ArrayRef<int64_t> position) {
3765 SmallVector<OpFoldResult> posVals;
3766 posVals.reserve(position.size());
3767 llvm::transform(position, std::back_inserter(posVals),
3769 build(builder,
result, source, dest, posVals);
3772void vector::InsertOp::build(OpBuilder &builder, OperationState &
result,
3773 Value source, Value dest,
3774 ArrayRef<OpFoldResult> position) {
3775 SmallVector<int64_t> staticPos;
3776 SmallVector<Value> dynamicPos;
3778 build(builder,
result, source, dest, dynamicPos,
3782LogicalResult InsertOp::verify() {
3783 if (
auto srcTy = dyn_cast<VectorType>(getValueToStoreType()))
3784 if (srcTy.getRank() == 0)
3786 "expected a scalar instead of a 0-d vector as the source operand");
3788 SmallVector<OpFoldResult> position = getMixedPosition();
3789 auto destVectorType = getDestVectorType();
3790 if (position.size() >
static_cast<unsigned>(destVectorType.getRank()))
3792 "expected position attribute of rank no greater than dest vector rank");
3793 auto srcVectorType = llvm::dyn_cast<VectorType>(getValueToStoreType());
3794 if (srcVectorType &&
3795 (
static_cast<unsigned>(srcVectorType.getRank()) + position.size() !=
3796 static_cast<unsigned>(destVectorType.getRank())))
3797 return emitOpError(
"expected position attribute rank + source rank to "
3798 "match dest vector rank");
3799 if (!srcVectorType &&
3800 (position.size() !=
static_cast<unsigned>(destVectorType.getRank())))
3802 "expected position attribute rank to match the dest vector rank");
3803 for (
auto [idx, pos] : llvm::enumerate(position)) {
3804 if (
auto attr = dyn_cast<Attribute>(pos)) {
3805 int64_t constIdx = cast<IntegerAttr>(attr).getInt();
3807 destVectorType.getDimSize(idx))) {
3808 return emitOpError(
"expected position attribute #")
3810 <<
" to be a non-negative integer smaller than the "
3812 "dest vector dimension";
3825 assert(positions.size() <= completePositions.size() &&
3826 "positions size must be less than or equal to destTy rank");
3827 copy(positions, completePositions.begin());
3835class InsertToBroadcast final :
public OpRewritePattern<InsertOp> {
3839 LogicalResult matchAndRewrite(InsertOp insertOp,
3840 PatternRewriter &rewriter)
const override {
3842 llvm::dyn_cast<VectorType>(insertOp.getValueToStoreType());
3843 if (!srcVecType || insertOp.getDestVectorType().getNumElements() !=
3844 srcVecType.getNumElements())
3847 insertOp, insertOp.getDestVectorType(), insertOp.getValueToStore());
3853class InsertSplatToSplat final :
public OpRewritePattern<InsertOp> {
3857 LogicalResult matchAndRewrite(InsertOp op,
3858 PatternRewriter &rewriter)
const override {
3860 Value splat = getScalarSplatSource(op.getValueToStore());
3861 if (!splat || getScalarSplatSource(op.getDest()) != splat)
3889class InsertChainFullyInitialized final :
public OpRewritePattern<InsertOp> {
3892 LogicalResult matchAndRewrite(InsertOp op,
3893 PatternRewriter &rewriter)
const override {
3895 VectorType destTy = op.getDestVectorType();
3896 if (destTy.isScalable())
3899 for (Operation *user : op.getResult().getUsers())
3900 if (
auto insertOp = dyn_cast<InsertOp>(user))
3901 if (insertOp.getDest() == op.getResult())
3904 InsertOp currentOp = op;
3905 SmallVector<InsertOp> chainInsertOps;
3908 if (currentOp.hasDynamicPosition())
3911 chainInsertOps.push_back(currentOp);
3912 currentOp = currentOp.getDest().getDefiningOp<InsertOp>();
3915 if (currentOp && !currentOp->hasOneUse())
3919 int64_t vectorSize = destTy.getNumElements();
3920 int64_t initializedCount = 0;
3921 SmallVector<bool> initializedDestIdxs(vectorSize,
false);
3922 SmallVector<int64_t> pendingInsertPos;
3923 SmallVector<int64_t> pendingInsertSize;
3924 SmallVector<Value> pendingInsertValues;
3926 for (
auto insertOp : chainInsertOps) {
3928 if (is_contained(insertOp.getStaticPosition(), InsertOp::kPoisonIndex))
3932 int64_t insertBeginPosition =
3937 int64_t insertSize = 1;
3938 if (
auto srcVectorType =
3939 llvm::dyn_cast<VectorType>(insertOp.getValueToStoreType()))
3940 insertSize = srcVectorType.getNumElements();
3942 assert(insertBeginPosition + insertSize <= vectorSize &&
3943 "insert would overflow the vector");
3945 for (
auto index : llvm::seq<int64_t>(insertBeginPosition,
3946 insertBeginPosition + insertSize)) {
3947 if (initializedDestIdxs[index])
3949 initializedDestIdxs[index] =
true;
3955 pendingInsertPos.push_back(insertBeginPosition);
3956 pendingInsertSize.push_back(insertSize);
3957 pendingInsertValues.push_back(insertOp.getValueToStore());
3959 if (initializedCount == vectorSize)
3964 if (initializedCount != vectorSize)
3967 SmallVector<Value> elements(vectorSize);
3968 for (
auto [insertBeginPosition, insertSize, valueToStore] :
3969 llvm::reverse(llvm::zip(pendingInsertPos, pendingInsertSize,
3970 pendingInsertValues))) {
3971 auto srcVectorType = llvm::dyn_cast<VectorType>(valueToStore.getType());
3973 if (!srcVectorType) {
3974 elements[insertBeginPosition] = valueToStore;
3978 Repeated<Type> elementToInsertTypes(insertSize,
3979 srcVectorType.getElementType());
3981 auto elementsToInsert = vector::ToElementsOp::create(
3982 rewriter, op.getLoc(), elementToInsertTypes, valueToStore);
3983 for (int64_t linearIdx = 0; linearIdx < insertSize; linearIdx++) {
3984 elements[insertBeginPosition + linearIdx] =
3985 elementsToInsert.getResult(linearIdx);
3999 int64_t maxVectorSizeFoldThreshold) {
4000 if (insertOp.hasDynamicPosition())
4003 auto denseDst = llvm::dyn_cast_if_present<DenseElementsAttr>(dstAttr);
4011 VectorType destTy = insertOp.getDestVectorType();
4012 if (destTy.isScalable())
4016 if (destTy.getNumElements() > maxVectorSizeFoldThreshold &&
4017 !insertOp->hasOneUse())
4022 if (is_contained(insertOp.getStaticPosition(), InsertOp::kPoisonIndex))
4029 Type destEltType = destTy.getElementType();
4033 if (
auto denseSource = llvm::dyn_cast<DenseElementsAttr>(srcAttr)) {
4034 for (
auto value : denseSource.getValues<
Attribute>())
4040 auto allValues = llvm::to_vector(denseDst.getValues<
Attribute>());
4041 copy(insertedValues, allValues.begin() + insertBeginPosition);
4050 auto destInsert = insertOp.getDest().
getDefiningOp<InsertOp>();
4054 if (insertOp.getMixedPosition() != destInsert.getMixedPosition())
4057 insertOp.
setOperand(1, destInsert.getDest());
4058 return insertOp.getResult();
4061void InsertOp::getCanonicalizationPatterns(RewritePatternSet &results,
4062 MLIRContext *context) {
4063 results.
add<InsertToBroadcast, BroadcastFolder, InsertSplatToSplat,
4064 InsertChainFullyInitialized>(context);
4067OpFoldResult InsertOp::fold(FoldAdaptor adaptor) {
4070 constexpr int64_t vectorSizeFoldThreshold = 256;
4074 if (getNumIndices() == 0 && getValueToStoreType() ==
getType())
4075 return getValueToStore();
4079 SmallVector<Value> operands = {getValueToStore(), getDest()};
4085 getContext(), adaptor.getStaticPosition(), kPoisonIndex))
4088 *
this, adaptor.getValueToStore(), adaptor.getDest(),
4089 vectorSizeFoldThreshold)) {
4093 return inplaceFolded;
4100void InsertStridedSliceOp::build(OpBuilder &builder, OperationState &
result,
4101 Value source, Value dest,
4102 ArrayRef<int64_t> offsets,
4103 ArrayRef<int64_t> strides) {
4104 result.addOperands({source, dest});
4108 result.addAttribute(InsertStridedSliceOp::getOffsetsAttrName(
result.name),
4110 result.addAttribute(InsertStridedSliceOp::getStridesAttrName(
result.name),
4115template <
typename OpType>
4119 StringRef attrName) {
4120 if (arrayAttr.size() >
shape.size())
4121 return op.emitOpError(
"expected ")
4122 << attrName <<
" attribute of rank no greater than vector rank";
4129template <
typename OpType>
4133 bool halfOpen =
true) {
4134 for (
auto attr : arrayAttr) {
4135 auto val = llvm::cast<IntegerAttr>(attr).getInt();
4139 if (val < min || val >= upper)
4140 return op.emitOpError(
"expected ") << attrName <<
" to be confined to ["
4141 <<
min <<
", " << upper <<
")";
4149template <
typename OpType>
4154 for (
auto [
index, attrDimPair] :
4155 llvm::enumerate(llvm::zip_first(arrayAttr,
shape))) {
4156 int64_t val = llvm::cast<IntegerAttr>(std::get<0>(attrDimPair)).getInt();
4160 if (val < min || val >=
max)
4161 return op.emitOpError(
"expected ")
4162 << attrName <<
" dimension " <<
index <<
" to be confined to ["
4163 <<
min <<
", " <<
max <<
")";
4173template <
typename OpType>
4178 assert(arrayAttr1.size() <=
shape.size());
4179 assert(arrayAttr2.size() <=
shape.size());
4180 for (
auto [
index, it] :
4181 llvm::enumerate(llvm::zip(arrayAttr1, arrayAttr2,
shape))) {
4182 auto val1 = llvm::cast<IntegerAttr>(std::get<0>(it)).getInt();
4183 auto val2 = llvm::cast<IntegerAttr>(std::get<1>(it)).getInt();
4187 if (val1 + val2 < 0 || val1 + val2 >=
max)
4188 return op.emitOpError(
"expected sum(")
4189 << attrName1 <<
", " << attrName2 <<
") dimension " <<
index
4190 <<
" to be confined to [" <<
min <<
", " <<
max <<
")";
4198 return IntegerAttr::get(IntegerType::get(context, 64), APInt(64, v));
4200 return ArrayAttr::get(context, llvm::to_vector<8>(attrs));
4203LogicalResult InsertStridedSliceOp::verify() {
4204 auto sourceVectorType = getSourceVectorType();
4205 auto destVectorType = getDestVectorType();
4206 auto offsets = getOffsetsAttr();
4207 auto strides = getStridesAttr();
4208 if (offsets.size() !=
static_cast<unsigned>(destVectorType.getRank()))
4210 "expected offsets of same size as destination vector rank");
4211 if (strides.size() !=
static_cast<unsigned>(sourceVectorType.getRank()))
4212 return emitOpError(
"expected strides of same size as source vector rank");
4213 if (sourceVectorType.getRank() > destVectorType.getRank())
4215 "expected source rank to be no greater than destination rank");
4217 auto sourceShape = sourceVectorType.getShape();
4218 auto destShape = destVectorType.getShape();
4219 SmallVector<int64_t, 4> sourceShapeAsDestShape(
4220 destShape.size() - sourceShape.size(), 0);
4221 sourceShapeAsDestShape.append(sourceShape.begin(), sourceShape.end());
4222 auto offName = InsertStridedSliceOp::getOffsetsAttrName();
4223 auto stridesName = InsertStridedSliceOp::getStridesAttrName();
4232 offName,
"source vector shape",
4236 unsigned rankDiff = destShape.size() - sourceShape.size();
4237 for (
unsigned idx = 0; idx < sourceShape.size(); ++idx) {
4238 if (sourceVectorType.getScalableDims()[idx] !=
4239 destVectorType.getScalableDims()[idx + rankDiff]) {
4240 return emitOpError(
"mismatching scalable flags (at source vector idx=")
4243 if (sourceVectorType.getScalableDims()[idx]) {
4244 auto sourceSize = sourceShape[idx];
4245 auto destSize = destShape[idx + rankDiff];
4246 if (sourceSize != destSize) {
4247 return emitOpError(
"expected size at idx=")
4249 << (
" to match the corresponding base size from the input "
4251 << sourceSize << (
" vs ") << destSize << (
")");
4261class FoldInsertStridedSliceSplat final
4262 :
public OpRewritePattern<InsertStridedSliceOp> {
4266 LogicalResult matchAndRewrite(InsertStridedSliceOp insertStridedSliceOp,
4267 PatternRewriter &rewriter)
const override {
4269 auto dst = insertStridedSliceOp.getDest();
4270 auto splat = getScalarSplatSource(insertStridedSliceOp.getValueToStore());
4271 if (!splat || getScalarSplatSource(dst) != splat)
4274 rewriter.
replaceOp(insertStridedSliceOp, dst);
4281class FoldInsertStridedSliceOfExtract final
4282 :
public OpRewritePattern<InsertStridedSliceOp> {
4286 LogicalResult matchAndRewrite(InsertStridedSliceOp insertStridedSliceOp,
4287 PatternRewriter &rewriter)
const override {
4288 auto extractStridedSliceOp =
4289 insertStridedSliceOp.getValueToStore()
4290 .getDefiningOp<vector::ExtractStridedSliceOp>();
4292 if (!extractStridedSliceOp)
4295 if (extractStridedSliceOp.getOperand() != insertStridedSliceOp.getDest())
4299 if (extractStridedSliceOp.getStrides() !=
4300 insertStridedSliceOp.getStrides() ||
4301 extractStridedSliceOp.getOffsets() != insertStridedSliceOp.getOffsets())
4304 rewriter.
replaceOp(insertStridedSliceOp, insertStridedSliceOp.getDest());
4311class InsertStridedSliceConstantFolder final
4312 :
public OpRewritePattern<InsertStridedSliceOp> {
4318 static constexpr int64_t vectorSizeFoldThreshold = 256;
4320 LogicalResult matchAndRewrite(InsertStridedSliceOp op,
4321 PatternRewriter &rewriter)
const override {
4325 Attribute vectorDestCst;
4329 VectorType destTy = destVector.getType();
4330 if (destTy.isScalable())
4334 if (destTy.getNumElements() > vectorSizeFoldThreshold &&
4335 !destVector.hasOneUse())
4339 Attribute sourceCst;
4349 if (op.hasNonUnitStrides())
4352 VectorType sliceVecTy = sourceValue.getType();
4353 ArrayRef<int64_t> sliceShape = sliceVecTy.getShape();
4354 int64_t rankDifference = destTy.getRank() - sliceVecTy.getRank();
4355 SmallVector<int64_t, 4> offsets =
getI64SubArray(op.getOffsets());
4356 SmallVector<int64_t, 4> destStrides =
computeStrides(destTy.getShape());
4364 auto denseDest = llvm::cast<DenseElementsAttr>(vectorDestCst);
4365 auto denseSlice = llvm::cast<DenseElementsAttr>(sourceCst);
4366 auto sliceValuesIt = denseSlice.value_begin<Attribute>();
4367 auto newValues = llvm::to_vector(denseDest.getValues<Attribute>());
4368 SmallVector<int64_t> currDestPosition(offsets.begin(), offsets.end());
4369 MutableArrayRef<int64_t> currSlicePosition(
4370 currDestPosition.begin() + rankDifference, currDestPosition.end());
4371 ArrayRef<int64_t> sliceOffsets(offsets.begin() + rankDifference,
4374 int64_t linearizedPosition =
linearize(currDestPosition, destStrides);
4375 assert(linearizedPosition < destTy.getNumElements() &&
"Invalid index");
4376 assert(sliceValuesIt != denseSlice.value_end<Attribute>() &&
4377 "Invalid slice element");
4378 newValues[linearizedPosition] = *sliceValuesIt;
4391void vector::InsertStridedSliceOp::getCanonicalizationPatterns(
4392 RewritePatternSet &results, MLIRContext *context) {
4393 results.
add<FoldInsertStridedSliceSplat, FoldInsertStridedSliceOfExtract,
4394 InsertStridedSliceConstantFolder>(context);
4397OpFoldResult InsertStridedSliceOp::fold(FoldAdaptor adaptor) {
4398 if (getSourceVectorType() == getDestVectorType())
4399 return getValueToStore();
4408void OuterProductOp::build(OpBuilder &builder, OperationState &
result,
4409 Value
lhs, Value
rhs, Value acc) {
4414void OuterProductOp::print(OpAsmPrinter &p) {
4415 p <<
" " << getLhs() <<
", " << getRhs();
4417 p <<
", " << getAcc();
4418 SmallVector<NamedAttribute> attrs((*this)->getDiscardableAttrs());
4419 attrs.emplace_back(getKindAttrName(), getKindAttr());
4423 p <<
" : " << getLhs().getType() <<
", " << getRhs().getType();
4426ParseResult OuterProductOp::parse(OpAsmParser &parser, OperationState &
result) {
4427 SmallVector<OpAsmParser::UnresolvedOperand, 3> operandsInfo;
4434 if (operandsInfo.size() < 2)
4436 "expected at least 2 operands");
4437 VectorType vLHS = llvm::dyn_cast<VectorType>(tLHS);
4438 VectorType vRHS = llvm::dyn_cast<VectorType>(tRHS);
4441 "expected vector type for operand #1");
4445 if (vLHS.getRank() == 0)
4447 "expected 1-d vector for operand #1");
4448 if (vRHS && vRHS.getRank() == 0)
4450 "expected 1-d vector for operand #2");
4454 SmallVector<bool> scalableDimsRes{vLHS.getScalableDims()[0],
4455 vRHS.getScalableDims()[0]};
4456 resType = VectorType::get({vLHS.getDimSize(0), vRHS.getDimSize(0)},
4457 vLHS.getElementType(), scalableDimsRes);
4460 SmallVector<bool> scalableDimsRes{vLHS.getScalableDims()[0]};
4461 resType = VectorType::get({vLHS.getDimSize(0)}, vLHS.getElementType(),
4465 if (!
result.attributes.get(OuterProductOp::getKindAttrName(
result.name))) {
4466 result.attributes.append(
4467 OuterProductOp::getKindAttrName(
result.name),
4468 CombiningKindAttr::get(
result.getContext(),
4469 OuterProductOp::getDefaultKind()));
4475 (operandsInfo.size() > 2 &&
4480LogicalResult OuterProductOp::verify() {
4481 Type tRHS = getOperandTypeRHS();
4482 VectorType vLHS = getOperandVectorTypeLHS(),
4483 vRHS = llvm::dyn_cast<VectorType>(tRHS),
4484 vACC = getOperandVectorTypeACC(), vRES = getResultVectorType();
4486 if (vLHS.getRank() != 1)
4487 return emitOpError(
"expected 1-d vector for operand #1");
4491 if (vRHS.getRank() != 1)
4492 return emitOpError(
"expected 1-d vector for operand #2");
4493 if (vRES.getRank() != 2)
4494 return emitOpError(
"expected 2-d vector result");
4495 if (vLHS.getDimSize(0) != vRES.getDimSize(0))
4496 return emitOpError(
"expected #1 operand dim to match result dim #1");
4497 if (vRHS.getDimSize(0) != vRES.getDimSize(1))
4498 return emitOpError(
"expected #2 operand dim to match result dim #2");
4499 if (vLHS.isScalable() && !vRHS.isScalable()) {
4503 "expected either both or only #2 operand dim to be scalable");
4507 if (vRES.getRank() != 1)
4508 return emitOpError(
"expected 1-d vector result");
4509 if (vLHS.getDimSize(0) != vRES.getDimSize(0))
4510 return emitOpError(
"expected #1 operand dim to match result dim #1");
4513 if (vACC && vACC != vRES)
4514 return emitOpError(
"expected operand #3 of same type as result type");
4516 if (!getKindAttr()) {
4517 return emitOpError(
"expected 'kind' attribute of type CombiningKind (e.g. "
4518 "'vector.kind<add>')");
4523 return emitOpError(
"unsupported outerproduct type");
4532Type OuterProductOp::getExpectedMaskType() {
4533 auto vecType = this->getResultVectorType();
4534 return VectorType::get(vecType.getShape(),
4535 IntegerType::get(vecType.getContext(), 1),
4536 vecType.getScalableDims());
4550 assert(offsets.size() == sizes.size() && offsets.size() == strides.size());
4552 shape.reserve(vectorType.getRank());
4554 for (
unsigned e = offsets.size(); idx < e; ++idx)
4555 shape.push_back(llvm::cast<IntegerAttr>(sizes[idx]).getInt());
4556 for (
unsigned e = vectorType.getShape().size(); idx < e; ++idx)
4557 shape.push_back(vectorType.getShape()[idx]);
4559 return VectorType::get(
shape, vectorType.getElementType(),
4560 vectorType.getScalableDims());
4563void ExtractStridedSliceOp::build(OpBuilder &builder, OperationState &
result,
4564 Value source, ArrayRef<int64_t> offsets,
4565 ArrayRef<int64_t> sizes,
4566 ArrayRef<int64_t> strides) {
4567 result.addOperands(source);
4573 offsetsAttr, sizesAttr, stridesAttr));
4574 result.addAttribute(ExtractStridedSliceOp::getOffsetsAttrName(
result.name),
4576 result.addAttribute(ExtractStridedSliceOp::getSizesAttrName(
result.name),
4578 result.addAttribute(ExtractStridedSliceOp::getStridesAttrName(
result.name),
4582LogicalResult ExtractStridedSliceOp::verify() {
4583 auto type = getSourceVectorType();
4584 auto offsets = getOffsetsAttr();
4585 auto sizes = getSizesAttr();
4586 auto strides = getStridesAttr();
4587 if (offsets.size() != sizes.size() || offsets.size() != strides.size())
4589 "expected offsets, sizes and strides attributes of same size");
4591 auto shape = type.getShape();
4592 auto offName = getOffsetsAttrName();
4593 auto sizesName = getSizesAttrName();
4594 auto stridesName = getStridesAttrName();
4610 shape, offName, sizesName,
4615 offsets, sizes, strides);
4616 if (getResult().
getType() != resultType)
4617 return emitOpError(
"expected result type to be ") << resultType;
4619 for (
unsigned idx = 0; idx < sizes.size(); ++idx) {
4620 if (type.getScalableDims()[idx]) {
4621 auto inputDim = type.getShape()[idx];
4622 auto inputSize = llvm::cast<IntegerAttr>(sizes[idx]).getInt();
4623 if (inputDim != inputSize)
4624 return emitOpError(
"expected size at idx=")
4626 << (
" to match the corresponding base size from the input "
4628 << inputSize << (
" vs ") << inputDim << (
")");
4641 auto getElement = [](
ArrayAttr array,
int idx) {
4642 return llvm::cast<IntegerAttr>(array[idx]).getInt();
4644 ArrayAttr extractOffsets = op.getOffsets();
4647 auto insertOp = op.getSource().getDefiningOp<InsertStridedSliceOp>();
4649 if (op.getSourceVectorType().getRank() !=
4650 insertOp.getSourceVectorType().getRank())
4652 ArrayAttr insertOffsets = insertOp.getOffsets();
4653 ArrayAttr insertStrides = insertOp.getStrides();
4656 if (extractOffsets.size() > insertOffsets.size())
4658 bool patialoverlap =
false;
4659 bool disjoint =
false;
4661 for (
unsigned dim = 0, e = extractOffsets.size(); dim < e; ++dim) {
4662 if (getElement(
extractStrides, dim) != getElement(insertStrides, dim))
4664 int64_t start = getElement(insertOffsets, dim);
4665 int64_t end = start + insertOp.getSourceVectorType().getDimSize(dim);
4666 int64_t offset = getElement(extractOffsets, dim);
4667 int64_t size = getElement(extractSizes, dim);
4669 if (start <= offset && offset < end) {
4672 if (offset + size > end)
4673 patialoverlap =
true;
4674 offsetDiffs.push_back(offset - start);
4681 if (!disjoint && !patialoverlap) {
4682 op.setOperand(insertOp.getValueToStore());
4685 op.setOffsetsAttr(
b.getI64ArrayAttr(offsetDiffs));
4691 insertOp = insertOp.getDest().getDefiningOp<InsertStridedSliceOp>();
4706 auto dense = llvm::dyn_cast_if_present<DenseElementsAttr>(foldInput);
4711 if (op.hasNonUnitStrides())
4714 VectorType sourceVecTy = op.getSourceVectorType();
4718 VectorType sliceVecTy = op.getType();
4720 int64_t rank = sliceVecTy.getRank();
4732 const auto denseValuesBegin = dense.value_begin<
Attribute>();
4734 sliceValues.reserve(sliceVecTy.getNumElements());
4738 assert(linearizedPosition < sourceVecTy.getNumElements() &&
4740 sliceValues.push_back(*(denseValuesBegin + linearizedPosition));
4741 }
while (succeeded(
incSlicePosition(currSlicePosition, sliceShape, offsets)));
4743 assert(
static_cast<int64_t>(sliceValues.size()) ==
4744 sliceVecTy.getNumElements() &&
4745 "Invalid number of slice elements");
4749OpFoldResult ExtractStridedSliceOp::fold(FoldAdaptor adaptor) {
4750 if (getSourceVectorType() == getResult().
getType())
4757 llvm::dyn_cast_if_present<SplatElementsAttr>(adaptor.getSource()))
4764void ExtractStridedSliceOp::getOffsets(SmallVectorImpl<int64_t> &results) {
4786class StridedSliceFolder final
4787 :
public OpRewritePattern<ExtractStridedSliceOp> {
4789 using OpRewritePattern<ExtractStridedSliceOp>::OpRewritePattern;
4791 LogicalResult matchAndRewrite(ExtractStridedSliceOp secondOp,
4792 PatternRewriter &rewriter)
const override {
4793 auto firstOp = secondOp.getSource().getDefiningOp<ExtractStridedSliceOp>();
4797 if (secondOp.hasNonUnitStrides() || firstOp.hasNonUnitStrides())
4800 SmallVector<int64_t> firstOffsets =
getI64SubArray(firstOp.getOffsets());
4801 SmallVector<int64_t> firstSizes =
getI64SubArray(firstOp.getSizes());
4802 SmallVector<int64_t> secondOffsets =
getI64SubArray(secondOp.getOffsets());
4803 SmallVector<int64_t> secondSizes =
getI64SubArray(secondOp.getSizes());
4805 unsigned newRank = std::max(firstOffsets.size(), secondOffsets.size());
4806 SmallVector<int64_t> combinedOffsets(newRank, 0);
4807 SmallVector<int64_t> combinedSizes(newRank);
4808 ArrayRef<int64_t> firstSourceShape =
4809 firstOp.getSourceVectorType().getShape();
4810 for (
unsigned i = 0; i < newRank; ++i) {
4811 int64_t off1 = (i < firstOffsets.size()) ? firstOffsets[i] : 0;
4812 int64_t off2 = (i < secondOffsets.size()) ? secondOffsets[i] : 0;
4813 combinedOffsets[i] = off1 + off2;
4815 if (i < secondSizes.size()) {
4816 combinedSizes[i] = secondSizes[i];
4817 }
else if (i < firstSizes.size()) {
4818 combinedSizes[i] = firstSizes[i];
4820 combinedSizes[i] = firstSourceShape[i];
4824 SmallVector<int64_t> combinedStrides(newRank, 1);
4826 secondOp, firstOp.getSource(), combinedOffsets, combinedSizes,
4844class StridedSliceCreateMaskFolder final
4845 :
public OpRewritePattern<ExtractStridedSliceOp> {
4849 LogicalResult matchAndRewrite(ExtractStridedSliceOp extractStridedSliceOp,
4850 PatternRewriter &rewriter)
const override {
4851 Location loc = extractStridedSliceOp.getLoc();
4855 extractStridedSliceOp.getSource().getDefiningOp<CreateMaskOp>();
4859 if (extractStridedSliceOp.hasNonUnitStrides())
4862 SmallVector<Value> maskDimSizes(createMaskOp.getOperands());
4864 SmallVector<int64_t> sliceOffsets;
4867 SmallVector<int64_t> sliceSizes;
4871 SmallVector<Value> sliceMaskDimSizes;
4872 sliceMaskDimSizes.reserve(maskDimSizes.size());
4876 for (
auto [maskDimSize, sliceOffset, sliceSize] :
4877 llvm::zip(maskDimSizes, sliceOffsets, sliceSizes)) {
4881 IntegerAttr offsetAttr =
4883 Value offset = arith::ConstantOp::create(rewriter, loc, offsetAttr);
4884 Value sliceMaskDimSize =
4885 arith::SubIOp::create(rewriter, loc, maskDimSize, offset);
4886 sliceMaskDimSizes.push_back(sliceMaskDimSize);
4891 llvm::drop_begin(maskDimSizes, sliceMaskDimSizes.size()));
4895 extractStridedSliceOp, extractStridedSliceOp.getResult().
getType(),
4903class StridedSliceConstantMaskFolder final
4904 :
public OpRewritePattern<ExtractStridedSliceOp> {
4908 LogicalResult matchAndRewrite(ExtractStridedSliceOp extractStridedSliceOp,
4909 PatternRewriter &rewriter)
const override {
4912 auto *defOp = extractStridedSliceOp.getSource().getDefiningOp();
4913 auto constantMaskOp = dyn_cast_or_null<ConstantMaskOp>(defOp);
4914 if (!constantMaskOp)
4917 if (extractStridedSliceOp.hasNonUnitStrides())
4920 ArrayRef<int64_t> maskDimSizes = constantMaskOp.getMaskDimSizes();
4922 SmallVector<int64_t> sliceOffsets;
4925 SmallVector<int64_t> sliceSizes;
4929 SmallVector<int64_t> sliceMaskDimSizes;
4930 sliceMaskDimSizes.reserve(maskDimSizes.size());
4931 for (
auto [maskDimSize, sliceOffset, sliceSize] :
4932 llvm::zip(maskDimSizes, sliceOffsets, sliceSizes)) {
4933 int64_t sliceMaskDimSize = std::max(
4934 static_cast<int64_t
>(0),
4935 std::min(sliceOffset + sliceSize, maskDimSize) - sliceOffset);
4936 sliceMaskDimSizes.push_back(sliceMaskDimSize);
4939 if (sliceMaskDimSizes.size() < maskDimSizes.size())
4940 for (
size_t i = sliceMaskDimSizes.size(); i < maskDimSizes.size(); ++i)
4941 sliceMaskDimSizes.push_back(maskDimSizes[i]);
4944 if (llvm::is_contained(sliceMaskDimSizes, 0))
4945 sliceMaskDimSizes.assign(maskDimSizes.size(), 0);
4950 extractStridedSliceOp, extractStridedSliceOp.getResult().
getType(),
4958class StridedSliceBroadcast final
4959 :
public OpRewritePattern<ExtractStridedSliceOp> {
4963 LogicalResult matchAndRewrite(ExtractStridedSliceOp op,
4964 PatternRewriter &rewriter)
const override {
4970 unsigned srcRank = srcVecType ? srcVecType.getRank() : 0;
4971 auto dstVecType = llvm::cast<VectorType>(op.getType());
4972 unsigned dstRank = dstVecType.getRank();
4973 unsigned rankDiff = dstRank - srcRank;
4977 bool needsSlice =
false;
4978 for (
unsigned i = 0; i < srcRank; i++) {
4979 if (srcVecType.getDimSize(i) != 1 &&
4980 srcVecType.getDimSize(i) != dstVecType.getDimSize(i + rankDiff)) {
4987 SmallVector<int64_t> offsets =
4989 SmallVector<int64_t> sizes =
4991 for (
unsigned i = 0; i < srcRank; i++) {
4992 if (srcVecType.getDimSize(i) == 1) {
5000 source = ExtractStridedSliceOp::create(
5001 rewriter, op->getLoc(), source, offsets, sizes,
5010class StridedSliceSplat final :
public OpRewritePattern<ExtractStridedSliceOp> {
5014 LogicalResult matchAndRewrite(ExtractStridedSliceOp op,
5015 PatternRewriter &rewriter)
const override {
5017 Value splat = getScalarSplatSource(op.getSource());
5041class ContiguousExtractStridedSliceToExtract final
5042 :
public OpRewritePattern<ExtractStridedSliceOp> {
5046 LogicalResult matchAndRewrite(ExtractStridedSliceOp op,
5047 PatternRewriter &rewriter)
const override {
5048 if (op.hasNonUnitStrides())
5050 Value source = op.getOperand();
5051 auto sourceType = cast<VectorType>(source.
getType());
5052 if (sourceType.isScalable() || sourceType.getRank() == 0)
5061 for (numOffsets = sizes.size(); numOffsets > 0; --numOffsets) {
5062 if (sizes[numOffsets - 1] != sourceType.getDimSize(numOffsets - 1))
5069 if (numOffsets == 0)
5074 if (numOffsets == sourceType.getRank() &&
5075 static_cast<int>(sizes.size()) == sourceType.getRank())
5079 for (
int i = 0; i < numOffsets; ++i) {
5087 while (numOffsets <
static_cast<int>(sizes.size()) - 1 &&
5088 sizes[numOffsets] == 1) {
5093 auto extractOffsets = ArrayRef(offsets).take_front(numOffsets);
5094 Value extract = vector::ExtractOp::create(rewriter, op->getLoc(), source,
5103void ExtractStridedSliceOp::getCanonicalizationPatterns(
5104 RewritePatternSet &results, MLIRContext *context) {
5107 results.
add<StridedSliceFolder, StridedSliceCreateMaskFolder,
5108 StridedSliceConstantMaskFolder, StridedSliceBroadcast,
5109 StridedSliceSplat, ContiguousExtractStridedSliceToExtract>(
5119void TransferReadOp::build(OpBuilder &builder, OperationState &
result,
5120 VectorType vectorType, Value source,
5122 AffineMapAttr permutationMapAttr,
5125 Type elemType = llvm::cast<ShapedType>(source.
getType()).getElementType();
5127 padding = ub::PoisonOp::create(builder,
result.location, elemType);
5130 build(builder,
result, vectorType, source,
indices, permutationMapAttr,
5131 *padding, Value(), inBoundsAttr);
5139void TransferReadOp::build(OpBuilder &builder, OperationState &
result,
5140 VectorType vectorType, Value source,
5142 AffineMap permutationMap,
5143 std::optional<ArrayRef<bool>> inBounds) {
5144 if (!permutationMap)
5146 llvm::cast<ShapedType>(source.
getType()), vectorType);
5147 auto permutationMapAttr = AffineMapAttr::get(permutationMap);
5148 auto inBoundsAttr = (inBounds && !inBounds.value().empty())
5151 SmallVector<bool>(vectorType.getRank(),
false));
5153 build(builder,
result, vectorType, source,
indices, padding,
5154 permutationMapAttr, inBoundsAttr);
5160void TransferReadOp::build(OpBuilder &builder, OperationState &
result,
5161 VectorType vectorType, Value source,
5163 std::optional<ArrayRef<bool>> inBounds) {
5165 build(builder,
result, vectorType, source,
indices, padding,
5166 AffineMap(), inBounds);
5169template <
typename EmitFun>
5171 EmitFun emitOpError) {
5173 for (
auto expr : permutationMap.
getResults()) {
5174 auto dim = dyn_cast<AffineDimExpr>(expr);
5175 auto zero = dyn_cast<AffineConstantExpr>(expr);
5177 if (zero.getValue() != 0) {
5179 "requires a projected permutation_map (at most one dim or the zero "
5180 "constant can appear in each result)");
5185 return emitOpError(
"requires a projected permutation_map (at most one "
5186 "dim or the zero constant can appear in each result)");
5188 if (seen[dim.getPosition()]) {
5190 "requires a permutation_map that is a permutation (found one dim "
5191 "used more than once)");
5193 seen[dim.getPosition()] =
true;
5200 VectorType vectorType, VectorType maskType,
5201 VectorType inferredMaskType,
AffineMap permutationMap,
5203 if (op->hasDiscardableAttr(
"masked")) {
5204 return op->emitOpError(
"masked attribute has been removed. "
5205 "Use in_bounds instead.");
5208 if (!llvm::isa<MemRefType, RankedTensorType>(shapedType))
5209 return op->emitOpError(
5210 "requires source to be a memref or ranked tensor type");
5212 auto elementType = shapedType.getElementType();
5214 if (
auto vectorElementType = llvm::dyn_cast<VectorType>(elementType)) {
5216 unsigned sourceVecSize =
5218 vectorElementType.getShape().back();
5219 unsigned resultVecSize =
5221 vectorType.getShape().back();
5222 if (resultVecSize % sourceVecSize != 0)
5223 return op->emitOpError(
5224 "requires the bitwidth of the minor 1-D vector to be an integral "
5225 "multiple of the bitwidth of the minor 1-D vector of the source");
5227 unsigned sourceVecEltRank = vectorElementType.getRank();
5228 unsigned resultVecRank = vectorType.getRank();
5229 if (sourceVecEltRank > resultVecRank)
5230 return op->emitOpError(
5231 "requires source vector element and vector result ranks to match.");
5232 unsigned rankOffset = resultVecRank - sourceVecEltRank;
5235 return op->emitOpError(
"requires a permutation_map with result dims of "
5236 "the same rank as the vector type");
5239 return op->emitOpError(
"does not support masks with vector element type");
5242 unsigned minorSize =
5243 vectorType.getRank() == 0 ? 1 : vectorType.getShape().back();
5244 unsigned resultVecSize =
5247 return op->emitOpError(
5248 "requires the bitwidth of the minor 1-D vector to be an integral "
5249 "multiple of the bitwidth of the source element type");
5253 return op->emitOpError(
"requires a permutation_map with result dims of "
5254 "the same rank as the vector type");
5258 return op->emitOpError(
"requires permutation_map without symbols");
5260 if (permutationMap.
getNumInputs() != shapedType.getRank())
5261 return op->emitOpError(
"requires a permutation_map with input dims of the "
5262 "same rank as the source type");
5264 if (maskType && maskType != inferredMaskType)
5265 return op->emitOpError(
"inferred mask type (")
5266 << inferredMaskType <<
") and mask operand type (" << maskType
5270 return op->emitOpError(
"expects the in_bounds attr of same rank "
5271 "as permutation_map results: ")
5272 << AffineMapAttr::get(permutationMap)
5273 <<
" vs inBounds of size: " << inBounds.size();
5281 if (llvm::any_of(op.getInBoundsValues(), [](
bool b) { return b; }))
5282 attrs.emplace_back(op.getInBoundsAttrName(), op.getInBounds());
5283 if (!op.getPermutationMap().isMinorIdentity())
5284 attrs.emplace_back(op.getPermutationMapAttrName(),
5285 AffineMapAttr::get(op.getPermutationMap()));
5290void TransferReadOp::print(OpAsmPrinter &p) {
5293 p <<
", " << getMask();
5300 auto i1Type = IntegerType::get(permMap.
getContext(), 1);
5302 assert(invPermMap &&
"Inversed permutation map couldn't be computed");
5307 if (maskShape.empty())
5308 maskShape.push_back(1);
5313 return VectorType::get(maskShape, i1Type, scalableDims);
5330 if (hasMask.succeeded()) {
5337 if (types.size() != 2)
5338 return parser.
emitError(typesLoc,
"requires two types");
5340 auto shapedType = llvm::dyn_cast<ShapedType>(types[0]);
5341 if (!shapedType || !llvm::isa<MemRefType, RankedTensorType>(shapedType))
5342 return parser.
emitError(typesLoc,
"requires memref or ranked tensor type");
5343 VectorType vectorType = llvm::dyn_cast<VectorType>(types[1]);
5345 return parser.
emitError(typesLoc,
"requires vector type");
5346 auto permMapAttrName = TransferReadOp::getPermutationMapAttrName(
result.name);
5350 if (shapedType.getRank() <
5353 "expected a custom permutation_map when "
5354 "rank(source) != rank(destination)");
5356 result.attributes.set(permMapAttrName, AffineMapAttr::get(permMap));
5358 permMap = llvm::cast<AffineMapAttr>(permMapAttr).getValue();
5360 auto inBoundsAttrName = TransferReadOp::getInBoundsAttrName(
result.name);
5361 Attribute inBoundsAttr =
result.attributes.get(inBoundsAttrName);
5362 if (!inBoundsAttr) {
5363 result.addAttribute(inBoundsAttrName,
5372 if (hasMask.succeeded()) {
5373 if (llvm::dyn_cast<VectorType>(shapedType.getElementType()))
5375 maskInfo.
location,
"does not support masks with vector element type");
5378 "expected the same rank for the vector and the "
5379 "results of the permutation map");
5387 result.addAttribute(TransferReadOp::getOperandSegmentSizeAttr(),
5389 {1, static_cast<int32_t>(indexInfo.size()), 1,
5390 static_cast<int32_t>(hasMask.succeeded())}));
5394LogicalResult TransferReadOp::verify() {
5396 ShapedType shapedType = getShapedType();
5398 VectorType maskType = getMaskType();
5399 auto paddingType = getPadding().getType();
5400 auto permutationMap = getPermutationMap();
5401 VectorType inferredMaskType =
5404 auto sourceElementType = shapedType.getElementType();
5406 if (
static_cast<int64_t
>(
getIndices().size()) != shapedType.getRank())
5407 return emitOpError(
"requires ") << shapedType.getRank() <<
" indices";
5410 shapedType, vectorType, maskType,
5411 inferredMaskType, permutationMap, getInBounds())))
5414 if (
auto sourceVectorElementType =
5415 llvm::dyn_cast<VectorType>(sourceElementType)) {
5418 if (sourceVectorElementType != paddingType)
5420 "requires source element type and padding type to match.");
5424 if (!VectorType::isValidElementType(paddingType))
5425 return emitOpError(
"requires valid padding vector elemental type");
5428 if (paddingType != sourceElementType)
5430 "requires formal padding and source of the same elemental type");
5434 [&](Twine t) {
return emitOpError(t); });
5441Type TransferReadOp::getExpectedMaskType() {
5448VectorType TransferReadOp::getVectorType() {
5449 return cast<VectorType>(getVector().
getType());
5452template <
typename TransferOp>
5456 if (op.getShapedType().isDynamicDim(indicesIdx))
5460 if (op.getVectorType().getScalableDims()[resultIdx])
5464 if (!cstOp.has_value())
5467 int64_t sourceSize = op.getShapedType().getDimSize(indicesIdx);
5468 int64_t vectorSize = op.getVectorType().getDimSize(resultIdx);
5474 int64_t maxStart = sourceSize - vectorSize;
5478 return *cstOp >= 0 && *cstOp <= maxStart;
5481template <
typename TransferOp>
5485 if (op.getTransferRank() == 0)
5488 bool changed =
false;
5490 newInBounds.reserve(op.getTransferRank());
5495 for (
unsigned i = 0; i < op.getTransferRank(); ++i) {
5497 if (op.isDimInBounds(i)) {
5498 newInBounds.push_back(
true);
5503 bool inBounds =
false;
5504 auto dimExpr = dyn_cast<AffineDimExpr>(permutationMap.
getResult(i));
5507 dimExpr.getPosition());
5508 nonBcastDims.push_back(i);
5511 newInBounds.push_back(inBounds);
5513 changed |= inBounds;
5519 bool allNonBcastDimsInBounds = llvm::all_of(
5520 nonBcastDims, [&newInBounds](
unsigned idx) {
return newInBounds[idx]; });
5521 if (allNonBcastDimsInBounds) {
5523 changed |= !newInBounds[idx];
5524 newInBounds[idx] =
true;
5532 op.setInBoundsAttr(
b.getBoolArrayAttr(newInBounds));
5536template <
typename TransferOp>
5538 auto mask = op.getMask();
5545 op.getMaskMutable().clear();
5553template <
typename TransferOp>
5555 VectorType vecType = op.getVectorType();
5556 if (vecType.getRank() != 1 || vecType.getShape()[0] != 1 ||
5557 vecType.isScalable())
5564 int64_t srcRank = op.getShapedType().getRank();
5570 op.setPermutationMapAttr(AffineMapAttr::get(minorIdentity));
5584static Value foldRAW(TransferReadOp readOp) {
5585 if (!llvm::isa<RankedTensorType>(readOp.getShapedType()))
5587 auto defWrite = readOp.getBase().getDefiningOp<vector::TransferWriteOp>();
5590 return defWrite.getVector();
5592 cast<VectorTransferOpInterface>(defWrite.getOperation()),
5593 cast<VectorTransferOpInterface>(readOp.getOperation())))
5595 defWrite = defWrite.getBase().getDefiningOp<vector::TransferWriteOp>();
5600OpFoldResult TransferReadOp::fold(FoldAdaptor) {
5601 if (Value vec = foldRAW(*
this))
5614 return OpFoldResult();
5617std::optional<SmallVector<int64_t, 4>>
5618TransferReadOp::getIterationShapeToUnroll() {
5622void TransferReadOp::getEffects(
5623 SmallVectorImpl<SideEffects::EffectInstance<MemoryEffects::Effect>>
5625 if (llvm::isa<MemRefType>(getShapedType()))
5626 effects.emplace_back(MemoryEffects::Read::get(), &getBaseMutable(),
5627 SideEffects::DefaultResource::get());
5631 if (hasPureTensorSemantics())
5638static AffineMap inverseWithUnusedDims(AffineMap map) {
5640 "expected a projected permutation map");
5645 int64_t pos = cast<AffineDimExpr>(
result).getPosition();
5675struct TransferReadAfterWriteToBroadcast
5676 :
public OpRewritePattern<TransferReadOp> {
5679 LogicalResult matchAndRewrite(TransferReadOp readOp,
5680 PatternRewriter &rewriter)
const override {
5681 auto defWrite = readOp.getBase().getDefiningOp<vector::TransferWriteOp>();
5685 if (!readOp.hasPureTensorSemantics() || !defWrite.hasPureTensorSemantics())
5691 if (readOp.getMask() || defWrite.getMask() || readOp.isMasked())
5694 if (readOp.getIndices() != defWrite.getIndices())
5697 if (readOp.hasOutOfBoundsDim() || defWrite.hasOutOfBoundsDim())
5701 if (readOp.getTransferChunkAccessed() !=
5702 defWrite.getTransferChunkAccessed())
5709 AffineMap readMap = readOp.getPermutationMap();
5710 AffineMap writeMap = defWrite.getPermutationMap();
5711 AffineMap invWriteMap = inverseWithUnusedDims(writeMap);
5712 AffineMap composedMap = readMap.
compose(invWriteMap);
5726 int64_t numBroadcastedDims = broadcastedDims.size();
5727 auto invPerm = llvm::to_vector_of<int64_t>(broadcastedDims);
5729 for (
auto [idx, expr] : llvm::enumerate(composedMap.
getResults())) {
5730 if (
auto dim = dyn_cast<AffineDimExpr>(expr)) {
5731 int64_t effectiveDim = dim.getPosition() + numBroadcastedDims;
5732 invPerm[effectiveDim] = idx;
5737 VectorType readVecTy = readOp.getVectorType();
5739 auto broadcastedVecTy =
5741 readVecTy.getElementType(),
5744 Value vec = defWrite.getVector();
5745 Location loc = readOp.getLoc();
5746 vec = vector::BroadcastOp::create(rewriter, loc, broadcastedVecTy, vec);
5753void TransferReadOp::getCanonicalizationPatterns(RewritePatternSet &results,
5754 MLIRContext *context) {
5755 results.
add<TransferReadAfterWriteToBroadcast>(context);
5758FailureOr<std::optional<SmallVector<Value>>>
5759TransferReadOp::bubbleDownCasts(OpBuilder &builder) {
5760 if (!hasPureBufferSemantics())
5771void TransferWriteOp::build(OpBuilder &builder, OperationState &
result,
5773 AffineMapAttr permutationMapAttr,
5776 Type resultType = llvm::dyn_cast<RankedTensorType>(dest.
getType());
5777 build(builder,
result, resultType, vector, dest,
indices, permutationMapAttr,
5778 mask, inBoundsAttr);
5782void TransferWriteOp::build(OpBuilder &builder, OperationState &
result,
5784 AffineMapAttr permutationMapAttr,
5786 build(builder,
result, vector, dest,
indices, permutationMapAttr,
5787 Value(), inBoundsAttr);
5792void TransferWriteOp::build(OpBuilder &builder, OperationState &
result,
5794 AffineMap permutationMap,
5795 std::optional<ArrayRef<bool>> inBounds) {
5796 if (!permutationMap)
5799 llvm::cast<VectorType>(vector.
getType()));
5800 auto permutationMapAttr = AffineMapAttr::get(permutationMap);
5802 (inBounds && !inBounds.value().empty())
5805 llvm::cast<VectorType>(vector.
getType()).getRank(),
false));
5806 build(builder,
result, vector, dest,
indices, permutationMapAttr,
5807 Value(), inBoundsAttr);
5812void TransferWriteOp::build(OpBuilder &builder, OperationState &
result,
5814 std::optional<ArrayRef<bool>> inBounds) {
5819ParseResult TransferWriteOp::parse(OpAsmParser &parser,
5820 OperationState &
result) {
5823 OpAsmParser::UnresolvedOperand vectorInfo, sourceInfo;
5824 SmallVector<OpAsmParser::UnresolvedOperand, 8> indexInfo;
5825 SmallVector<Type, 2> types;
5826 OpAsmParser::UnresolvedOperand maskInfo;
5832 if (hasMask.succeeded() && parser.
parseOperand(maskInfo))
5837 if (types.size() != 2)
5838 return parser.
emitError(typesLoc,
"requires two types");
5840 VectorType vectorType = llvm::dyn_cast<VectorType>(types[0]);
5842 return parser.
emitError(typesLoc,
"requires vector type");
5843 ShapedType shapedType = llvm::dyn_cast<ShapedType>(types[1]);
5844 if (!shapedType || !llvm::isa<MemRefType, RankedTensorType>(shapedType))
5845 return parser.
emitError(typesLoc,
"requires memref or ranked tensor type");
5846 auto permMapAttrName =
5847 TransferWriteOp::getPermutationMapAttrName(
result.name);
5848 auto permMapAttr =
result.attributes.get(permMapAttrName);
5851 if (shapedType.getRank() <
5854 "expected a custom permutation_map when "
5855 "rank(source) != rank(destination)");
5857 result.attributes.set(permMapAttrName, AffineMapAttr::get(permMap));
5859 permMap = llvm::cast<AffineMapAttr>(permMapAttr).getValue();
5861 auto inBoundsAttrName = TransferWriteOp::getInBoundsAttrName(
result.name);
5862 Attribute inBoundsAttr =
result.attributes.get(inBoundsAttrName);
5863 if (!inBoundsAttr) {
5864 result.addAttribute(inBoundsAttrName,
5872 if (hasMask.succeeded()) {
5873 if (llvm::dyn_cast<VectorType>(shapedType.getElementType()))
5875 maskInfo.
location,
"does not support masks with vector element type");
5878 "expected the same rank for the vector and the "
5879 "results of the permutation map");
5885 result.addAttribute(TransferWriteOp::getOperandSegmentSizeAttr(),
5887 {1, 1, static_cast<int32_t>(indexInfo.size()),
5888 static_cast<int32_t>(hasMask.succeeded())}));
5889 return failure(llvm::isa<RankedTensorType>(shapedType) &&
5893void TransferWriteOp::print(OpAsmPrinter &p) {
5896 p <<
", " << getMask();
5901LogicalResult TransferWriteOp::verify() {
5903 ShapedType shapedType = getShapedType();
5905 VectorType maskType = getMaskType();
5906 auto permutationMap = getPermutationMap();
5907 VectorType inferredMaskType =
5911 if (llvm::size(
getIndices()) != shapedType.getRank())
5912 return emitOpError(
"requires ") << shapedType.getRank() <<
" indices";
5916 if (hasBroadcastDim())
5917 return emitOpError(
"should not have broadcast dimensions");
5920 shapedType, vectorType, maskType,
5921 inferredMaskType, permutationMap, getInBounds())))
5925 [&](Twine t) {
return emitOpError(t); });
5934Type TransferWriteOp::getExpectedMaskType() {
5941Value TransferWriteOp::getVector() {
return getOperand(0); }
5942VectorType TransferWriteOp::getVectorType() {
5943 return cast<VectorType>(getValueToStore().
getType());
5966static LogicalResult foldReadInitWrite(TransferWriteOp write,
5967 ArrayRef<Attribute>,
5968 SmallVectorImpl<OpFoldResult> &results) {
5970 if (write.getTransferRank() == 0)
5972 auto rankedTensorType =
5973 llvm::dyn_cast<RankedTensorType>(write.getBase().getType());
5975 if (!rankedTensorType)
5978 auto read = write.getVector().getDefiningOp<vector::TransferReadOp>();
5982 if (read.getTransferRank() == 0)
5985 if (!read.getPermutationMap().isMinorIdentity() ||
5986 !write.getPermutationMap().isMinorIdentity())
5989 if (read.getTransferRank() != write.getTransferRank())
5992 if (read.hasOutOfBoundsDim() || write.hasOutOfBoundsDim())
5995 if (read.getMask() || write.getMask())
5998 if (read.getBase().getType() != rankedTensorType)
6001 if (read.getVectorType() != write.getVectorType())
6004 if (read.getVectorType().getShape() != rankedTensorType.getShape())
6007 auto isNotConstantZero = [](Value v) {
6009 return !cstOp.has_value() || cstOp.value() != 0;
6011 if (llvm::any_of(read.getIndices(), isNotConstantZero) ||
6012 llvm::any_of(write.getIndices(), isNotConstantZero))
6015 results.push_back(read.getBase());
6019static bool checkSameValueWAR(vector::TransferReadOp read,
6020 vector::TransferWriteOp write) {
6021 return read.getBase() == write.getBase() &&
6022 read.getIndices() == write.getIndices() &&
6023 read.getPermutationMap() == write.getPermutationMap() &&
6024 read.getVectorType() == write.getVectorType() && !read.getMask() &&
6041static LogicalResult foldWAR(TransferWriteOp write,
6042 SmallVectorImpl<OpFoldResult> &results) {
6043 if (!llvm::isa<RankedTensorType>(write.getBase().getType()))
6045 auto read = write.getVector().getDefiningOp<vector::TransferReadOp>();
6049 if (!checkSameValueWAR(read, write))
6051 results.push_back(read.getBase());
6055LogicalResult TransferWriteOp::fold(FoldAdaptor adaptor,
6056 SmallVectorImpl<OpFoldResult> &results) {
6057 if (succeeded(foldReadInitWrite(*
this, adaptor.getOperands(), results)))
6059 if (succeeded(foldWAR(*
this, results)))
6073std::optional<SmallVector<int64_t, 4>>
6074TransferWriteOp::getIterationShapeToUnroll() {
6078void TransferWriteOp::getEffects(
6079 SmallVectorImpl<SideEffects::EffectInstance<MemoryEffects::Effect>>
6081 if (llvm::isa<MemRefType>(getShapedType()))
6082 effects.emplace_back(MemoryEffects::Write::get(), &getBaseMutable(),
6083 SideEffects::DefaultResource::get());
6087 if (hasPureTensorSemantics())
6117class FoldWaw final :
public OpRewritePattern<TransferWriteOp> {
6120 LogicalResult matchAndRewrite(TransferWriteOp writeOp,
6121 PatternRewriter &rewriter)
const override {
6122 if (!llvm::isa<RankedTensorType>(writeOp.getShapedType()))
6124 vector::TransferWriteOp writeToModify = writeOp;
6126 auto defWrite = writeOp.getBase().getDefiningOp<vector::TransferWriteOp>();
6130 writeToModify.getBaseMutable().assign(defWrite.getBase());
6135 cast<VectorTransferOpInterface>(defWrite.getOperation()),
6136 cast<VectorTransferOpInterface>(writeOp.getOperation())))
6140 if (!defWrite->hasOneUse())
6142 writeToModify = defWrite;
6143 defWrite = defWrite.getBase().getDefiningOp<vector::TransferWriteOp>();
6172struct SwapExtractSliceOfTransferWrite
6173 :
public OpRewritePattern<tensor::InsertSliceOp> {
6177 LogicalResult matchAndRewrite(tensor::InsertSliceOp insertOp,
6178 PatternRewriter &rewriter)
const override {
6179 if (!insertOp.hasUnitStride())
6182 insertOp.getSource().getDefiningOp<tensor::ExtractSliceOp>();
6183 if (!extractOp || !extractOp.hasUnitStride() || !extractOp->hasOneUse())
6185 auto transferOp = extractOp.getSource().getDefiningOp<TransferWriteOp>();
6186 if (!transferOp || !transferOp->hasOneUse())
6191 if (insertOp.getSourceType().getRank() != transferOp.getTransferRank()) {
6193 "use-def chain is rank-reducing");
6197 if (!extractOp.hasZeroOffset()) {
6199 "ExtractSliceOp has non-zero offset");
6203 if (!llvm::all_of(transferOp.getIndices(), [](Value value) {
6204 return getConstantIntValue(value) == static_cast<int64_t>(0);
6207 "TranferWriteOp has non-zero offset");
6211 if (insertOp.getMixedSizes().size() != extractOp.getMixedSizes().size()) {
6213 insertOp,
"InsertSliceOp and ExtractSliceOp ranks differ");
6216 for (
auto [insertSize, extractSize] :
6217 llvm::zip_equal(insertOp.getMixedSizes(), extractOp.getMixedSizes())) {
6220 insertOp,
"InsertSliceOp and ExtractSliceOp sizes differ");
6225 assert(transferOp.getVectorType().hasStaticShape() &&
6226 "expected vector to have a static shape");
6227 ArrayRef<int64_t>
vectorShape = transferOp.getVectorType().getShape();
6229 transferOp.getPermutationMap(), transferOp.getShapedType().getShape());
6230 if (transferOp.getMask() || !
vectorShape.equals(resultShape)) {
6232 insertOp,
"TransferWriteOp may not write the full tensor.");
6237 SmallVector<bool> newInBounds(
vectorShape.size(),
false);
6238 auto newExtractOp = tensor::ExtractSliceOp::create(
6239 rewriter, extractOp.getLoc(), insertOp.getSourceType(),
6240 insertOp.getDest(), insertOp.getMixedOffsets(),
6241 insertOp.getMixedSizes(), insertOp.getMixedStrides());
6242 auto newTransferWriteOp = TransferWriteOp::create(
6243 rewriter, transferOp.getLoc(), transferOp.getVector(),
6244 newExtractOp.getResult(), transferOp.getIndices(),
6245 transferOp.getPermutationMapAttr(),
6248 insertOp.getSourceMutable().assign(newTransferWriteOp.getResult());
6256void TransferWriteOp::getCanonicalizationPatterns(RewritePatternSet &results,
6257 MLIRContext *context) {
6258 results.
add<FoldWaw, SwapExtractSliceOfTransferWrite>(context);
6261FailureOr<std::optional<SmallVector<Value>>>
6262TransferWriteOp::bubbleDownCasts(OpBuilder &builder) {
6263 if (!hasPureBufferSemantics())
6277 result = dyn_cast<BoolAttr>(attr);
6280 "expected boolean attribute");
6284static void printBoolAttr(OpAsmPrinter &printer, Operation *, BoolAttr attr) {
6288static LogicalResult verifyLoadStoreMemRefLayout(Operation *op,
6290 MemRefType memRefTy) {
6293 if (!vecTy.isScalable() &&
6294 (vecTy.getRank() == 0 || vecTy.getNumElements() == 1))
6297 if (!memRefTy.isLastDimUnitStride())
6298 return op->
emitOpError(
"most minor memref dim must have unit stride");
6302LogicalResult vector::LoadOp::verify() {
6306 if (
failed(verifyLoadStoreMemRefLayout(*
this, resVecTy, memRefTy)))
6313 return emitOpError(
"memref strides must be non-negative");
6315 if (memRefTy.getRank() < resVecTy.getRank())
6317 "destination memref has lower rank than the result vector");
6320 Type memElemTy = memRefTy.getElementType();
6321 if (
auto memVecTy = llvm::dyn_cast<VectorType>(memElemTy)) {
6322 if (memVecTy != resVecTy)
6323 return emitOpError(
"base memref and result vector types should match");
6324 memElemTy = memVecTy.getElementType();
6327 if (resVecTy.getElementType() != memElemTy)
6328 return emitOpError(
"base and result element types should match");
6329 if (llvm::size(
getIndices()) != memRefTy.getRank())
6330 return emitOpError(
"requires ") << memRefTy.getRank() <<
" indices";
6334OpFoldResult LoadOp::fold(FoldAdaptor) {
6337 return OpFoldResult();
6340std::optional<SmallVector<int64_t, 4>> LoadOp::getIterationShapeToUnroll() {
6344FailureOr<std::optional<SmallVector<Value>>>
6345LoadOp::bubbleDownCasts(OpBuilder &builder) {
6354LogicalResult vector::StoreOp::verify() {
6358 if (
failed(verifyLoadStoreMemRefLayout(*
this, valueVecTy, memRefTy)))
6365 return emitOpError(
"memref strides must be non-negative");
6367 if (memRefTy.getRank() < valueVecTy.getRank())
6368 return emitOpError(
"source memref has lower rank than the vector to store");
6371 Type memElemTy = memRefTy.getElementType();
6372 if (
auto memVecTy = llvm::dyn_cast<VectorType>(memElemTy)) {
6373 if (memVecTy != valueVecTy)
6375 "base memref and valueToStore vector types should match");
6376 memElemTy = memVecTy.getElementType();
6379 if (valueVecTy.getElementType() != memElemTy)
6380 return emitOpError(
"base and valueToStore element type should match");
6381 if (llvm::size(
getIndices()) != memRefTy.getRank())
6382 return emitOpError(
"requires ") << memRefTy.getRank() <<
" indices";
6386LogicalResult StoreOp::fold(FoldAdaptor adaptor,
6387 SmallVectorImpl<OpFoldResult> &results) {
6391std::optional<SmallVector<int64_t, 4>> StoreOp::getIterationShapeToUnroll() {
6395FailureOr<std::optional<SmallVector<Value>>>
6396StoreOp::bubbleDownCasts(OpBuilder &builder) {
6405LogicalResult MaskedLoadOp::verify() {
6406 VectorType maskVType = getMaskVectorType();
6407 VectorType passVType = getPassThruVectorType();
6411 if (
failed(verifyLoadStoreMemRefLayout(*
this, resVType, memType)))
6418 return emitOpError(
"memref strides must be non-negative");
6423 if (llvm::size(
getIndices()) != memType.getRank())
6424 return emitOpError(
"requires ") << memType.getRank() <<
" indices";
6425 if (resVType.getShape() != maskVType.getShape())
6426 return emitOpError(
"expected result shape to match mask shape");
6427 if (resVType != passVType)
6428 return emitOpError(
"expected pass_thru of same type as result type");
6433class MaskedLoadFolder final :
public OpRewritePattern<MaskedLoadOp> {
6436 LogicalResult matchAndRewrite(MaskedLoadOp
load,
6437 PatternRewriter &rewriter)
const override {
6449 llvm_unreachable(
"Unexpected 1DMaskFormat on MaskedLoad");
6454void MaskedLoadOp::getCanonicalizationPatterns(RewritePatternSet &results,
6455 MLIRContext *context) {
6456 results.
add<MaskedLoadFolder>(context);
6459OpFoldResult MaskedLoadOp::fold(FoldAdaptor) {
6462 return OpFoldResult();
6465FailureOr<std::optional<SmallVector<Value>>>
6466MaskedLoadOp::bubbleDownCasts(OpBuilder &builder) {
6475LogicalResult MaskedStoreOp::verify() {
6476 VectorType maskVType = getMaskVectorType();
6480 if (
failed(verifyLoadStoreMemRefLayout(*
this, valueVType, memType)))
6487 return emitOpError(
"memref strides must be non-negative");
6492 if (llvm::size(
getIndices()) != memType.getRank())
6493 return emitOpError(
"requires ") << memType.getRank() <<
" indices";
6494 if (valueVType.getShape() != maskVType.getShape())
6495 return emitOpError(
"expected valueToStore shape to match mask shape");
6500class MaskedStoreFolder final :
public OpRewritePattern<MaskedStoreOp> {
6503 LogicalResult matchAndRewrite(MaskedStoreOp store,
6504 PatternRewriter &rewriter)
const override {
6508 store, store.getValueToStore(), store.getBase(), store.getIndices());
6516 llvm_unreachable(
"Unexpected 1DMaskFormat on MaskedStore");
6521void MaskedStoreOp::getCanonicalizationPatterns(RewritePatternSet &results,
6522 MLIRContext *context) {
6523 results.
add<MaskedStoreFolder>(context);
6526LogicalResult MaskedStoreOp::fold(FoldAdaptor adaptor,
6527 SmallVectorImpl<OpFoldResult> &results) {
6531FailureOr<std::optional<SmallVector<Value>>>
6532MaskedStoreOp::bubbleDownCasts(OpBuilder &builder) {
6541LogicalResult GatherOp::verify() {
6542 VectorType indVType = getIndexVectorType();
6543 VectorType maskVType = getMaskVectorType();
6545 ShapedType baseType = getBaseType();
6547 if (!llvm::isa<MemRefType, RankedTensorType>(baseType))
6548 return emitOpError(
"requires base to be a memref or ranked tensor type");
6553 if (
auto memRefType = dyn_cast<MemRefType>(baseType))
6555 return emitOpError(
"memref strides must be non-negative");
6560 if (llvm::size(getOffsets()) != baseType.getRank())
6561 return emitOpError(
"requires ") << baseType.getRank() <<
" indices";
6562 if (resVType.getShape() != indVType.getShape())
6563 return emitOpError(
"expected result dim to match indices dim");
6564 if (resVType.getShape() != maskVType.getShape())
6565 return emitOpError(
"expected result dim to match mask dim");
6566 if (resVType != getPassThruVectorType())
6567 return emitOpError(
"expected pass_thru of same type as result type");
6568 if (getAlignmentAttr() && !isa<MemRefType>(baseType)) {
6570 "alignment is only supported for memref bases, not tensor bases");
6579Type GatherOp::getExpectedMaskType() {
6580 auto vecType = this->getIndexVectorType();
6581 return VectorType::get(vecType.getShape(),
6582 IntegerType::get(vecType.getContext(), 1),
6583 vecType.getScalableDims());
6586std::optional<SmallVector<int64_t, 4>> GatherOp::getIterationShapeToUnroll() {
6591static LogicalResult isZeroBasedContiguousSeq(Value indexVec) {
6592 auto vecType = dyn_cast<VectorType>(indexVec.
getType());
6593 if (!vecType || vecType.getRank() != 1 || vecType.isScalable())
6599 DenseIntElementsAttr elements;
6604 llvm::equal(elements, llvm::seq<int64_t>(0, vecType.getNumElements())));
6608class GatherFolder final :
public OpRewritePattern<GatherOp> {
6611 LogicalResult matchAndRewrite(GatherOp gather,
6612 PatternRewriter &rewriter)
const override {
6617 rewriter.
replaceOp(gather, gather.getPassThru());
6622 llvm_unreachable(
"Unexpected 1DMaskFormat on GatherFolder");
6628class FoldContiguousGather final :
public OpRewritePattern<GatherOp> {
6631 LogicalResult matchAndRewrite(GatherOp op,
6632 PatternRewriter &rewriter)
const override {
6633 if (!isa<MemRefType>(op.getBase().getType()))
6636 if (
failed(isZeroBasedContiguousSeq(op.getIndices())))
6640 op.getOffsets(), op.getMask(),
6647void GatherOp::getCanonicalizationPatterns(RewritePatternSet &results,
6648 MLIRContext *context) {
6649 results.
add<GatherFolder, FoldContiguousGather>(context);
6652FailureOr<std::optional<SmallVector<Value>>>
6653GatherOp::bubbleDownCasts(OpBuilder &builder) {
6662LogicalResult ScatterOp::verify() {
6663 VectorType indVType = getIndexVectorType();
6664 VectorType maskVType = getMaskVectorType();
6666 ShapedType baseType = getBaseType();
6668 if (!llvm::isa<MemRefType, RankedTensorType>(baseType))
6669 return emitOpError(
"requires base to be a memref or ranked tensor type");
6674 if (
auto memRefType = dyn_cast<MemRefType>(baseType))
6676 return emitOpError(
"memref strides must be non-negative");
6681 if (llvm::size(getOffsets()) != baseType.getRank())
6682 return emitOpError(
"requires ") << baseType.getRank() <<
" indices";
6683 if (valueVType.getShape() != indVType.getShape())
6684 return emitOpError(
"expected valueToStore dim to match indices dim");
6685 if (valueVType.getShape() != maskVType.getShape())
6686 return emitOpError(
"expected valueToStore dim to match mask dim");
6687 if (getAlignmentAttr() && !isa<MemRefType>(baseType)) {
6689 "alignment is only supported for memref bases, not tensor bases");
6694class ScatterFolder final :
public OpRewritePattern<ScatterOp> {
6697 LogicalResult matchAndRewrite(ScatterOp scatter,
6698 PatternRewriter &rewriter)
const override {
6699 ShapedType baseType = scatter.getBaseType();
6700 bool isMemRef = isa<MemRefType>(baseType);
6701 if (!isMemRef && !isa<RankedTensorType>(baseType))
6714 rewriter.
replaceOp(scatter, scatter.getBase());
6719 llvm_unreachable(
"Unexpected 1DMaskFormat on ScatterFolder");
6725class FoldContiguousScatter final :
public OpRewritePattern<ScatterOp> {
6728 LogicalResult matchAndRewrite(ScatterOp op,
6729 PatternRewriter &rewriter)
const override {
6732 if (!isa<MemRefType>(op.getBase().getType()))
6735 if (
failed(isZeroBasedContiguousSeq(op.getIndices())))
6739 op, op.getBase(), op.getOffsets(), op.getMask(), op.getValueToStore());
6745void ScatterOp::getCanonicalizationPatterns(RewritePatternSet &results,
6746 MLIRContext *context) {
6747 results.
add<ScatterFolder, FoldContiguousScatter>(context);
6750FailureOr<std::optional<SmallVector<Value>>>
6751ScatterOp::bubbleDownCasts(OpBuilder &builder) {
6760LogicalResult ExpandLoadOp::verify() {
6761 VectorType maskVType = getMaskVectorType();
6762 VectorType passVType = getPassThruVectorType();
6766 if (
failed(verifyLoadStoreMemRefLayout(*
this, resVType, memType)))
6773 return emitOpError(
"memref strides must be non-negative");
6778 if (llvm::size(
getIndices()) != memType.getRank())
6779 return emitOpError(
"requires ") << memType.getRank() <<
" indices";
6780 if (resVType.getShape() != maskVType.getShape())
6781 return emitOpError(
"expected result shape to match mask shape");
6782 if (resVType.getScalableDims() != maskVType.getScalableDims())
6784 "expected result scalable dims to match mask scalable dims");
6785 if (resVType != passVType)
6786 return emitOpError(
"expected pass_thru of same type as result type");
6791class ExpandLoadFolder final :
public OpRewritePattern<ExpandLoadOp> {
6794 LogicalResult matchAndRewrite(ExpandLoadOp expand,
6795 PatternRewriter &rewriter)
const override {
6799 expand, expand.getType(), expand.getBase(), expand.getIndices());
6802 rewriter.
replaceOp(expand, expand.getPassThru());
6807 llvm_unreachable(
"Unexpected 1DMaskFormat on ExpandLoadFolder");
6812void ExpandLoadOp::getCanonicalizationPatterns(RewritePatternSet &results,
6813 MLIRContext *context) {
6814 results.
add<ExpandLoadFolder>(context);
6817FailureOr<std::optional<SmallVector<Value>>>
6818ExpandLoadOp::bubbleDownCasts(OpBuilder &builder) {
6827LogicalResult CompressStoreOp::verify() {
6828 VectorType maskVType = getMaskVectorType();
6832 if (
failed(verifyLoadStoreMemRefLayout(*
this, valueVType, memType)))
6839 return emitOpError(
"memref strides must be non-negative");
6844 if (llvm::size(
getIndices()) != memType.getRank())
6845 return emitOpError(
"requires ") << memType.getRank() <<
" indices";
6846 if (valueVType.getShape() != maskVType.getShape())
6847 return emitOpError(
"expected valueToStore shape to match mask shape");
6848 if (valueVType.getScalableDims() != maskVType.getScalableDims())
6850 "expected valueToStore scalable dims to match mask scalable dims");
6855class CompressStoreFolder final :
public OpRewritePattern<CompressStoreOp> {
6858 LogicalResult matchAndRewrite(CompressStoreOp compress,
6859 PatternRewriter &rewriter)
const override {
6863 compress, compress.getValueToStore(), compress.getBase(),
6864 compress.getIndices());
6872 llvm_unreachable(
"Unexpected 1DMaskFormat on CompressStoreFolder");
6877void CompressStoreOp::getCanonicalizationPatterns(RewritePatternSet &results,
6878 MLIRContext *context) {
6879 results.
add<CompressStoreFolder>(context);
6882FailureOr<std::optional<SmallVector<Value>>>
6883CompressStoreOp::bubbleDownCasts(OpBuilder &builder) {
6892void ShapeCastOp::inferResultRanges(ArrayRef<ConstantIntRanges> argRanges,
6894 setResultRanges(getResult(), argRanges.front());
6897std::optional<SmallVector<int64_t, 4>>
6898ShapeCastOp::getIterationShapeToUnroll() {
6899 return llvm::to_vector<4>(getResultVectorType().
getShape());
6902LogicalResult ShapeCastOp::verify() {
6904 VectorType sourceType = getSourceVectorType();
6905 VectorType resultType = getResultVectorType();
6913 int64_t sourceNElms = sourceType.getNumElements();
6914 int64_t resultNElms = resultType.getNumElements();
6915 if (sourceNElms != resultNElms) {
6916 return emitOpError() <<
"has different number of elements at source ("
6917 << sourceNElms <<
") and result (" << resultNElms
6922 int64_t sourceNScalableDims = sourceType.getNumScalableDims();
6923 int64_t resultNScalableDims = resultType.getNumScalableDims();
6924 if (sourceNScalableDims != resultNScalableDims)
6925 return emitOpError() <<
"has different number of scalable dims at source ("
6926 << sourceNScalableDims <<
") and result ("
6927 << resultNScalableDims <<
")";
6938bool ShapeCastOp::isBroadcastLike() {
6939 auto srcType = getSourceVectorType();
6940 auto resType = getResultVectorType();
6943 std::pair<VectorDim, VectorDim> mismatchingDims;
6945 BroadcastableToResult::Success)
6952 size_t rankDiff = resType.getRank() - srcType.getRank();
6957 if (!llvm::all_of(resType.getShape().take_front(rankDiff),
6958 [](int64_t dim) { return dim == 1; }))
6962 return resType.getShape().take_back(srcType.getRank()) == srcType.getShape();
6969static bool isOrderPreserving(TransposeOp transpose) {
6970 ArrayRef<int64_t> permutation = transpose.getPermutation();
6971 VectorType sourceType = transpose.getSourceVectorType();
6972 ArrayRef<int64_t> inShape = sourceType.getShape();
6973 ArrayRef<bool> inDimIsScalable = sourceType.getScalableDims();
6974 auto isNonScalableUnitDim = [&](int64_t dim) {
6975 return inShape[dim] == 1 && !inDimIsScalable[dim];
6977 int64_t current = 0;
6978 for (
auto p : permutation) {
6979 if (!isNonScalableUnitDim(p)) {
6989OpFoldResult ShapeCastOp::fold(FoldAdaptor adaptor) {
6991 VectorType resultType =
getType();
6994 if (getSource().
getType() == resultType)
6998 if (
auto precedingShapeCast = getSource().getDefiningOp<ShapeCastOp>()) {
6999 setOperand(precedingShapeCast.getSource());
7004 if (
auto transpose = getSource().getDefiningOp<TransposeOp>()) {
7005 if (isOrderPreserving(transpose)) {
7006 setOperand(transpose.getVector());
7014 if (
auto bcastOp = getSource().getDefiningOp<BroadcastOp>()) {
7015 if (bcastOp.getSourceType() == resultType)
7016 return bcastOp.getSource();
7020 if (
auto denseAttr =
7021 dyn_cast_if_present<DenseElementsAttr>(adaptor.getSource()))
7022 return denseAttr.reshape(
getType());
7038static VectorType trimTrailingUnitDims(VectorType oldType) {
7039 ArrayRef<int64_t> oldShape = oldType.getShape();
7040 ArrayRef<int64_t> newShape = oldShape;
7042 ArrayRef<bool> oldScalableDims = oldType.getScalableDims();
7043 ArrayRef<bool> newScalableDims = oldScalableDims;
7045 while (!newShape.empty() && newShape.back() == 1 && !newScalableDims.back()) {
7046 newShape = newShape.drop_back(1);
7047 newScalableDims = newScalableDims.drop_back(1);
7052 if (newShape.empty()) {
7053 newShape = oldShape.take_back();
7054 newScalableDims = oldScalableDims.take_back();
7057 return VectorType::get(newShape, oldType.getElementType(), newScalableDims);
7066 ArrayRef<int64_t> oldShape = oldType.getShape();
7067 ArrayRef<int64_t> newShape = oldShape;
7069 ArrayRef<bool> oldScalableDims = oldType.getScalableDims();
7070 ArrayRef<bool> newScalableDims = oldScalableDims;
7072 while (!newShape.empty() && newShape.front() == 1 &&
7073 !newScalableDims.front()) {
7074 newShape = newShape.drop_front(1);
7075 newScalableDims = newScalableDims.drop_front(1);
7080 if (newShape.empty()) {
7081 newShape = oldShape.take_back();
7082 newScalableDims = oldScalableDims.take_back();
7085 return VectorType::get(newShape, oldType.getElementType(), newScalableDims);
7088enum class UnitDimSide { Leading, Trailing };
7111template <UnitDimS
ide S
ide>
7112class ShapeCastCreateMaskFolderBoundaryUnitDim final
7113 :
public OpRewritePattern<ShapeCastOp> {
7117 LogicalResult matchAndRewrite(ShapeCastOp shapeOp,
7118 PatternRewriter &rewriter)
const override {
7119 Value shapeOpSrc = shapeOp->getOperand(0);
7120 auto createMaskOp = shapeOpSrc.
getDefiningOp<vector::CreateMaskOp>();
7121 auto constantMaskOp = shapeOpSrc.
getDefiningOp<vector::ConstantMaskOp>();
7122 if (!createMaskOp && !constantMaskOp)
7125 VectorType shapeOpResTy = shapeOp.getResultVectorType();
7126 VectorType shapeOpSrcTy = shapeOp.getSourceVectorType();
7128 VectorType newVecType = (Side == UnitDimSide::Trailing)
7129 ? trimTrailingUnitDims(shapeOpSrcTy)
7132 if (newVecType != shapeOpResTy) {
7133 return (Side == UnitDimSide::Trailing)
7135 shapeOp,
"Non-trailing-unit-dim dropping shape_cast Op")
7137 shapeOp,
"Non-leading-unit-dim dropping shape_cast Op");
7140 auto numDimsToDrop = shapeOpSrcTy.getRank() - shapeOpResTy.getRank();
7143 if (!numDimsToDrop) {
7144 return (Side == UnitDimSide::Trailing)
7146 shapeOp,
"Non-trailing-unit-dim dropping shape_cast Op")
7148 shapeOp,
"Non-leading-unit-dim dropping shape_cast Op");
7152 auto maskOperands = createMaskOp.getOperands();
7154 auto maskOperandsToDrop = (Side == UnitDimSide::Trailing)
7155 ? maskOperands.take_back(numDimsToDrop)
7156 : maskOperands.take_front(numDimsToDrop);
7160 if (llvm::any_of(maskOperandsToDrop, [](Value maskDim) {
7162 return !cst || (cst.value() != 1);
7166 auto newMaskOperands = (Side == UnitDimSide::Trailing)
7167 ? maskOperands.drop_back(numDimsToDrop)
7168 : maskOperands.drop_front(numDimsToDrop);
7174 if (constantMaskOp) {
7175 auto maskDimSizes = constantMaskOp.getMaskDimSizes();
7177 ArrayRef<int64_t> maskDimSizesToDrop =
7178 (Side == UnitDimSide::Trailing)
7179 ? maskDimSizes.take_back(numDimsToDrop)
7180 : maskDimSizes.take_front(numDimsToDrop);
7184 if (llvm::any_of(maskDimSizesToDrop,
7185 [](int64_t dim) {
return dim != 1; }))
7188 ArrayRef<int64_t> newMaskDimSizes =
7189 (Side == UnitDimSide::Trailing)
7190 ? maskDimSizes.drop_back(numDimsToDrop)
7191 : maskDimSizes.drop_front(numDimsToDrop);
7204int64_t getBroadcastStretchingFactor(ArrayRef<int64_t> srcShape,
7205 ArrayRef<int64_t> dstShape) {
7206 int stretchingFactor = 1;
7207 int numLeadingDims = dstShape.size() - srcShape.size();
7208 for (
int i = 0, e = srcShape.size(); i < e; i++) {
7209 int64_t dstDim = dstShape[numLeadingDims + i];
7210 if (srcShape[i] == 1 && dstDim != 1) {
7211 stretchingFactor *= dstDim;
7214 return stretchingFactor;
7218class ShapeCastBroadcastFolder final :
public OpRewritePattern<ShapeCastOp> {
7222 LogicalResult matchAndRewrite(ShapeCastOp shapeCastOp,
7223 PatternRewriter &rewriter)
const override {
7225 shapeCastOp.getSource().getDefiningOp<vector::BroadcastOp>();
7229 auto srcVectorType = dyn_cast<VectorType>(broadcastOp.getSourceType());
7230 bool srcIsScalar = !srcVectorType;
7238 VectorType dstVectorType = shapeCastOp.getResultVectorType();
7239 ArrayRef<int64_t> dstShape = dstVectorType.getShape();
7240 ArrayRef<int64_t> srcShape =
7241 srcIsScalar ? ArrayRef<int64_t>{} : srcVectorType.getShape();
7242 ArrayRef<int64_t> broadcastShape =
7243 broadcastOp.getResultVectorType().getShape();
7247 BroadcastableToResult::Success) {
7255 if (srcVectorType.getNumElements() != 1) {
7256 if (getBroadcastStretchingFactor(srcShape, dstShape) !=
7257 getBroadcastStretchingFactor(srcShape, broadcastShape)) {
7264 broadcastOp.getSource());
7283class FoldShapeCastOfFromElements final :
public OpRewritePattern<ShapeCastOp> {
7287 LogicalResult matchAndRewrite(ShapeCastOp shapeCastOp,
7288 PatternRewriter &rewriter)
const override {
7289 auto fromElements = shapeCastOp.getSource().getDefiningOp<FromElementsOp>();
7294 shapeCastOp, shapeCastOp.getResultVectorType(),
7295 fromElements.getElements());
7302void ShapeCastOp::getCanonicalizationPatterns(RewritePatternSet &results,
7303 MLIRContext *context) {
7304 results.
add<ShapeCastCreateMaskFolderBoundaryUnitDim<UnitDimSide::Leading>,
7305 ShapeCastCreateMaskFolderBoundaryUnitDim<UnitDimSide::Trailing>,
7306 ShapeCastBroadcastFolder, FoldShapeCastOfFromElements>(context);
7313LogicalResult BitCastOp::verify() {
7314 auto sourceVectorType = getSourceVectorType();
7315 auto resultVectorType = getResultVectorType();
7317 for (int64_t i = 0, e = sourceVectorType.getRank() - 1; i < e; i++) {
7318 if (sourceVectorType.getDimSize(i) != resultVectorType.getDimSize(i))
7319 return emitOpError(
"dimension size mismatch at: ") << i;
7322 DataLayout dataLayout = DataLayout::closest(*
this);
7323 auto sourceElementBits =
7325 auto resultElementBits =
7328 if (sourceVectorType.getRank() == 0) {
7329 if (sourceElementBits != resultElementBits)
7330 return emitOpError(
"source/result bitwidth of the 0-D vector element "
7331 "types must be equal");
7332 }
else if (sourceElementBits * sourceVectorType.getShape().back() !=
7333 resultElementBits * resultVectorType.getShape().back()) {
7335 "source/result bitwidth of the minor 1-D vectors must be equal");
7341OpFoldResult BitCastOp::fold(FoldAdaptor adaptor) {
7347 if (
auto otherOp = getSource().getDefiningOp<BitCastOp>()) {
7348 if (getResult().
getType() == otherOp.getSource().getType())
7349 return otherOp.getSource();
7351 setOperand(otherOp.getSource());
7355 Attribute sourceConstant = adaptor.getSource();
7356 if (!sourceConstant)
7359 Type srcElemType = getSourceVectorType().getElementType();
7360 Type dstElemType = getResultVectorType().getElementType();
7362 if (
auto floatPack = llvm::dyn_cast<DenseFPElementsAttr>(sourceConstant)) {
7363 if (floatPack.isSplat()) {
7364 auto splat = floatPack.getSplatValue<FloatAttr>();
7367 if (srcElemType.
isF16() && dstElemType.
isF32()) {
7368 uint32_t bits =
static_cast<uint32_t
>(
7369 splat.getValue().bitcastToAPInt().getZExtValue());
7371 bits = (bits << 16) | (bits & 0xffff);
7372 APInt intBits(32, bits);
7373 APFloat floatBits(llvm::APFloat::IEEEsingle(), intBits);
7379 if (
auto intPack = llvm::dyn_cast<DenseIntElementsAttr>(sourceConstant)) {
7380 if (intPack.isSplat()) {
7381 auto splat = intPack.getSplatValue<IntegerAttr>();
7383 if (llvm::isa<IntegerType>(dstElemType) && srcElemType.
isIntOrFloat()) {
7388 if (dstBitWidth > srcBitWidth && dstBitWidth % srcBitWidth == 0) {
7389 APInt intBits = splat.getValue().zext(dstBitWidth);
7392 for (uint64_t i = 0; i < dstBitWidth / srcBitWidth - 1; i++)
7393 intBits = (intBits << srcBitWidth) | intBits;
7403std::optional<SmallVector<int64_t, 4>> BitCastOp::getIterationShapeToUnroll() {
7404 return llvm::to_vector<4>(getResultVectorType().
getShape());
7411static SmallVector<int64_t, 8> extractShape(MemRefType memRefType) {
7412 auto vectorType = llvm::dyn_cast<VectorType>(memRefType.getElementType());
7413 SmallVector<int64_t, 8> res(memRefType.getShape());
7415 res.append(vectorType.getShape().begin(), vectorType.getShape().end());
7421void TypeCastOp::build(OpBuilder &builder, OperationState &
result,
7423 result.addOperands(source);
7424 MemRefType memRefType = llvm::cast<MemRefType>(source.
getType());
7425 VectorType vectorType =
7426 VectorType::get(extractShape(memRefType),
7428 result.addTypes(MemRefType::get({}, vectorType, MemRefLayoutAttrInterface(),
7429 memRefType.getMemorySpace()));
7432LogicalResult TypeCastOp::verify() {
7433 MemRefType canonicalType =
getMemRefType().canonicalizeStridedLayout();
7434 if (!canonicalType.getLayout().isIdentity())
7435 return emitOpError(
"expects operand to be a memref with identity layout");
7436 if (!getResultMemRefType().getLayout().isIdentity())
7437 return emitOpError(
"expects result to be a memref with identity layout");
7438 if (getResultMemRefType().getMemorySpace() !=
7440 return emitOpError(
"expects result in same memory space");
7443 auto resultType = getResultMemRefType();
7447 "expects result and operand with same underlying scalar type: ")
7449 if (extractShape(sourceType) != extractShape(resultType))
7451 "expects concatenated result and operand shapes to be equal: ")
7460void vector::TransposeOp::build(OpBuilder &builder, OperationState &
result,
7461 Value vector, ArrayRef<int64_t> permutation) {
7462 VectorType vt = llvm::cast<VectorType>(vector.
getType());
7463 SmallVector<int64_t, 4> transposedShape(vt.getRank());
7464 SmallVector<bool, 4> transposedScalableDims(vt.getRank());
7465 for (
unsigned i = 0; i < permutation.size(); ++i) {
7466 transposedShape[i] = vt.getShape()[permutation[i]];
7467 transposedScalableDims[i] = vt.getScalableDims()[permutation[i]];
7470 result.addOperands(vector);
7471 result.addTypes(VectorType::get(transposedShape, vt.getElementType(),
7472 transposedScalableDims));
7473 result.addAttribute(TransposeOp::getPermutationAttrName(
result.name),
7477OpFoldResult vector::TransposeOp::fold(FoldAdaptor adaptor) {
7480 llvm::dyn_cast_if_present<SplatElementsAttr>(adaptor.getVector()))
7481 return splat.reshape(getResultVectorType());
7498 if (getSourceVectorType() == getResultVectorType() &&
7499 isOrderPreserving(*
this))
7505LogicalResult vector::TransposeOp::verify() {
7506 VectorType vectorType = getSourceVectorType();
7507 VectorType resultType = getResultVectorType();
7508 int64_t rank = resultType.getRank();
7509 if (vectorType.getRank() != rank)
7510 return emitOpError(
"vector result rank mismatch: ") << rank;
7512 ArrayRef<int64_t> perm = getPermutation();
7513 int64_t size = perm.size();
7515 return emitOpError(
"transposition length mismatch: ") << size;
7516 SmallVector<bool, 8> seen(rank,
false);
7517 for (
const auto &ta : llvm::enumerate(perm)) {
7518 if (ta.value() < 0 || ta.value() >= rank)
7519 return emitOpError(
"transposition index out of range: ") << ta.value();
7520 if (seen[ta.value()])
7521 return emitOpError(
"duplicate position index: ") << ta.value();
7522 seen[ta.value()] =
true;
7523 if (resultType.getDimSize(ta.index()) != vectorType.getDimSize(ta.value()))
7524 return emitOpError(
"dimension size mismatch at: ") << ta.value();
7529std::optional<SmallVector<int64_t, 4>>
7530TransposeOp::getIterationShapeToUnroll() {
7531 return llvm::to_vector<4>(getResultVectorType().
getShape());
7534void TransposeOp::inferResultRanges(ArrayRef<ConstantIntRanges> argRanges,
7536 setResultRanges(getResult(), argRanges.front());
7542class TransposeFolder final :
public OpRewritePattern<vector::TransposeOp> {
7546 LogicalResult matchAndRewrite(vector::TransposeOp transposeOp,
7547 PatternRewriter &rewriter)
const override {
7549 auto composePermutations = [](ArrayRef<int64_t> permutation1,
7550 ArrayRef<int64_t> permutation2) {
7551 SmallVector<int64_t, 4>
result;
7552 for (
auto index : permutation2)
7553 result.push_back(permutation1[index]);
7558 vector::TransposeOp parentTransposeOp =
7559 transposeOp.getVector().getDefiningOp<vector::TransposeOp>();
7560 if (!parentTransposeOp)
7563 SmallVector<int64_t, 4> permutation = composePermutations(
7564 parentTransposeOp.getPermutation(), transposeOp.getPermutation());
7567 transposeOp, transposeOp.getResult().
getType(),
7568 parentTransposeOp.getVector(), permutation);
7574class FoldTransposeSplat final :
public OpRewritePattern<TransposeOp> {
7578 LogicalResult matchAndRewrite(TransposeOp transposeOp,
7579 PatternRewriter &rewriter)
const override {
7580 Value splat = getScalarSplatSource(transposeOp.getVector());
7585 transposeOp, transposeOp.getResultVectorType(), splat);
7591class FoldTransposeCreateMask final :
public OpRewritePattern<TransposeOp> {
7595 LogicalResult matchAndRewrite(TransposeOp transpOp,
7596 PatternRewriter &rewriter)
const override {
7597 Value transposeSrc = transpOp.getVector();
7598 auto createMaskOp = transposeSrc.
getDefiningOp<vector::CreateMaskOp>();
7599 auto constantMaskOp = transposeSrc.
getDefiningOp<vector::ConstantMaskOp>();
7600 if (!createMaskOp && !constantMaskOp)
7605 ArrayRef<int64_t> permutation = transpOp.getPermutation();
7608 auto maskOperands = createMaskOp.getOperands();
7609 SmallVector<Value> newOperands(maskOperands.begin(), maskOperands.end());
7613 transpOp, transpOp.getResultVectorType(), newOperands);
7618 auto maskDimSizes = constantMaskOp.getMaskDimSizes();
7622 transpOp, transpOp.getResultVectorType(), newMaskDimSizes);
7628class FoldTransposeShapeCast final :
public OpRewritePattern<TransposeOp> {
7632 LogicalResult matchAndRewrite(TransposeOp transposeOp,
7633 PatternRewriter &rewriter)
const override {
7635 transposeOp.getVector().getDefiningOp<vector::ShapeCastOp>();
7638 if (!isOrderPreserving(transposeOp))
7641 VectorType resultType = transposeOp.getType();
7648 shapeCastOp.getSource());
7667class FoldTransposeFromElements final :
public OpRewritePattern<TransposeOp> {
7670 LogicalResult matchAndRewrite(vector::TransposeOp transposeOp,
7671 PatternRewriter &rewriter)
const override {
7672 auto fromElementsOp =
7673 transposeOp.getVector().getDefiningOp<vector::FromElementsOp>();
7674 if (!fromElementsOp)
7677 VectorType srcTy = fromElementsOp.getDest().getType();
7678 VectorType dstTy = transposeOp.getType();
7680 ArrayRef<int64_t> permutation = transposeOp.getPermutation();
7681 int64_t rank = srcTy.getRank();
7684 SmallVector<int64_t> inversePerm(rank, 0);
7685 for (int64_t i = 0; i < rank; ++i)
7686 inversePerm[permutation[i]] = i;
7688 ArrayRef<int64_t> srcShape = srcTy.getShape();
7689 ArrayRef<int64_t> dstShape = dstTy.getShape();
7690 SmallVector<int64_t> srcIdx(rank, 0);
7691 SmallVector<int64_t> dstIdx(rank, 0);
7695 auto elementsOld = fromElementsOp.getElements();
7696 SmallVector<Value> elementsNew;
7697 int64_t dstNumElements = dstTy.getNumElements();
7698 elementsNew.reserve(dstNumElements);
7702 for (int64_t linearIdx = 0; linearIdx < dstNumElements; ++linearIdx) {
7706 for (int64_t j = 0; j < rank; ++j)
7707 srcIdx[j] = dstIdx[inversePerm[j]];
7709 int64_t srcLin =
linearize(srcIdx, srcStrides);
7711 elementsNew.push_back(elementsOld[srcLin]);
7745class FoldTransposeBroadcast :
public OpRewritePattern<vector::TransposeOp> {
7748 FoldTransposeBroadcast(MLIRContext *context, PatternBenefit benefit = 1)
7749 : OpRewritePattern<vector::TransposeOp>(context, benefit) {}
7751 LogicalResult matchAndRewrite(vector::TransposeOp transpose,
7752 PatternRewriter &rewriter)
const override {
7758 "not preceded by a broadcast");
7761 auto inputType = dyn_cast<VectorType>(
broadcast.getSourceType());
7762 VectorType outputType = transpose.getResultVectorType();
7765 bool inputIsScalar = !inputType;
7766 if (inputIsScalar) {
7772 ArrayRef<int64_t> permutation = transpose.getPermutation();
7773 ArrayRef<int64_t> inputShape = inputType.getShape();
7774 int64_t inputRank = inputType.getRank();
7775 int64_t outputRank = transpose.getType().getRank();
7776 int64_t deltaRank = outputRank - inputRank;
7779 for (
int inputIndex = 0; inputIndex < inputRank; ++inputIndex) {
7780 bool notOne = inputShape[inputIndex] != 1;
7781 bool prevNotOne = (inputIndex != 0 && inputShape[inputIndex - 1] != 1);
7782 bool groupEndFound = notOne || prevNotOne;
7783 if (groupEndFound) {
7784 int high = inputIndex + deltaRank;
7788 for (
int i = low; i < high; ++i) {
7789 if (permutation[i] < low || permutation[i] >= high) {
7791 transpose,
"permutation not local to group");
7805 vector::BroadcastableToResult::Success &&
7806 "not broadcastable directly to transpose output");
7817void vector::TransposeOp::getCanonicalizationPatterns(
7818 RewritePatternSet &results, MLIRContext *context) {
7819 results.
add<FoldTransposeCreateMask, FoldTransposeShapeCast, TransposeFolder,
7820 FoldTransposeSplat, FoldTransposeFromElements,
7821 FoldTransposeBroadcast>(context);
7828void ConstantMaskOp::build(OpBuilder &builder, OperationState &
result,
7830 assert(kind == ConstantMaskKind::AllTrue ||
7831 kind == ConstantMaskKind::AllFalse);
7832 build(builder,
result, type,
7833 kind == ConstantMaskKind::AllTrue
7835 : SmallVector<int64_t>(type.getRank(), 0));
7838LogicalResult ConstantMaskOp::verify() {
7839 auto resultType = llvm::cast<VectorType>(getResult().
getType());
7841 if (resultType.getRank() == 0) {
7842 if (getMaskDimSizes().size() != 1)
7843 return emitError(
"array attr must have length 1 for 0-D vectors");
7844 auto dim = getMaskDimSizes()[0];
7845 if (dim != 0 && dim != 1)
7846 return emitError(
"mask dim size must be either 0 or 1 for 0-D vectors");
7851 if (
static_cast<int64_t
>(getMaskDimSizes().size()) != resultType.getRank())
7853 "must specify array attr of size equal vector result rank");
7856 auto resultShape = resultType.getShape();
7857 auto resultScalableDims = resultType.getScalableDims();
7858 ArrayRef<int64_t> maskDimSizes = getMaskDimSizes();
7859 for (
const auto [index, maskDimSize] : llvm::enumerate(maskDimSizes)) {
7860 if (maskDimSize < 0 || maskDimSize > resultShape[index])
7862 "array attr of size out of bounds of vector result dimension size");
7863 if (resultScalableDims[index] && maskDimSize != 0 &&
7864 maskDimSize != resultShape[index])
7866 "only supports 'none set' or 'all set' scalable dimensions");
7870 bool anyZeros = llvm::is_contained(maskDimSizes, 0);
7871 bool allZeros = llvm::all_of(maskDimSizes, [](int64_t s) {
return s == 0; });
7872 if (anyZeros && !allZeros)
7873 return emitOpError(
"expected all mask dim sizes to be zeros, "
7874 "as a result of conjunction with zero mask dim");
7878bool ConstantMaskOp::isAllOnesMask() {
7881 if (resultType.getRank() == 0) {
7882 assert(getMaskDimSizes().size() == 1 &&
"invalid sizes for zero rank mask");
7883 return getMaskDimSizes()[0] == 1;
7885 for (
const auto [resultSize, maskDimSize] :
7886 llvm::zip_equal(resultType.getShape(), getMaskDimSizes())) {
7887 if (maskDimSize < resultSize)
7893OpFoldResult ConstantMaskOp::fold(FoldAdaptor adaptor) {
7894 ArrayRef<int64_t> bounds = getMaskDimSizes();
7897 auto createBoolSplat = [&](
bool x) {
7903 if (vectorSizes.empty()) {
7904 assert(bounds.size() == 1 &&
"invalid sizes for zero rank mask");
7905 return createBoolSplat(bounds[0] == 1);
7908 if (bounds == vectorSizes)
7909 return createBoolSplat(
true);
7910 if (llvm::all_of(bounds, [](int64_t x) {
return x == 0; }))
7911 return createBoolSplat(
false);
7912 return OpFoldResult();
7919void CreateMaskOp::build(OpBuilder &builder, OperationState &
result,
7921 ArrayRef<OpFoldResult> mixedOperands) {
7922 SmallVector<Value> operands =
7924 build(builder,
result, type, operands);
7927LogicalResult CreateMaskOp::verify() {
7928 auto vectorType = llvm::cast<VectorType>(getResult().
getType());
7930 if (vectorType.getRank() == 0) {
7933 "must specify exactly one operand for 0-D create_mask");
7935 llvm::cast<VectorType>(getResult().
getType()).getRank()) {
7937 "must specify an operand for each result vector dimension");
7967class CreateMaskFolder final :
public OpRewritePattern<CreateMaskOp> {
7971 LogicalResult matchAndRewrite(CreateMaskOp createMaskOp,
7972 PatternRewriter &rewriter)
const override {
7973 VectorType maskType = createMaskOp.getVectorType();
7974 ArrayRef<int64_t> maskTypeDimSizes = maskType.getShape();
7975 ArrayRef<bool> maskTypeDimScalableFlags = maskType.getScalableDims();
7978 constexpr std::array<int64_t, 1> rankZeroShape{1};
7979 constexpr std::array<bool, 1> rankZeroScalableDims{
false};
7980 if (maskType.getRank() == 0) {
7981 maskTypeDimSizes = rankZeroShape;
7982 maskTypeDimScalableFlags = rankZeroScalableDims;
7987 SmallVector<int64_t, 4> constantDims;
7988 for (
auto [i, dimSize] : llvm::enumerate(createMaskOp.getOperands())) {
7993 if (maskTypeDimScalableFlags[i] && intSize >= 0)
7995 constantDims.push_back(*intSize);
7999 if (vscaleMultiplier < maskTypeDimSizes[i])
8001 constantDims.push_back(*vscaleMultiplier);
8008 for (
auto [value, maskDimSize] : llvm::zip(constantDims, maskTypeDimSizes))
8009 value = std::clamp<int64_t>(value, 0, maskDimSize);
8012 if (llvm::is_contained(constantDims, 0))
8013 constantDims.assign(constantDims.size(), 0);
8024void CreateMaskOp::getCanonicalizationPatterns(RewritePatternSet &results,
8025 MLIRContext *context) {
8026 results.
add<CreateMaskFolder>(context);
8034 OpBuilder &builder, OperationState &
result, Value mask,
8035 Operation *maskableOp,
8036 function_ref<
void(OpBuilder &, Operation *)> maskRegionBuilder) {
8037 assert(maskRegionBuilder &&
8038 "builder callback for 'maskRegion' must be present");
8040 result.addOperands(mask);
8041 OpBuilder::InsertionGuard guard(builder);
8042 Region *maskRegion =
result.addRegion();
8044 maskRegionBuilder(builder, maskableOp);
8049 Value mask, Operation *maskableOp,
8050 function_ref<
void(OpBuilder &, Operation *)> maskRegionBuilder) {
8051 build(builder,
result, resultTypes, mask, Value(), maskableOp,
8057 Value mask, Value passthru, Operation *maskableOp,
8058 function_ref<
void(OpBuilder &, Operation *)> maskRegionBuilder) {
8059 build(builder,
result, mask, maskableOp, maskRegionBuilder);
8061 result.addOperands(passthru);
8062 result.addTypes(resultTypes);
8065ParseResult MaskOp::parse(OpAsmParser &parser, OperationState &
result) {
8067 result.regions.reserve(1);
8068 Region &maskRegion = *
result.addRegion();
8073 OpAsmParser::UnresolvedOperand mask;
8078 OpAsmParser::UnresolvedOperand passthru;
8080 if (parsePassthru.succeeded() && parser.
parseOperand(passthru))
8087 MaskOp::ensureTerminator(maskRegion, builder,
result.location);
8098 SmallVector<Type> resultTypes;
8101 result.types.append(resultTypes);
8107 if (parsePassthru.succeeded()) {
8108 if (resultTypes.empty())
8111 "expects a result if passthru operand is provided");
8120void mlir::vector::MaskOp::print(OpAsmPrinter &p) {
8121 p <<
" " << getMask();
8123 p <<
", " << getPassthru();
8127 Block *singleBlock = &getMaskRegion().getBlocks().front();
8133 getOperation()->getDiscardableAttrDictionary().getValue());
8135 p <<
" : " << getMask().getType();
8136 if (getNumResults() > 0)
8137 p <<
" -> " << getResultTypes();
8140void MaskOp::ensureTerminator(Region ®ion, Builder &builder, Location loc) {
8143 OpTrait::SingleBlockImplicitTerminator<vector::YieldOp>::Impl<
8144 MaskOp>::ensureTerminator(region, builder, loc);
8150 if (isa<vector::YieldOp>(block.
back()))
8158 OpTrait::SingleBlockImplicitTerminator<vector::YieldOp>::Impl<
8159 MaskOp>::ensureTerminator(region, builder, loc);
8165 Operation *maskedOp = &block.
front();
8166 opBuilder.setInsertionPointToEnd(&block);
8167 vector::YieldOp::create(opBuilder, loc, maskedOp->
getResults());
8170LogicalResult MaskOp::verify() {
8172 Block &block = getMaskRegion().getBlocks().
front();
8174 return emitOpError(
"expects a terminator within the mask region");
8177 if (numMaskRegionOps > 2)
8178 return emitOpError(
"expects only one operation to mask");
8181 auto terminator = dyn_cast<vector::YieldOp>(block.
back());
8183 return emitOpError(
"expects a terminator within the mask region");
8185 if (terminator->getNumOperands() != getNumResults())
8187 "expects number of results to match mask region yielded values");
8190 if (numMaskRegionOps == 1)
8193 auto maskableOp = dyn_cast<MaskableOpInterface>(block.
front());
8195 return emitOpError(
"expects a MaskableOpInterface within the mask region");
8199 return emitOpError(
"expects number of results to match maskable operation "
8200 "number of results");
8202 if (!llvm::equal(maskableOp->
getResults(), terminator.getOperands()))
8203 return emitOpError(
"expects all the results from the MaskableOpInterface "
8204 "to match all the values returned by the terminator");
8206 if (!llvm::equal(maskableOp->
getResultTypes(), getResultTypes()))
8208 "expects result type to match maskable operation result type");
8211 [](Type t) { return llvm::isa<VectorType>(t); }) > 1)
8212 return emitOpError(
"multiple vector results not supported");
8215 Type expectedMaskType = maskableOp.getExpectedMaskType();
8216 if (getMask().
getType() != expectedMaskType)
8217 return emitOpError(
"expects a ")
8218 << expectedMaskType <<
" mask for the maskable operation";
8221 Value passthru = getPassthru();
8223 if (!maskableOp.supportsPassthru())
8225 "doesn't expect a passthru argument for this maskable operation");
8228 return emitOpError(
"expects result when passthru argument is provided");
8231 return emitOpError(
"expects passthru type to match result type");
8251static LogicalResult foldEmptyMaskOp(MaskOp maskOp, MaskOp::FoldAdaptor adaptor,
8252 SmallVectorImpl<OpFoldResult> &results) {
8253 if (!maskOp.isEmpty() || maskOp.hasPassthru())
8256 Block *block = maskOp.getMaskBlock();
8257 auto terminator = cast<vector::YieldOp>(block->
front());
8258 if (terminator.getNumOperands() == 0)
8262 llvm::append_range(results, terminator.getOperands());
8266LogicalResult MaskOp::fold(FoldAdaptor adaptor,
8267 SmallVectorImpl<OpFoldResult> &results) {
8268 if (succeeded(foldEmptyMaskOp(*
this, adaptor, results)))
8278 Operation *maskableOp = getMaskableOp();
8284 llvm::append_range(results, maskableOp->
getResults());
8300class CanonializeEmptyMaskOp :
public OpRewritePattern<MaskOp> {
8303 LogicalResult matchAndRewrite(MaskOp maskOp,
8304 PatternRewriter &rewriter)
const override {
8305 if (!maskOp.isEmpty())
8308 if (!maskOp.hasPassthru())
8315 VectorType maskType = maskOp.getMask().getType();
8316 for (Type resultType : maskOp.getResultTypes()) {
8317 auto vecResultType = dyn_cast<VectorType>(resultType);
8318 if (!vecResultType || vecResultType.getShape() != maskType.getShape())
8322 Block *block = maskOp.getMaskBlock();
8323 auto terminator = cast<vector::YieldOp>(block->
front());
8324 assert(terminator.getNumOperands() == 1 &&
8325 "expected one result when passthru is provided");
8328 maskOp, maskOp.getResultTypes(), maskOp.getMask(),
8329 terminator.getOperand(0), maskOp.getPassthru());
8335void MaskOp::getCanonicalizationPatterns(RewritePatternSet &results,
8336 MLIRContext *context) {
8337 results.
add<CanonializeEmptyMaskOp>(context);
8343Operation *MaskOp::getMaskableOp() {
8344 Block *block = getMaskBlock();
8348 return &block->
front();
8352bool MaskOp::hasPassthru() {
return getPassthru() != Value(); }
8358LogicalResult ScanOp::verify() {
8359 VectorType srcType = getSourceType();
8360 VectorType initialType = getInitialValueType();
8362 int64_t srcRank = srcType.getRank();
8363 int64_t reductionDim = getReductionDim();
8364 if (reductionDim >= srcRank)
8365 return emitOpError(
"reduction dimension ")
8366 << reductionDim <<
" has to be less than " << srcRank;
8369 int64_t initialValueRank = initialType.getRank();
8370 if (initialValueRank != srcRank - 1)
8371 return emitOpError(
"initial value rank ")
8372 << initialValueRank <<
" has to be equal to " << srcRank - 1;
8375 ArrayRef<int64_t> srcShape = srcType.getShape();
8376 ArrayRef<int64_t> initialValueShapes = initialType.getShape();
8377 SmallVector<int64_t> expectedShape;
8378 for (
int i = 0; i < srcRank; i++) {
8379 if (i != reductionDim)
8380 expectedShape.push_back(srcShape[i]);
8382 if (!llvm::equal(initialValueShapes, expectedShape)) {
8383 return emitOpError(
"incompatible input/initial value shapes");
8387 Type eltType = getDestType().getElementType();
8389 return emitOpError(
"unsupported reduction type ")
8390 << eltType <<
" for kind '" << stringifyCombiningKind(getKind())
8397 RewritePatternSet &patterns, PatternBenefit benefit) {
8399 .
add<CreateMaskFolder, MaskedLoadFolder, MaskedStoreFolder, GatherFolder,
8400 ScatterFolder, ExpandLoadFolder, CompressStoreFolder,
8401 StridedSliceConstantMaskFolder, TransposeFolder>(
8406 CombiningKind kind, Value v1, Value acc,
8407 arith::FastMathFlagsAttr fastmath,
8414 case CombiningKind::ADD:
8416 result =
b.createOrFold<arith::AddIOp>(loc, v1, acc);
8417 else if (llvm::isa<FloatType>(t1) && llvm::isa<FloatType>(tAcc))
8418 result =
b.createOrFold<arith::AddFOp>(loc, v1, acc, fastmath);
8420 llvm_unreachable(
"invalid value types for ADD reduction");
8422 case CombiningKind::AND:
8424 result =
b.createOrFold<arith::AndIOp>(loc, v1, acc);
8426 case CombiningKind::MAXNUMF:
8427 assert(llvm::isa<FloatType>(t1) && llvm::isa<FloatType>(tAcc) &&
8428 "expected float values");
8429 result =
b.createOrFold<arith::MaxNumFOp>(loc, v1, acc, fastmath);
8431 case CombiningKind::MAXIMUMNUMF:
8432 assert(llvm::isa<FloatType>(t1) && llvm::isa<FloatType>(tAcc) &&
8433 "expected float values");
8434 result =
b.createOrFold<arith::MaximumNumFOp>(loc, v1, acc, fastmath);
8436 case CombiningKind::MAXIMUMF:
8437 assert(llvm::isa<FloatType>(t1) && llvm::isa<FloatType>(tAcc) &&
8438 "expected float values");
8439 result =
b.createOrFold<arith::MaximumFOp>(loc, v1, acc, fastmath);
8441 case CombiningKind::MINNUMF:
8442 assert(llvm::isa<FloatType>(t1) && llvm::isa<FloatType>(tAcc) &&
8443 "expected float values");
8444 result =
b.createOrFold<arith::MinNumFOp>(loc, v1, acc, fastmath);
8446 case CombiningKind::MINIMUMNUMF:
8447 assert(llvm::isa<FloatType>(t1) && llvm::isa<FloatType>(tAcc) &&
8448 "expected float values");
8449 result =
b.createOrFold<arith::MinimumNumFOp>(loc, v1, acc, fastmath);
8451 case CombiningKind::MINIMUMF:
8452 assert(llvm::isa<FloatType>(t1) && llvm::isa<FloatType>(tAcc) &&
8453 "expected float values");
8454 result =
b.createOrFold<arith::MinimumFOp>(loc, v1, acc, fastmath);
8456 case CombiningKind::MAXSI:
8458 result =
b.createOrFold<arith::MaxSIOp>(loc, v1, acc);
8460 case CombiningKind::MINSI:
8462 result =
b.createOrFold<arith::MinSIOp>(loc, v1, acc);
8464 case CombiningKind::MAXUI:
8466 result =
b.createOrFold<arith::MaxUIOp>(loc, v1, acc);
8468 case CombiningKind::MINUI:
8470 result =
b.createOrFold<arith::MinUIOp>(loc, v1, acc);
8472 case CombiningKind::MUL:
8474 result =
b.createOrFold<arith::MulIOp>(loc, v1, acc);
8475 else if (llvm::isa<FloatType>(t1) && llvm::isa<FloatType>(tAcc))
8476 result =
b.createOrFold<arith::MulFOp>(loc, v1, acc, fastmath);
8478 llvm_unreachable(
"invalid value types for MUL reduction");
8480 case CombiningKind::OR:
8482 result =
b.createOrFold<arith::OrIOp>(loc, v1, acc);
8484 case CombiningKind::XOR:
8486 result =
b.createOrFold<arith::XOrIOp>(loc, v1, acc);
8490 assert(
result &&
"unknown CombiningKind");
8498void StepOp::inferResultRanges(ArrayRef<ConstantIntRanges> argRanges,
8500 auto resultType = cast<VectorType>(
getType());
8501 if (resultType.isScalable()) {
8507 uint64_t maxIndex = resultType.getDimSize(0) - 1;
8508 APInt umin = APInt::getZero(bitwidth);
8509 APInt umax = APInt::getMaxValue(bitwidth).ugt(maxIndex)
8510 ? APInt(bitwidth, maxIndex)
8511 : APInt::getMaxValue(bitwidth);
8542struct StepCompareFolder :
public OpRewritePattern<StepOp> {
8545 LogicalResult matchAndRewrite(StepOp stepOp,
8546 PatternRewriter &rewriter)
const override {
8547 const int64_t stepSize = stepOp.getResult().getType().getNumElements();
8549 for (OpOperand &use : stepOp.getResult().getUses()) {
8550 auto cmpiOp = dyn_cast<arith::CmpIOp>(use.getOwner());
8555 const unsigned stepOperandNumber = use.getOperandNumber();
8556 if (stepOperandNumber != 0)
8560 unsigned constOperandNumber = 1;
8561 Value otherOperand = cmpiOp.getOperand(constOperandNumber);
8562 std::optional<int64_t> maybeConstValue =
8564 if (!maybeConstValue.has_value())
8567 int64_t constValue = maybeConstValue.value();
8568 arith::CmpIPredicate pred = cmpiOp.getPredicate();
8570 auto maybeSplat = [&]() -> std::optional<bool> {
8572 if ((pred == arith::CmpIPredicate::ult ||
8573 pred == arith::CmpIPredicate::uge) &&
8574 stepSize <= constValue)
8575 return pred == arith::CmpIPredicate::ult;
8578 if ((pred == arith::CmpIPredicate::ule ||
8579 pred == arith::CmpIPredicate::ugt) &&
8580 stepSize - 1 <= constValue) {
8581 return pred == arith::CmpIPredicate::ule;
8585 if ((pred == arith::CmpIPredicate::eq ||
8586 pred == arith::CmpIPredicate::ne) &&
8587 stepSize <= constValue)
8588 return pred == arith::CmpIPredicate::ne;
8590 return std::nullopt;
8593 if (!maybeSplat.has_value())
8598 auto type = dyn_cast<VectorType>(cmpiOp.getResult().getType());
8603 Value splat = mlir::arith::ConstantOp::create(rewriter, cmpiOp.getLoc(),
8615void StepOp::getCanonicalizationPatterns(RewritePatternSet &results,
8616 MLIRContext *context) {
8617 results.
add<StepCompareFolder>(context);
8627 Operation *maskableOp) {
8628 assert(maskableOp->
getBlock() &&
"MaskableOp must be inserted into a block");
8640 Operation *maskableOp, Value mask,
8645 return MaskOp::create(builder, maskableOp->
getLoc(),
8648 return MaskOp::create(builder, maskableOp->
getLoc(),
8661 Value newValue, Value passthru) {
8665 return arith::SelectOp::create(builder, newValue.
getLoc(), newValue.
getType(),
8666 mask, newValue, passthru);
8677struct InterleaveDeinterleaveFolder :
public OpRewritePattern<InterleaveOp> {
8680 LogicalResult matchAndRewrite(InterleaveOp interleaveOp,
8681 PatternRewriter &rewriter)
const override {
8682 auto lhsDefOp = interleaveOp.getLhs().getDefiningOp<DeinterleaveOp>();
8683 auto rhsDefOp = interleaveOp.getRhs().getDefiningOp<DeinterleaveOp>();
8684 if (!lhsDefOp || !rhsDefOp || lhsDefOp != rhsDefOp)
8686 for (
auto [idx, operand] : llvm::enumerate(interleaveOp.getOperands())) {
8687 if (cast<OpResult>(operand).getResultNumber() != idx)
8690 rewriter.
replaceOp(interleaveOp, lhsDefOp.getSource());
8696void InterleaveOp::getCanonicalizationPatterns(RewritePatternSet &results,
8697 MLIRContext *context) {
8698 results.
add<InterleaveDeinterleaveFolder>(context);
8701OpFoldResult InterleaveOp::fold(FoldAdaptor adaptor) {
8703 auto splat = dyn_cast_if_present<SplatElementsAttr>(adaptor.getLhs());
8704 if (!splat || adaptor.getLhs() != adaptor.getRhs())
8706 return SplatElementsAttr::get(getResultVectorType(),
8707 splat.getSplatValue<Attribute>());
8710std::optional<SmallVector<int64_t, 4>>
8711InterleaveOp::getIterationShapeToUnroll() {
8712 return llvm::to_vector<4>(getResultVectorType().
getShape());
8719std::optional<SmallVector<int64_t, 4>>
8720DeinterleaveOp::getIterationShapeToUnroll() {
8721 return llvm::to_vector<4>(getResultVectorType().
getShape());
8730struct SelectBroadcastCondition : OpRewritePattern<arith::SelectOp> {
8733 LogicalResult matchAndRewrite(arith::SelectOp select,
8734 PatternRewriter &rewriter)
const override {
8739 select.getConditionMutable().assign(
broadcast.getSource());
8746void VectorDialect::getCanonicalizationPatterns(
8747 RewritePatternSet &results)
const {
8755#define GET_ATTRDEF_CLASSES
8756#include "mlir/Dialect/Vector/IR/VectorAttributes.cpp.inc"
8758#define GET_OP_CLASSES
8759#include "mlir/Dialect/Vector/IR/VectorOps.cpp.inc"
if(failed(verifyVectorMemoryOp(getOperation(), memrefType, getVectorType()))) return failure()
getNumOperands() - 1))) return failure()
Given a list of lists of parsed operands, populates uniqueOperands with unique operands.
static LogicalResult extractStrides(AffineExpr e, AffineExpr multiplicativeFactor, MutableArrayRef< AffineExpr > strides, AffineExpr &offset)
Takes a single AffineExpr e and populates the strides array with the strides expressions for each dim...
static void copy(Location loc, Value dst, Value src, Value size, OpBuilder &builder)
Copies the given number of bytes from src to dst pointers.
static Value getBase(Value v)
Looks through known "view-like" ops to find the base memref.
static bool isLegalToInline(InlinerInterface &interface, Region *src, Region *insertRegion, bool shouldCloneInlinedRegion, IRMapping &valueMapping)
Utility to check that all of the operations within 'src' can be inlined.
static SmallVector< unsigned > extractPosition(ArrayRef< int64_t > indices)
Convert the value of a DenseI64ArrayAttr to a vector of unsigned indices.
*if copies could not be generated due to yet unimplemented cases *copyInPlacementStart and copyOutPlacementStart in copyPlacementBlock *specify the insertion points where the incoming copies and outgoing should be inserted(the insertion happens right before the *insertion point). Since `begin` can itself be invalidated due to the memref *rewriting done from this method
static ParseResult parseBoolAttr(OpAsmParser &parser, BoolAttr &result)
static void printBoolAttr(OpAsmPrinter &printer, Operation *, BoolAttr attr)
static std::optional< VectorShape > vectorShape(Type type)
static Value max(ImplicitLocOpBuilder &builder, Value value, Value bound)
static Value min(ImplicitLocOpBuilder &builder, Value value, Value bound)
static void contract(RootOrderingGraph &graph, ArrayRef< Value > cycle, const DenseMap< Value, unsigned > &parentDepths, DenseMap< Value, Value > &actualSource, DenseMap< Value, Value > &actualTarget)
Contracts the specified cycle in the given graph in-place.
static Type getElementType(Type type, ArrayRef< int32_t > indices, function_ref< InFlightDiagnostic(StringRef)> emitErrorFn)
Walks the given type hierarchy with the given indices, potentially down to component granularity,...
static Value broadcast(Location loc, Value toBroadcast, unsigned numElements, const TypeConverter &typeConverter, ConversionPatternRewriter &rewriter)
Broadcasts the value to vector with numElements number of elements.
static VectorType getVectorType(Type scalarTy, const VectorizationStrategy *strategy)
Returns the vector type resulting from applying the provided vectorization strategy on the scalar typ...
static ArrayRef< int64_t > getShape(Type type)
Returns the shape of the given type.
static SmallVector< Value > slice(OpBuilder &b, Location loc, Value value, VectorType nativeType)
static VectorType trimLeadingUnitDims(VectorType oldType, bool trimOnlyOneDim=false, bool allowRank0=false)
static MaskFormat getMaskFormat(Value mask)
Helper method to classify a mask value.
static OpFoldResult foldShuffleIdentityMask(ShuffleOp op)
Fold shuffle V1, V2, [0, 1, 2, 3] : <4xi32>, <2xi32> -> V1.
static LogicalResult foldExtractOpFromExtractChain(ExtractOp extractOp)
Fold the result of chains of ExtractOp in place by simply concatenating the positions.
static OpFoldResult foldFromElementsToElements(FromElementsOp fromElementsOp)
Folds vector.from_elements(vector.to_elements(vector)) into vector.
static bool hasZeroDimVectors(Operation *op)
Returns true if the operation has a 0-D vector type operand or result.
static void printTransferAttrs(OpAsmPrinter &p, VectorTransferOpInterface op)
static Value foldScalarExtractFromFromElements(ExtractOp extractOp)
Try to fold the extraction of a scalar from a vector defined by vector.from_elements.
static std::vector< std::pair< int64_t, int64_t > > getDimPairForIterTy(ContractionOp *contract, IteratorType targetIteratorType)
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 std::optional< int64_t > getOneSidedReductionDim(vector::ContractionOp *contract)
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 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 isFunctionOfDim(unsigned position) const
Return true if any affine expression involves AffineDimExpr position.
bool isMinorIdentity() const
Returns true if this affine map is a minor identity, i.e.
unsigned getDimPosition(unsigned idx) const
Extracts the position of the dimensional expression at the given result, when the caller knows it is ...
static AffineMap get(MLIRContext *context)
Returns a zero result affine map with no dimensions or symbols: () -> ().
bool isProjectedPermutation(bool allowZeroInResults=false) const
Returns true if the AffineMap represents a subset (i.e.
unsigned getNumSymbols() const
unsigned getNumDims() const
ArrayRef< AffineExpr > getResults() const
unsigned getNumResults() const
static SmallVector< AffineMap, 4 > inferFromExprList(ArrayRef< ArrayRef< AffineExpr > > exprsList, MLIRContext *context)
Returns a vector of AffineMaps; each with as many results as exprs.size(), as many dims as the larges...
unsigned getNumInputs() const
AffineExpr getResult(unsigned idx) const
static AffineMap getPermutationMap(ArrayRef< unsigned > permutation, MLIRContext *context)
Returns an AffineMap representing a permutation.
SmallVector< unsigned > getBroadcastDims() const
Returns the list of broadcast dimensions (i.e.
AffineMap compose(AffineMap map) const
Returns the AffineMap resulting from composing this with map.
@ Square
Square brackets surrounding zero or more operands.
virtual ParseResult parseColonTypeList(SmallVectorImpl< Type > &result)=0
Parse a colon followed by a type list, which must have at least one type.
virtual Builder & getBuilder() const =0
Return a builder which provides useful access to MLIRContext, global objects like types and attribute...
virtual ParseResult parseOptionalAttrDict(NamedAttrList &result)=0
Parse a named dictionary into 'result' if it is present.
MLIRContext * getContext() const
virtual InFlightDiagnostic emitError(SMLoc loc, const Twine &message={})=0
Emit a diagnostic at the specified location and return failure.
ParseResult addTypeToList(Type type, SmallVectorImpl< Type > &result)
Add the specified type to the end of the specified type list and return success.
virtual ParseResult parseColonType(Type &result)=0
Parse a colon followed by a type.
virtual SMLoc getCurrentLocation()=0
Get the location of the next token and store it into the argument.
virtual ParseResult parseOptionalComma()=0
Parse a , token if present.
virtual SMLoc getNameLoc() const =0
Return the location of the original name token.
virtual ParseResult parseType(Type &result)=0
Parse a type.
virtual ParseResult parseComma()=0
Parse a , token.
virtual ParseResult parseOptionalArrowTypeList(SmallVectorImpl< Type > &result)=0
Parse an optional arrow followed by a type list.
ParseResult parseKeywordType(const char *keyword, Type &result)
Parse a keyword followed by a type.
virtual ParseResult parseAttribute(Attribute &result, Type type={})=0
Parse an arbitrary attribute of a given type and return it in result.
virtual void printAttribute(Attribute attr)
Base storage class appearing in an attribute.
Attributes are known-constant values of operations.
Dialect & getDialect() const
Get the dialect this attribute is registered to.
OpListType & getOperations()
static BoolAttr get(MLIRContext *context, bool value)
This class is a general helper class for creating context-global objects like types,...
IntegerAttr getIndexAttr(int64_t value)
DenseI32ArrayAttr getDenseI32ArrayAttr(ArrayRef< int32_t > values)
IntegerAttr getIntegerAttr(Type type, int64_t value)
DenseI64ArrayAttr getDenseI64ArrayAttr(ArrayRef< int64_t > values)
IntegerAttr getI64IntegerAttr(int64_t value)
IntegerType getIntegerType(unsigned width)
TypedAttr getZeroAttr(Type type)
ArrayAttr getArrayAttr(ArrayRef< Attribute > value)
MLIRContext * getContext() const
ArrayAttr getI64ArrayAttr(ArrayRef< int64_t > values)
ArrayAttr getBoolArrayAttr(ArrayRef< bool > values)
ArrayAttr getAffineMapArrayAttr(ArrayRef< AffineMap > values)
static ConstantIntRanges fromUnsigned(const APInt &umin, const APInt &umax)
Create an ConstantIntRanges with the unsigned minimum and maximum equal to umin and umax and the sign...
static unsigned getStorageBitwidth(Type type)
Return the bitwidth that should be used for integer ranges describing type.
The main mechanism for performing data layout queries.
static DataLayout closest(Operation *op)
Returns the layout of the closest parent operation carrying layout info.
llvm::TypeSize getTypeSizeInBits(Type t) const
Returns the size in bits of the given type in the current scope.
An attribute that represents a reference to a dense vector or tensor object.
std::enable_if_t<!std::is_base_of< Attribute, T >::value||std::is_same< Attribute, T >::value, T > getSplatValue() const
Return the splat value for this attribute.
bool isSplat() const
Returns true if this attribute corresponds to a splat, i.e.
static DenseElementsAttr get(ShapedType type, ArrayRef< Attribute > values)
Constructs a dense elements attribute from an array of element values.
virtual Operation * materializeConstant(OpBuilder &builder, Attribute value, Type type, Location loc)
Registered hook to materialize a single constant operation from a given attribute value with the desi...
This is a utility class for mapping one set of IR entities to another.
void map(Value from, Value to)
Inserts a new mapping for 'from' to 'to'.
This class defines the main interface for locations in MLIR and acts as a non-nullable wrapper around...
MLIRContext is the top-level object for a collection of MLIR operations.
NamedAttrList is array of NamedAttributes that tracks whether it is sorted and does some basic work t...
The OpAsmParser has methods for interacting with the asm parser: parsing things from it,...
virtual ParseResult parseRegion(Region ®ion, ArrayRef< Argument > arguments={}, bool enableNameShadowing=false)=0
Parses a region.
ParseResult parseTrailingOperandList(SmallVectorImpl< UnresolvedOperand > &result, Delimiter delimiter=Delimiter::None)
Parse zero or more trailing SSA comma-separated trailing operand references with a specified surround...
virtual ParseResult resolveOperand(const UnresolvedOperand &operand, Type type, SmallVectorImpl< Value > &result)=0
Resolve an operand to an SSA value, emitting an error on failure.
ParseResult resolveOperands(Operands &&operands, Type type, SmallVectorImpl< Value > &result)
Resolve a list of operands to SSA values, emitting an error on failure, or appending the results to t...
virtual ParseResult parseOperand(UnresolvedOperand &result, bool allowResultNumber=true)=0
Parse a single SSA value operand name along with a result number if allowResultNumber is true.
virtual ParseResult parseOperandList(SmallVectorImpl< UnresolvedOperand > &result, Delimiter delimiter=Delimiter::None, bool allowResultNumber=true, int requiredOperandCount=-1)=0
Parse zero or more SSA comma-separated operand references with a specified surrounding delimiter,...
This is a pure-virtual base class that exposes the asmprinter hooks necessary to implement a custom p...
virtual void printOptionalAttrDict(ArrayRef< NamedAttribute > attrs, ArrayRef< StringRef > elidedAttrs={})=0
If the specified operation has attributes, print out an attribute dictionary with their values.
virtual void printCustomOrGenericOp(Operation *op)=0
Prints the entire operation with the custom assembly form, if available, or the generic assembly form...
RAII guard to reset the insertion point of the builder when destroyed.
This class helps build Operations.
Block * createBlock(Region *parent, Region::iterator insertPt={}, TypeRange argTypes={}, ArrayRef< Location > locs={})
Add new block with 'argTypes' arguments and set the insertion point to the end of it.
Operation * clone(Operation &op, IRMapping &mapper)
Creates a deep copy of the specified operation, remapping any operands that use values outside of the...
void setInsertionPoint(Block *block, Block::iterator insertPoint)
Set the insertion point to the specified location.
Block * getInsertionBlock() const
Return the block the current insertion point belongs to.
void createOrFold(SmallVectorImpl< Value > &results, Location location, Args &&...args)
Create an operation of specific op type at the current insertion point, and immediately try to fold i...
void setInsertionPointAfter(Operation *op)
Sets the insertion point to the node after the specified operation, which will cause subsequent inser...
This class represents a single result from folding an operation.
This class implements the operand iterators for the Operation class.
Operation is the basic unit of execution within MLIR.
Value getOperand(unsigned idx)
void dropAllUses()
Drop all uses of results of this operation.
void setOperand(unsigned idx, Value value)
Block * getBlock()
Returns the operation block that contains this operation.
Location getLoc()
The source location the operation was defined or derived from.
operand_type_range getOperandTypes()
result_type_range getResultTypes()
void moveBefore(Operation *existingOp)
Unlink this operation from its current block and insert it right before existingOp which may be in th...
result_range getResults()
InFlightDiagnostic emitOpError(const Twine &message={})
Emit an error with the op name prefixed, like "'dim' op " which is convenient for verifiers.
unsigned getNumResults()
Return the number of results held by this operation.
A special type of RewriterBase that coordinates the application of a rewrite pattern on the current I...
This class contains a list of basic blocks and a link to the parent operation it is attached to.
MLIRContext * getContext() const
RewritePatternSet & add(ConstructorArg &&arg, ConstructorArgs &&...args)
Add an instance of each of the pattern types 'Ts' to the pattern list with the given arguments.
virtual void replaceOp(Operation *op, ValueRange newValues)
Replace the results of the given (original) operation with the specified list of values (replacements...
virtual void eraseOp(Operation *op)
This method erases an operation that is known to have no uses.
std::enable_if_t<!std::is_convertible< CallbackT, Twine >::value, LogicalResult > notifyMatchFailure(Location loc, CallbackT &&reasonCallback)
Used to notify the listener that the IR failed to be rewritten because of a match failure,...
void modifyOpInPlace(Operation *root, CallableT &&callable)
This method is a utility wrapper around an in-place modification of an operation.
OpTy replaceOpWithNewOp(Operation *op, Args &&...args)
Replace the results of the given (original) op with a new op that is created without verification (re...
T * allocate()
Allocate an instance of the provided type.
Instances of the Type class are uniqued, have an immutable identifier and an optional mutable compone...
bool isIntOrIndexOrFloat() const
Return true if this is an integer (of any signedness), index, or float type.
bool isIntOrIndex() const
Return true if this is an integer (of any signedness) or an index type.
bool isIntOrFloat() const
Return true if this is an integer (of any signedness) or a float type.
unsigned getIntOrFloatBitWidth() const
Return the bit width of an integer or a float type, assert failure on other types.
static FailureOr< bool > areEqual(const Variable &var1, const Variable &var2)
Compute whether the given variables are equal.
static FailureOr< int64_t > computeConstantDelta(Value value1, Value value2, std::optional< int64_t > dim1=std::nullopt, std::optional< int64_t > dim2=std::nullopt)
Compute a constant delta between the given two values.
This class represents an instance of an SSA value in the MLIR system, representing a computable value...
Type getType() const
Return the type of this value.
Location getLoc() const
Return the location of this value.
Operation * getDefiningOp() const
If this value is the result of an operation, return the operation that defines it.
This is a builder type that keeps local references to arguments.
Builder & setElementType(Type newElementType)
Specialization of arith.constant op that returns an integer of index type.
static ConstantIndexOp create(OpBuilder &builder, Location location, int64_t value)
Speculatability
This enum is returned from the getSpeculatability method in the ConditionallySpeculatable op interfac...
constexpr auto Speculatable
constexpr auto NotSpeculatable
FailureOr< int64_t > fullyComposeAndComputeConstantDelta(Value value1, Value value2)
Compute a constant delta of the given two values.
constexpr void enumerate(std::tuple< Tys... > &tuple, CallbackT &&callback)
FailureOr< std::optional< SmallVector< Value > > > bubbleDownInPlaceMemorySpaceCastImpl(OpOperand &operand, ValueRange results)
Tries to bubble-down inplace a MemorySpaceCastOpInterface operation referenced by operand.
bool hasNegativeStaticStride(MemRefType memRefTy)
Returns true if any stride of memRefTy is statically known to be negative.
LogicalResult foldMemRefCast(Operation *op, Value inner=nullptr)
This is a common utility used for patterns of the form "someop(memref.cast) -> someop".
Operation::operand_range getIndices(Operation *op)
Get the indices that the given load/store operation is operating on.
MemRefType getMemRefType(T &&t)
Convenience method to abbreviate casting getType().
LogicalResult foldTensorCast(Operation *op)
Performs folding of any operand of op if it comes from a tensor::CastOp that can be folded.
detail::poison_attr_matcher m_Poison()
Matches a poison constant (any attribute implementing PoisonAttrInterface).
Value makeArithReduction(OpBuilder &b, Location loc, CombiningKind kind, Value v1, Value acc, arith::FastMathFlagsAttr fastmath=nullptr, Value mask=nullptr)
Returns the result value of reducing two scalar/vector values with the corresponding arith operation.
ArrayAttr getVectorSubscriptAttr(Builder &b, ArrayRef< int64_t > values)
Returns an integer array attribute containing the given values using the integer type required for su...
Operation * maskOperation(OpBuilder &builder, Operation *maskableOp, Value mask, Value passthru=Value())
Creates a vector.mask operation around a maskable operation.
void buildTerminatedBody(OpBuilder &builder, Location loc)
Default callback to build a region with a 'vector.yield' terminator with no arguments.
std::optional< int64_t > getConstantVscaleMultiplier(Value value)
If value is a constant multiple of vector.vscale (e.g.
AffineMap getTransferMinorIdentityMap(ShapedType shapedType, VectorType vectorType)
Build the default minor identity map suitable for a vector transfer.
bool checkSameValueRAW(TransferWriteOp defWrite, TransferReadOp read)
Return true if the transfer_write fully writes the data accessed by the transfer_read.
ConstantMaskKind
Predefined constant_mask kinds.
BroadcastableToResult isBroadcastableTo(Type srcType, VectorType dstVectorType, std::pair< VectorDim, VectorDim > *mismatchingDims=nullptr)
Return whether srcType can be broadcast to dstVectorType under the semantics of the vector....
VectorType inferTransferOpMaskType(VectorType vecType, AffineMap permMap)
Infers the mask type for a transfer op given its vector type and permutation map.
Value selectPassthru(OpBuilder &builder, Value mask, Value newValue, Value passthru)
Creates a vector select operation that picks values from newValue or passthru for each result vector ...
bool isDisjointTransferIndices(VectorTransferOpInterface transferA, VectorTransferOpInterface transferB, bool testDynamicValueUsingBounds=false)
Return true if we can prove that the transfer operations access disjoint memory, without requring the...
bool isDisjointTransferSet(VectorTransferOpInterface transferA, VectorTransferOpInterface transferB, bool testDynamicValueUsingBounds=false)
Return true if we can prove that the transfer operations access disjoint memory, requiring the operat...
bool checkSameValueWAW(TransferWriteOp write, TransferWriteOp priorWrite)
Return true if the write op fully over-write the priorWrite transfer_write op.
SmallVector< int64_t > getAsIntegers(ArrayRef< Value > values)
Returns the integer numbers in values.
void populateVectorToVectorCanonicalizationPatterns(RewritePatternSet &patterns, PatternBenefit benefit=1)
Collect a set of vector-to-vector canonicalization patterns.
void createMaskOpRegion(OpBuilder &builder, Operation *maskableOp)
Create the vector.yield-ended region of a vector.mask op with maskableOp as masked operation.
SmallVector< Value > getAsValues(OpBuilder &builder, Location loc, ArrayRef< OpFoldResult > foldResults)
Convert foldResults into Values.
Value getVectorReductionOp(arith::AtomicRMWKind op, OpBuilder &builder, Location loc, Value vector)
Returns the value obtained by reducing the vector into a scalar using the operation kind associated w...
BroadcastableToResult
Models whether srcType can be broadcast to dstVectorType under the semantics of the vector....
IntegerType getVectorSubscriptType(Builder &builder)
Returns the integer type required for subscripts in the vector dialect.
Include the generated interface declarations.
AffineMap simplifyAffineMap(AffineMap map)
Simplifies an affine map by simplifying its underlying AffineExpr results.
bool matchPattern(Value value, const Pattern &pattern)
Entry point for matching a pattern over a Value.
std::optional< int64_t > getConstantIntValue(OpFoldResult ofr)
If ofr is a constant integer or an IntegerAttr, return the integer.
llvm::function_ref< void(Value, const ConstantIntRanges &)> SetIntRangeFn
The type of the setResultRanges callback provided to ops implementing InferIntRangeInterface.
SmallVector< int64_t > computeStrides(ArrayRef< int64_t > sizes)
bool isEqualConstantIntOrValue(OpFoldResult ofr1, OpFoldResult ofr2)
Return true if ofr1 and ofr2 are the same integer constant attribute values or the same SSA value.
Type getType(OpFoldResult ofr)
Returns the int type of the integer in ofr.
SmallVector< T > applyPermutation(ArrayRef< T > input, ArrayRef< int64_t > permutation)
SmallVector< int64_t > delinearize(int64_t linearIndex, ArrayRef< int64_t > strides)
Given the strides together with a linear index in the dimension space, return the vector-space offset...
LogicalResult emitOptionalError(std::optional< Location > loc, Args &&...args)
Overloads of the above emission functions that take an optionally null location.
llvm::DenseSet< ValueT, ValueInfoT > DenseSet
InFlightDiagnostic emitError(Location loc)
Utility method to emit an error message using this location.
AffineMap inversePermutation(AffineMap map)
Returns a map of codomain to domain dimensions such that the first codomain dimension for a particula...
StorageUniquer::StorageAllocator AttributeStorageAllocator
Type getElementTypeOrSelf(Type type)
Return the element type or return the type itself.
SmallVector< int64_t > getI64SubArray(ArrayAttr arrayAttr, unsigned dropFront=0, unsigned dropBack=0)
Helper to return a subset of arrayAttr as a vector of int64_t.
std::conditional_t< std::is_same_v< Ty, mlir::Type >, mlir::Value, detail::TypedValue< Ty > > TypedValue
If Ty is mlir::Type this will select Value instead of having a wrapper around it.
void dispatchIndexOpFoldResults(ArrayRef< OpFoldResult > ofrs, SmallVectorImpl< Value > &dynamicVec, SmallVectorImpl< int64_t > &staticVec)
Helper function to dispatch multiple OpFoldResults according to the behavior of dispatchIndexOpFoldRe...
AffineMap compressUnusedDims(AffineMap map)
Drop the dims that are not used.
Value getValueOrCreateConstantIndexOp(OpBuilder &b, Location loc, OpFoldResult ofr)
Converts an OpFoldResult to a Value.
AffineExpr getAffineConstantExpr(int64_t constant, MLIRContext *context)
LogicalResult verifyElementTypesMatch(Operation *op, ShapedType lhs, ShapedType rhs, StringRef lhsName, StringRef rhsName)
Verify that two shaped types have matching element types.
llvm::DenseMap< KeyT, ValueT, KeyInfoT, BucketT > DenseMap
SmallVector< T > applyPermutationMap(AffineMap map, llvm::ArrayRef< T > source)
Apply a permutation from map to source and return the result.
llvm::SmallBitVector getUnusedDimsBitVector(ArrayRef< AffineMap > maps)
int64_t linearize(ArrayRef< int64_t > offsets, ArrayRef< int64_t > basis)
Return the linearized index of 'offsets' w.r.t.
detail::constant_op_matcher m_Constant()
Matches a constant foldable operation.
void applyPermutationToVector(SmallVector< T, N > &inVec, ArrayRef< int64_t > permutation)
Apply the permutation defined by permutation to inVec.
AffineExpr getAffineDimExpr(unsigned position, MLIRContext *context)
These free functions allow clients of the API to not use classes in detail.
llvm::function_ref< Fn > function_ref
std::pair< SmallVector< int64_t >, SmallVector< Value > > decomposeMixedValues(ArrayRef< OpFoldResult > mixedValues)
Decompose a vector of mixed static or dynamic values into the corresponding pair of arrays.
SmallVector< int64_t > invertPermutationVector(ArrayRef< int64_t > permutation)
Helper method to apply to inverse a permutation.
Return a fused vector::ContractionOp which represents a patterns such as:
LogicalResult matchAndRewrite(AddOpType addOp, PatternRewriter &rewriter) const override
Canonicalize vector.to_elements(vector.broadcast(v)) where v is a vector.
LogicalResult matchAndRewrite(ToElementsOp toElementsOp, PatternRewriter &rewriter) const override
This is the representation of an operand reference.
OpRewritePattern is a wrapper around RewritePattern that allows for matching and rewriting against an...
OpRewritePattern Base
Type alias to allow derived classes to inherit constructors with using Base::Base;.
OpRewritePattern(MLIRContext *context, PatternBenefit benefit=1, ArrayRef< StringRef > generatedNames={})
This represents an operation in an abstracted form, suitable for use with the builder APIs.
static BitmaskEnumStorage * construct(AttributeStorageAllocator &allocator, const KeyTy &key)
bool operator==(const KeyTy &key) const
BitmaskEnumStorage(KeyTy val)