41#include "llvm/ADT/DenseMap.h"
42#include "llvm/ADT/STLExtras.h"
43#include "llvm/ADT/SetOperations.h"
44#include "llvm/ADT/SmallVector.h"
45#include "llvm/ADT/SmallVectorExtras.h"
46#include "llvm/ADT/StringSet.h"
47#include "llvm/ADT/TypeSwitch.h"
48#include "llvm/Support/FormatVariadic.h"
49#include "llvm/Support/InterleavedRange.h"
50#include "llvm/Support/LogicalResult.h"
51#include "llvm/Support/MathExtras.h"
52#include "llvm/Support/raw_ostream.h"
62 auto type = cast<ShapedType>(v.
getType());
63 if (!type.isDynamicDim(dim))
68 .Case([&](RankedTensorType t) ->
Value {
69 return tensor::DimOp::create(builder, loc, v, dim);
71 .Case([&](MemRefType t) ->
Value {
72 return memref::DimOp::create(builder, loc, v, dim);
83 .Case([&](RankedTensorType t) ->
Operation * {
84 return tensor::ExtractSliceOp::create(
b, loc, source, offsets, sizes,
87 .Case([&](MemRefType type) ->
Operation * {
88 return memref::SubViewOp::create(
b, loc, source, offsets, sizes,
94static std::optional<TypedAttr>
98 if (!splatAttr || !splatAttr.
isSplat())
110 if (llvm::isa<UnrankedMemRefType, MemRefType>(source.
getType()))
111 return b.createOrFold<memref::DimOp>(loc, source, dim);
112 if (llvm::isa<UnrankedTensorType, RankedTensorType>(source.
getType()))
113 return b.createOrFold<tensor::DimOp>(loc, source, dim);
114 llvm_unreachable(
"Expected MemRefType or TensorType");
119 auto shapedType = llvm::cast<ShapedType>(source.
getType());
120 if (!shapedType.hasRank() || shapedType.isDynamicDim(dim))
122 return b.getIndexAttr(shapedType.getDimSize(dim));
145 for (
auto containers : {inputTypes, outputTypes}) {
146 for (
auto t : containers) {
158 opBuilder.
createBlock(®ion, {}, argTypes, argLocs);
174 std::optional<TypeRange> resultTensorTypes,
181 if (!resultTensorTypes)
182 copy_if(outputs.
getTypes(), std::back_inserter(derivedResultTypes),
183 llvm::IsaPred<RankedTensorType>);
191 "operandSegmentSizes",
192 b.getDenseI32ArrayAttr({static_cast<int32_t>(inputs.size()),
193 static_cast<int32_t>(outputs.size())}));
203 std::optional<TypeRange> resultTensorTypes,
210 return attr.
getName() ==
"indexing_maps";
213 indexingMapsAttrVal = llvm::map_to_vector(
216 state.
addAttribute(
"indexing_maps",
b.getArrayAttr(indexingMapsAttrVal));
219 attributes, regionBuilder);
223 std::optional<TypeRange> resultTensorTypes,
230 return attr.
getName() ==
"indexing_maps";
233 indexingMapsAttrVal = llvm::map_to_vector(
236 state.
addAttribute(
"indexing_maps",
b.getArrayAttr(indexingMapsAttrVal));
239 attributes, regionBuilder);
243 std::optional<TypeRange> resultTensorTypes,
250 indexingMapsAttrVal =
252 return AffineMapAttr::get(map);
254 state.
addAttribute(
"indexing_maps",
b.getArrayAttr(indexingMapsAttrVal));
256 attributes, regionBuilder);
265 bool addOperandSegmentSizes =
true) {
266 SMLoc attrsLoc, inputsOperandsLoc, outputsOperandsLoc;
295 if (parser.
resolveOperands(inputsOperands, inputTypes, inputsOperandsLoc,
297 parser.
resolveOperands(outputsOperands, outputTypes, outputsOperandsLoc,
301 if (addOperandSegmentSizes) {
308 if (
result.propertiesAttr) {
310 attrs.
append(
"operandSegmentSizes",
312 {static_cast<int32_t>(inputsOperands.size()),
313 static_cast<int32_t>(outputsOperands.size())}));
316 result.addAttribute(
"operandSegmentSizes",
318 {static_cast<int32_t>(inputsOperands.size()),
319 static_cast<int32_t>(outputsOperands.size())}));
322 if (!
result.propertiesAttr) {
323 std::optional<RegisteredOperationName> info =
324 result.name.getRegisteredInfo();
326 if (failed(info->verifyInherentAttrs(
result.attributes, [&]() {
327 return parser.emitError(attrsLoc)
328 <<
"'" << result.name.getStringRef() <<
"' op ";
339 p <<
" ins(" << inputs <<
" : " << inputs.
getTypes() <<
")";
340 if (!outputs.empty())
341 p <<
" outs(" << outputs <<
" : " << outputs.
getTypes() <<
")";
352 if (numRegionArgs != inputTypes.size() + outputTypes.size()) {
355 llvm::formatv(
"[parseNamedStructuredOpRegion] ods-gen generated "
356 "region expects {0} args, got {1}",
357 numRegionArgs, inputTypes.size() + outputTypes.size()));
363 opBuilder, region, inputTypes, outputTypes, attrs,
382 unsigned numRegionArgs,
399 result.addTypes(outputTensorsTypes);
401 std::unique_ptr<Region> region = std::make_unique<Region>();
403 outputTypes,
result.attributes.getAttrs(),
406 result.addRegion(std::move(region));
413 if (resultTypes.empty())
458class RegionBuilderHelper {
460 RegionBuilderHelper(OpBuilder &builder,
Block &block)
461 : builder(builder), block(block) {}
464 Value buildUnaryFn(UnaryFn unaryFn, Value arg,
466 if (!isFloatingPoint(arg)) {
468 emitError() <<
"unsupported non numeric type";
471 llvm_unreachable(
"unsupported non numeric type");
473 OpBuilder::InsertionGuard g(builder);
474 builder.setInsertionPointToEnd(&block);
477 return math::ExpOp::create(builder, arg.
getLoc(), arg);
479 return math::LogOp::create(builder, arg.
getLoc(), arg);
481 return math::AbsFOp::create(builder, arg.
getLoc(), arg);
483 return math::CeilOp::create(builder, arg.
getLoc(), arg);
485 return math::FloorOp::create(builder, arg.
getLoc(), arg);
487 return arith::NegFOp::create(builder, arg.
getLoc(), arg);
488 case UnaryFn::reciprocal: {
489 Attribute oneAttr = builder.getOneAttr(arg.
getType());
490 auto one = arith::ConstantOp::create(builder, arg.
getLoc(),
491 ::cast<TypedAttr>(oneAttr));
492 return arith::DivFOp::create(builder, arg.
getLoc(), one, arg);
495 return math::RoundOp::create(builder, arg.
getLoc(), arg);
497 return math::SqrtOp::create(builder, arg.
getLoc(), arg);
499 return math::RsqrtOp::create(builder, arg.
getLoc(), arg);
500 case UnaryFn::square:
501 return arith::MulFOp::create(builder, arg.
getLoc(), arg, arg);
503 return math::TanhOp::create(builder, arg.
getLoc(), arg);
505 return math::ErfOp::create(builder, arg.
getLoc(), arg);
507 return math::SinOp::create(builder, arg.
getLoc(), arg);
509 return math::CosOp::create(builder, arg.
getLoc(), arg);
511 return math::TanOp::create(builder, arg.
getLoc(), arg);
513 return math::AcosOp::create(builder, arg.
getLoc(), arg);
515 return math::AcoshOp::create(builder, arg.
getLoc(), arg);
517 return math::AsinOp::create(builder, arg.
getLoc(), arg);
519 return math::AsinhOp::create(builder, arg.
getLoc(), arg);
521 return math::AtanOp::create(builder, arg.
getLoc(), arg);
523 return math::AtanhOp::create(builder, arg.
getLoc(), arg);
525 return math::Log10Op::create(builder, arg.
getLoc(), arg);
527 return math::Log1pOp::create(builder, arg.
getLoc(), arg);
529 return math::Log2Op::create(builder, arg.
getLoc(), arg);
532 emitError() <<
"unsupported unary function";
535 llvm_unreachable(
"unsupported unary function");
542 Value buildBinaryFn(BinaryFn binaryFn, Value arg0, Value arg1,
544 bool allComplex = isComplex(arg0) && isComplex(arg1);
545 bool allFloatingPoint = isFloatingPoint(arg0) && isFloatingPoint(arg1);
546 bool allInteger = isInteger(arg0) && isInteger(arg1);
549 if (!allComplex && !allFloatingPoint && !allInteger) {
552 <<
"Cannot build binary Linalg operation: expects allComplex, "
553 "allFloatingPoint, or allInteger, got "
557 llvm_unreachable(
"unsupported non numeric type");
559 OpBuilder::InsertionGuard g(builder);
560 builder.setInsertionPointToEnd(&block);
564 return complex::AddOp::create(builder, arg0.
getLoc(), arg0, arg1);
565 if (allFloatingPoint)
566 return arith::AddFOp::create(builder, arg0.
getLoc(), arg0, arg1);
568 return arith::OrIOp::create(builder, arg0.
getLoc(), arg0, arg1);
569 return arith::AddIOp::create(builder, arg0.
getLoc(), arg0, arg1);
572 return complex::SubOp::create(builder, arg0.
getLoc(), arg0, arg1);
573 if (allFloatingPoint)
574 return arith::SubFOp::create(builder, arg0.
getLoc(), arg0, arg1);
577 emitError() <<
"unsupported operation: sub with bools";
580 llvm_unreachable(
"unsupported operation: sub with bools");
582 return arith::SubIOp::create(builder, arg0.
getLoc(), arg0, arg1);
585 return complex::MulOp::create(builder, arg0.
getLoc(), arg0, arg1);
586 if (allFloatingPoint)
587 return arith::MulFOp::create(builder, arg0.
getLoc(), arg0, arg1);
589 return arith::AndIOp::create(builder, arg0.
getLoc(), arg0, arg1);
590 return arith::MulIOp::create(builder, arg0.
getLoc(), arg0, arg1);
593 return complex::DivOp::create(builder, arg0.
getLoc(), arg0, arg1);
594 if (allFloatingPoint)
595 return arith::DivFOp::create(builder, arg0.
getLoc(), arg0, arg1);
598 emitError() <<
"unsupported operation: div with bools";
601 llvm_unreachable(
"unsupported operation: div with bools");
603 return arith::DivSIOp::create(builder, arg0.
getLoc(), arg0, arg1);
604 case BinaryFn::div_unsigned:
605 if (!allInteger || allBool) {
607 emitError() <<
"unsupported operation: unsigned div not on uint";
610 llvm_unreachable(
"unsupported operation: unsigned div not on uint");
612 return arith::DivUIOp::create(builder, arg0.
getLoc(), arg0, arg1);
613 case BinaryFn::max_signed:
615 if (allFloatingPoint)
616 return arith::MaximumFOp::create(builder, arg0.
getLoc(), arg0, arg1);
617 return arith::MaxSIOp::create(builder, arg0.
getLoc(), arg0, arg1);
618 case BinaryFn::min_signed:
620 if (allFloatingPoint)
621 return arith::MinimumFOp::create(builder, arg0.
getLoc(), arg0, arg1);
622 return arith::MinSIOp::create(builder, arg0.
getLoc(), arg0, arg1);
623 case BinaryFn::max_unsigned:
625 if (!allInteger || allBool) {
627 emitError() <<
"unsupported operation: unsigned max not on uint";
630 llvm_unreachable(
"unsupported operation: unsigned max not on uint");
632 return arith::MaxUIOp::create(builder, arg0.
getLoc(), arg0, arg1);
633 case BinaryFn::min_unsigned:
635 if (!allInteger || allBool) {
637 emitError() <<
"unsupported operation: unsigned min not on uint";
640 llvm_unreachable(
"unsupported operation: unsigned min not on uint");
642 return arith::MinUIOp::create(builder, arg0.
getLoc(), arg0, arg1);
644 assert(allFloatingPoint);
645 return math::PowFOp::create(builder, arg0.
getLoc(), arg0, arg1);
648 emitError() <<
"unsupported binary function";
651 llvm_unreachable(
"unsupported binary function");
655 Value buildTernaryFn(TernaryFn ternaryFn, Value arg0, Value arg1, Value arg2,
657 OpBuilder::InsertionGuard g(builder);
658 builder.setInsertionPointToEnd(&block);
660 case TernaryFn::select:
661 return arith::SelectOp::create(builder, arg0.
getLoc(), arg0, arg1, arg2);
664 emitError() <<
"unsupported ternary function";
667 llvm_unreachable(
"unsupported ternary function");
671 Value buildTypeFn(TypeFn typeFn, Type toType, Value operand,
674 case TypeFn::cast_signed:
675 return cast(toType, operand,
false);
676 case TypeFn::cast_unsigned:
677 return cast(toType, operand,
true);
680 emitError() <<
"unsupported type conversion function";
683 llvm_unreachable(
"unsupported type conversion function");
687 OpBuilder::InsertionGuard g(builder);
688 builder.setInsertionPointToEnd(&block);
689 Location loc = builder.getUnknownLoc();
690 YieldOp::create(builder, loc, values);
693 Value constant(
const std::string &value) {
694 OpBuilder::InsertionGuard g(builder);
695 builder.setInsertionPointToEnd(&block);
696 Location loc = builder.getUnknownLoc();
697 Attribute valueAttr =
parseAttribute(value, builder.getContext());
698 return arith::ConstantOp::create(builder, loc,
699 ::cast<TypedAttr>(valueAttr));
702 Value index(int64_t dim) {
703 OpBuilder::InsertionGuard g(builder);
704 builder.setInsertionPointToEnd(&block);
705 return IndexOp::create(builder, builder.getUnknownLoc(), dim);
708 Type getIntegerType(
unsigned width) {
709 return IntegerType::get(builder.getContext(), width);
712 Type getFloat32Type() {
return Float32Type::get(builder.getContext()); }
713 Type getFloat64Type() {
return Float64Type::get(builder.getContext()); }
720 Value cast(Type toType, Value operand,
bool isUnsignedCast) {
721 OpBuilder::InsertionGuard g(builder);
722 builder.setInsertionPointToEnd(&block);
723 auto loc = operand.
getLoc();
724 if (isa<UnknownLoc>(loc)) {
734 bool isComplex(Value value) {
735 return llvm::isa<ComplexType>(value.
getType());
737 bool isFloatingPoint(Value value) {
738 return llvm::isa<FloatType>(value.
getType());
740 bool isInteger(Value value) {
741 return llvm::isa<IntegerType>(value.
getType());
757 using OpRewritePattern<CopyOp>::OpRewritePattern;
758 LogicalResult matchAndRewrite(CopyOp copyOp,
759 PatternRewriter &rewriter)
const override {
760 if (copyOp.getInputs() != copyOp.getOutputs())
762 if (copyOp.hasPureBufferSemantics())
765 rewriter.
replaceOp(copyOp, copyOp.getInputs());
775 results.
add<EraseSelfCopy>(context);
788template <
typename TensorReshapeOp>
789struct FoldFillWithTensorReshape : OpRewritePattern<TensorReshapeOp> {
790 using OpRewritePattern<TensorReshapeOp>::OpRewritePattern;
791 LogicalResult matchAndRewrite(TensorReshapeOp reshapeOp,
792 PatternRewriter &rewriter)
const override {
793 auto oldFill = reshapeOp.getSrc().template getDefiningOp<FillOp>();
797 Location loc = oldFill.getLoc();
798 TensorReshapeOp newInit;
799 if constexpr (std::is_same<TensorReshapeOp, tensor::ExpandShapeOp>::value) {
801 newInit = TensorReshapeOp::create(
802 rewriter, loc, reshapeOp.getResultType(), oldFill.output(),
803 reshapeOp.getReassociation(), reshapeOp.getOutputShape(),
804 reshapeOp.getStaticOutputShape());
806 newInit = TensorReshapeOp::create(
807 rewriter, loc, reshapeOp.getResultType(), oldFill.output(),
808 reshapeOp.getReassociation());
818struct FoldFillWithPad final :
public OpRewritePattern<tensor::PadOp> {
821 LogicalResult matchAndRewrite(tensor::PadOp padOp,
822 PatternRewriter &rewriter)
const override {
823 auto fillOp = padOp.getSource().getDefiningOp<linalg::FillOp>();
829 Value padValue = padOp.getConstantPaddingValue();
830 if (!padValue || fillOp.value() != padValue)
836 padOp,
"failed to reify tensor.pad op result shape");
839 tensor::EmptyOp::create(rewriter, padOp.getLoc(), reifiedShape.front(),
840 padOp.getResultType().getElementType());
842 FillOp::create(rewriter, fillOp.getLoc(),
ValueRange{padValue},
845 if (
replacement.getType() != padOp.getResultType()) {
846 replacement = tensor::CastOp::create(rewriter, fillOp.getLoc(),
857struct FoldInsertPadIntoFill :
public OpRewritePattern<tensor::InsertSliceOp> {
860 LogicalResult matchAndRewrite(tensor::InsertSliceOp insertOp,
861 PatternRewriter &rewriter)
const override {
862 auto srcPadOp = insertOp.getSource().getDefiningOp<tensor::PadOp>();
866 if (insertOp.getType().getRank() != insertOp.getSourceType().getRank())
871 Value firstDest = insertOp.getDest();
872 while (
auto prevOp = firstDest.
getDefiningOp<tensor::InsertSliceOp>()) {
873 if (prevOp.getType().getRank() != prevOp.getSourceType().getRank())
878 bool disjoint =
false;
879 for (
int i = 0, e = prevOp.getType().getRank(); i < e; ++i) {
882 if (insertOp.isDynamicOffset(i) || insertOp.isDynamicSize(i) ||
883 insertOp.isDynamicStride(i) || prevOp.isDynamicOffset(i) ||
884 prevOp.isDynamicSize(i) || prevOp.isDynamicStride(i))
888 int64_t prevStart = prevOp.getStaticOffset(i);
889 int64_t prevEnd = prevStart + (prevOp.getStaticSize(i) - 1) *
890 prevOp.getStaticStride(i);
891 int64_t nextStart = insertOp.getStaticOffset(i);
892 int64_t nextEnd = nextStart + (insertOp.getStaticSize(i) - 1) *
893 insertOp.getStaticStride(i);
894 if (prevEnd < nextStart || nextEnd < prevStart) {
902 firstDest = prevOp.getDest();
913 Value padValue = srcPadOp.getConstantPaddingValue();
914 if (!padValue || dstFillOp.value() != padValue)
917 SmallVector<OpFoldResult> lowPads = srcPadOp.getMixedLowPad();
918 SmallVector<OpFoldResult> oldOffsets = insertOp.getMixedOffsets();
920 Location loc = insertOp.getLoc();
923 AffineExpr sym0, sym1;
929 SmallVector<OpFoldResult, 4> newOffsets;
930 for (
const auto &p : llvm::zip(lowPads, oldOffsets)) {
932 rewriter, loc, addMap, {std::get<0>(p), std::get<1>(p)}));
935 RankedTensorType srcPadType = srcPadOp.getSourceType();
936 SmallVector<OpFoldResult, 4> newSizes;
937 for (
int i = 0, e = srcPadType.getRank(); i < e; ++i) {
938 if (srcPadType.isDynamicDim(i)) {
940 tensor::DimOp::create(rewriter, loc, srcPadOp.getSource(), i)
943 newSizes.push_back(rewriter.
getIndexAttr(srcPadType.getDimSize(i)));
948 insertOp, srcPadOp.getSource(), insertOp.getDest(), newOffsets,
949 newSizes, insertOp.getMixedStrides());
955struct FoldFillWithTensorExtract :
public OpRewritePattern<tensor::ExtractOp> {
957 using OpRewritePattern<tensor::ExtractOp>::OpRewritePattern;
959 LogicalResult matchAndRewrite(tensor::ExtractOp extractOp,
960 PatternRewriter &rewriter)
const override {
963 auto fillOp = extractOp.getTensor().getDefiningOp<linalg::FillOp>();
968 Value extractedScalar = fillOp.getInputs()[0];
971 rewriter.
replaceOp(extractOp, extractedScalar);
979static FailureOr<FillOp> foldFillPackIntoFillOp(RewriterBase &rewriter,
980 linalg::PackOp packOp) {
981 auto fillOp = packOp.getSource().getDefiningOp<FillOp>();
985 if (
auto paddingValue = packOp.getPaddingValue())
989 Value packOpDest = packOp.getDest();
993 return linalg::FillOp::create(rewriter, packOp.getLoc(), fillOp.getInputs(),
998struct FoldFillWithPack :
public OpRewritePattern<linalg::PackOp> {
1000 FoldFillWithPack(MLIRContext *context)
1001 : OpRewritePattern<linalg::PackOp>(context) {}
1003 LogicalResult matchAndRewrite(linalg::PackOp packOp,
1004 PatternRewriter &rewriter)
const override {
1005 auto fillOp = foldFillPackIntoFillOp(rewriter, packOp);
1008 rewriter.
replaceOp(packOp, fillOp.value().result());
1014struct FoldFillWithCopy : OpRewritePattern<linalg::CopyOp> {
1015 using OpRewritePattern<linalg::CopyOp>::OpRewritePattern;
1017 LogicalResult matchAndRewrite(linalg::CopyOp copyOp,
1018 PatternRewriter &rewriter)
const override {
1019 if (
auto fillOp = copyOp.getInputs().front().getDefiningOp<FillOp>()) {
1022 copyOp.getOutputs());
1025 if (
auto fillOp = copyOp.getOutputs().front().getDefiningOp<FillOp>()) {
1027 fillOp.getOutputs());
1035struct FoldFillWithTranspose : OpRewritePattern<linalg::TransposeOp> {
1036 using OpRewritePattern<linalg::TransposeOp>::OpRewritePattern;
1038 LogicalResult matchAndRewrite(linalg::TransposeOp transposeOp,
1039 PatternRewriter &rewriter)
const override {
1040 if (
auto fillOp = transposeOp.getInput().getDefiningOp<FillOp>()) {
1042 transposeOp, transposeOp.getResultTypes(), fillOp.getInputs(),
1043 transposeOp.getDpsInitOperand(0)->get());
1052struct FoldConcatsOfFill :
public OpRewritePattern<tensor::ConcatOp> {
1055 LogicalResult matchAndRewrite(tensor::ConcatOp concatOp,
1056 PatternRewriter &rewriter)
const override {
1057 auto concatOperands = concatOp.getInputs();
1058 if (concatOperands.empty()) {
1062 auto firstFillOp = concatOperands.front().getDefiningOp<linalg::FillOp>();
1067 OpFoldResult firstFillVal =
1070 SmallVector<Value> allOuts;
1071 allOuts.push_back(firstFillOp.getDpsInitOperand(0)->get());
1073 auto isDefinedByCompatibleFillOp = [&](Value v) ->
bool {
1074 auto fillOp = v.getDefiningOp<linalg::FillOp>();
1079 OpFoldResult fillVal =
1081 if (fillVal != firstFillVal)
1084 allOuts.push_back(fillOp.getDpsInitOperand(0)->get());
1087 if (!llvm::all_of(concatOperands.drop_front(),
1088 isDefinedByCompatibleFillOp)) {
1090 concatOp,
"not all operands are defined by a compatible fill op");
1093 Value outsConcat = tensor::ConcatOp::create(rewriter, concatOp.getLoc(),
1094 concatOp.getDim(), allOuts);
1096 concatOp, firstFillOp.getDpsInputOperand(0)->
get(), outsConcat);
1103void FillOp::getCanonicalizationPatterns(RewritePatternSet &results,
1104 MLIRContext *context) {
1105 results.
add<FoldConcatsOfFill, FoldFillWithCopy, FoldFillWithTensorExtract,
1106 FoldFillWithPack, FoldFillWithPad,
1107 FoldFillWithTensorReshape<tensor::CollapseShapeOp>,
1108 FoldFillWithTensorReshape<tensor::ExpandShapeOp>,
1109 FoldInsertPadIntoFill, FoldFillWithTranspose>(context);
1122 for (
ValueRange container : {inputs, outputs}) {
1123 for (
Value v : container) {
1124 Type t = v.getType();
1125 blockArgTypes.push_back(
1127 blockArgLocs.push_back(v.getLoc());
1133 builder.
createBlock(®ion, region.
end(), blockArgTypes, blockArgLocs);
1137void GenericOp::getAsmBlockArgumentNames(Region ®ion,
1139 for (Value v : getRegionInputArgs())
1141 for (Value v : getRegionOutputArgs())
1142 setNameFn(v,
"out");
1145void GenericOp::build(
1146 OpBuilder &builder, OperationState &
result,
TypeRange resultTensorTypes,
1148 ArrayAttr iteratorTypes, StringAttr doc, StringAttr libraryCall,
1150 ArrayRef<NamedAttribute> attributes) {
1151 build(builder,
result, resultTensorTypes, inputs, outputs, indexingMaps,
1152 iteratorTypes, doc, libraryCall);
1153 result.addAttributes(attributes);
1156 inputs, outputs, bodyBuild);
1159void GenericOp::build(
1160 OpBuilder &builder, OperationState &
result,
TypeRange resultTensorTypes,
1162 ArrayRef<utils::IteratorType> iteratorTypes, StringRef doc,
1163 StringRef libraryCall,
1165 ArrayRef<NamedAttribute> attributes) {
1166 build(builder,
result, resultTensorTypes, inputs, outputs,
1170 [&](utils::IteratorType iter) -> mlir::Attribute {
1171 return IteratorTypeAttr::get(builder.getContext(), iter);
1174 libraryCall.empty() ? StringAttr() : builder.
getStringAttr(libraryCall),
1175 bodyBuild, attributes);
1178void GenericOp::build(
1180 ValueRange outputs, ArrayRef<AffineMap> indexingMaps,
1181 ArrayRef<utils::IteratorType> iteratorTypes, StringRef doc,
1182 StringRef libraryCall,
1184 ArrayRef<NamedAttribute> attributes) {
1186 iteratorTypes, doc, libraryCall, bodyBuild, attributes);
1189void GenericOp::build(
1191 ValueRange outputs, ArrayRef<AffineMap> indexingMaps,
1192 ArrayRef<utils::IteratorType> iteratorTypes,
1194 ArrayRef<NamedAttribute> attributes) {
1195 build(builder,
result, inputs, outputs, indexingMaps, iteratorTypes,
1197 "", bodyBuild, attributes);
1200void GenericOp::build(
1201 OpBuilder &builder, OperationState &
result,
TypeRange resultTensorTypes,
1203 ArrayRef<utils::IteratorType> iteratorTypes,
1205 ArrayRef<NamedAttribute> attributes) {
1206 build(builder,
result, resultTensorTypes, inputs, outputs, indexingMaps,
1209 "", bodyBuild, attributes);
1212void GenericOp::print(OpAsmPrinter &p) {
1216 auto genericAttrNames = linalgTraitAttrNames();
1218 llvm::StringSet<> genericAttrNamesSet;
1219 genericAttrNamesSet.insert_range(genericAttrNames);
1220 SmallVector<NamedAttribute, 8> genericAttrs;
1221 for (
auto attr : (*this)->getAttrs()) {
1222 if (attr.getName() == getIteratorTypesAttrName()) {
1223 auto iteratorTypes =
1224 llvm::cast<ArrayAttr>(attr.getValue())
1225 .getAsValueRange<IteratorTypeAttr, utils::IteratorType>();
1230 SmallVector<Attribute> iteratorTypeNames = llvm::map_to_vector(
1231 iteratorTypes, [&](utils::IteratorType t) -> Attribute {
1232 return StringAttr::get(
getContext(), stringifyIteratorType(t));
1235 genericAttrs.emplace_back(
1236 getIteratorTypesAttrName(),
1237 ArrayAttr::get(
getContext(), iteratorTypeNames));
1238 }
else if (genericAttrNamesSet.count(attr.getName().strref()) > 0) {
1239 genericAttrs.push_back(attr);
1242 if (!genericAttrs.empty()) {
1243 auto genericDictAttr = DictionaryAttr::get(
getContext(), genericAttrs);
1244 p << genericDictAttr;
1250 genericAttrNames.push_back(
"operandSegmentSizes");
1251 genericAttrNamesSet.insert(genericAttrNames.back());
1253 bool hasExtraAttrs =
false;
1254 for (NamedAttribute n : (*this)->getAttrs()) {
1255 if ((hasExtraAttrs = !genericAttrNamesSet.contains(n.getName().strref())))
1258 if (hasExtraAttrs) {
1265 if (!getRegion().empty()) {
1274ParseResult GenericOp::parse(OpAsmParser &parser, OperationState &
result) {
1275 DictionaryAttr dictAttr;
1283 result.attributes.assign(dictAttr.getValue().begin(),
1284 dictAttr.getValue().end());
1290 auto iteratorTypes = dyn_cast_or_null<ArrayAttr>(
1291 result.attributes.get(getIteratorTypesAttrName(
result.name)));
1292 if (!iteratorTypes) {
1293 return parser.
emitError(attributeLocation)
1294 <<
"expected " << getIteratorTypesAttrName(
result.name)
1295 <<
" array attribute";
1298 SmallVector<Attribute> iteratorTypeAttrs;
1300 for (StringRef s : iteratorTypes.getAsValueRange<StringAttr>()) {
1301 auto maybeIteratorType = utils::symbolizeIteratorType(s);
1302 if (!maybeIteratorType.has_value())
1304 <<
"unexpected iterator_type (" << s <<
")";
1306 iteratorTypeAttrs.push_back(
1307 IteratorTypeAttr::get(parser.
getContext(), maybeIteratorType.value()));
1309 result.attributes.set(getIteratorTypesAttrName(
result.name),
1313 SmallVector<Type, 1> inputTypes, outputTypes;
1323 std::unique_ptr<Region> region = std::make_unique<Region>();
1326 result.addRegion(std::move(region));
1332 SmallVector<Type, 1> outputTensorsTypes;
1335 result.addTypes(outputTensorsTypes);
1343 LinalgOp linalgOp) {
1344 for (
auto [
index, operand] : llvm::enumerate(linalgOp.getDpsInputs())) {
1345 if (!llvm::isa<MemRefType>(operand.
getType()))
1347 effects.emplace_back(
1352 for (
OpOperand &operand : linalgOp.getDpsInitsMutable()) {
1353 if (!llvm::isa<MemRefType>(operand.get().
getType()))
1355 if (linalgOp.payloadUsesValueFromOperand(&operand)) {
1366void GenericOp::getEffects(
1367 SmallVectorImpl<SideEffects::EffectInstance<MemoryEffects::Effect>>
1376 if (!linalgOp.hasPureTensorSemantics())
1394template <
typename OpTy>
1395struct EraseIdentityLinalgOp :
public OpRewritePattern<OpTy> {
1396 using OpRewritePattern<OpTy>::OpRewritePattern;
1398 LogicalResult matchAndRewrite(OpTy linalgOp,
1399 PatternRewriter &rewriter)
const override {
1401 if (!llvm::all_equal(linalgOp.getIndexingMapsArray()))
1406 Block &body = linalgOp->getRegion(0).front();
1407 if (!llvm::hasSingleElement(body))
1409 auto yieldOp = dyn_cast<linalg::YieldOp>(body.
getTerminator());
1414 if (linalgOp.hasPureBufferSemantics()) {
1415 if (linalgOp.getNumDpsInputs() != 1 || linalgOp.getNumDpsInits() != 1 ||
1416 linalgOp.getDpsInputOperand(0)->get() !=
1417 linalgOp.getDpsInitOperand(0)->get()) {
1419 linalgOp,
"expected single input and output to be the same value");
1422 auto yieldArg = dyn_cast<BlockArgument>(yieldOp.getOperand(0));
1423 if (!yieldArg || yieldArg.getOwner() != &body) {
1425 "cannot fold fill-like op");
1432 if (!linalgOp.hasPureTensorSemantics()) {
1434 linalgOp,
"mixed semantics is not supported yet");
1439 SmallVector<Value> returnedArgs;
1440 for (
const auto &yieldVal : llvm::enumerate(yieldOp.getValues())) {
1441 auto yieldArg = llvm::dyn_cast<BlockArgument>(yieldVal.value());
1442 if (!yieldArg || yieldArg.getOwner() != &body)
1444 unsigned argumentNumber = yieldArg.getArgNumber();
1445 Value returnedArg = linalgOp->getOperand(argumentNumber);
1446 Type resultType = linalgOp->getResult(yieldVal.index()).getType();
1449 Type returnType = returnedArg.
getType();
1450 if (returnType != resultType) {
1455 returnedArg = sparse_tensor::ConvertOp::create(
1456 rewriter, linalgOp.getLoc(), resultType, returnedArg);
1458 if (!tensor::CastOp::areCastCompatible(returnedArg.
getType(),
1461 returnedArg = tensor::CastOp::create(rewriter, linalgOp.getLoc(),
1462 resultType, returnedArg);
1465 returnedArgs.push_back(returnedArg);
1468 if (returnedArgs.size() != linalgOp->getNumResults())
1470 rewriter.
replaceOp(linalgOp, returnedArgs);
1477void GenericOp::getCanonicalizationPatterns(RewritePatternSet &results,
1478 MLIRContext *context) {
1479 results.
add<EraseIdentityLinalgOp<GenericOp>>(context);
1482LogicalResult GenericOp::fold(FoldAdaptor, SmallVectorImpl<OpFoldResult> &) {
1501 for (
Type outputType : outputTypes) {
1502 if (llvm::isa<RankedTensorType>(outputType))
1503 result.addTypes(outputType);
1507 if (parseAttrsFn && failed(parseAttrsFn(parser,
result.attributes)))
1516void MapOp::getAsmBlockArgumentNames(Region ®ion,
1518 for (Value v : getRegionInputArgs())
1520 for (Value v : getRegionOutputArgs())
1521 setNameFn(v,
"init");
1524void MapOp::getAsmResultNames(
function_ref<
void(Value, StringRef)> setNameFn) {
1525 if (!getResults().empty())
1526 setNameFn(getResults().front(),
"mapped");
1532 ArrayRef<NamedAttribute> attributes) {
1534 result.addAttributes(attributes);
1537 Type initType = init.
getType();
1538 if (llvm::isa<RankedTensorType>(initType))
1539 result.addTypes(initType);
1543 inputs, {init}, bodyBuild);
1550 bool initFirst =
false,
bool mapInit =
true) {
1554 b.setInsertionPointToStart(&block);
1555 for (
auto &operand : operands) {
1557 llvm::cast<ShapedType>(operand.
getType()).getElementType(),
1565 payloadOpOperands.push_back(block.
getArguments().back());
1566 for (
const auto &arg : block.
getArguments().drop_back())
1567 payloadOpOperands.push_back(arg);
1576 TypeRange{llvm::cast<ShapedType>(result.operands.back().getType())
1582ParseResult MapOp::parse(OpAsmParser &parser, OperationState &
result) {
1583 std::optional<OperationName> payloadOpName;
1584 NamedAttrList payloadOpAttrs;
1587 if (
failed(operationName))
1591 payloadOpName = operationName.value();
1599 if (payloadOpName.has_value()) {
1600 if (!
result.operands.empty())
1602 payloadOpAttrs, ArrayRef(
result.operands),
false,
1607 SmallVector<OpAsmParser::Argument> regionArgs;
1612 Region *body =
result.addRegion();
1620 bool mapInit =
true) {
1622 if (initFirst && !mapInit)
1646 for (
const auto &[operand, bbArg] :
1648 if (bbArg != operand)
1652 for (
const auto &[operand, bbArg] :
1655 if (bbArg != operand)
1662 return yieldOp.getNumOperands() == 1 &&
1663 yieldOp.getOperand(0).getDefiningOp() &&
1664 yieldOp.getOperand(0).getDefiningOp() == &payload;
1669 std::string attrToElide;
1671 for (
const auto &attr : payloadOp->
getAttrs()) {
1673 llvm::dyn_cast<mlir::arith::FastMathFlagsAttr>(attr.getValue());
1674 if (fastAttr && fastAttr.getValue() == mlir::arith::FastMathFlags::none) {
1675 attrToElide = attr.getName().str();
1676 elidedAttrs.push_back(attrToElide);
1684void MapOp::print(OpAsmPrinter &p) {
1685 Block *mapper = getBody();
1695 if (!useShortForm) {
1701 [&](
auto arg) { p.printRegionArgument(arg); });
1709LogicalResult MapOp::verify() {
1710 auto *bodyBlock = getBody();
1711 auto blockArgs = bodyBlock->getArguments();
1715 if (getInputs().size() + 1 != blockArgs.size())
1716 return emitOpError() <<
"expects number of operands to match the arity of "
1718 << getInputs().size() + 1 <<
" and "
1719 << blockArgs.size();
1722 for (
const auto &[bbArgType, inputArg] :
1723 llvm::zip(bodyBlock->getArgumentTypes(), getInputs())) {
1724 auto inputElemType =
1725 llvm::cast<ShapedType>(inputArg.getType()).getElementType();
1726 if (bbArgType != inputElemType) {
1727 return emitOpError() <<
"expected element type of input " << inputElemType
1728 <<
" to match bbArg type " << bbArgType;
1733 auto outputShape = getInit().getType().getShape();
1734 for (Type inputArgType :
TypeRange{getInputs()}) {
1735 auto inputElemShape = llvm::cast<ShapedType>(inputArgType).getShape();
1736 if (inputElemShape != outputShape) {
1737 return emitOpError() <<
"expected shape of input (" << inputElemShape
1738 <<
") to match shape of output (" << outputShape
1746SmallVector<utils::IteratorType> MapOp::getIteratorTypesArray() {
1747 int64_t rank = getInit().getType().getRank();
1748 return SmallVector<utils::IteratorType>(rank, utils::IteratorType::parallel);
1753 int64_t rank = getInit().getType().getRank();
1754 int64_t numIndexingMaps = getOperands().size();
1759void MapOp::getEffects(
1760 SmallVectorImpl<SideEffects::EffectInstance<MemoryEffects::Effect>>
1773void ReduceOp::getAsmBlockArgumentNames(Region ®ion,
1775 for (Value v : getRegionInputArgs())
1777 for (Value v : getRegionOutputArgs())
1778 setNameFn(v,
"init");
1781void ReduceOp::getAsmResultNames(
1783 if (!getResults().empty())
1784 setNameFn(getResults().front(),
"reduced");
1787void ReduceOp::build(
1789 ValueRange inits, ArrayRef<int64_t> dimensions,
1791 ArrayRef<NamedAttribute> attributes) {
1793 result.addAttributes(attributes);
1796 for (Value init : inits) {
1797 Type initType = init.
getType();
1798 if (llvm::isa<RankedTensorType>(initType))
1799 result.addTypes(initType);
1804 inputs, inits, bodyBuild);
1807SmallVector<utils::IteratorType> ReduceOp::getIteratorTypesArray() {
1809 llvm::cast<ShapedType>(getInputs()[0].
getType()).getRank();
1810 SmallVector<utils::IteratorType> iteratorTypes(inputRank,
1811 utils::IteratorType::parallel);
1812 for (int64_t reductionDim : getDimensions())
1813 iteratorTypes[reductionDim] = utils::IteratorType::reduction;
1814 return iteratorTypes;
1819 llvm::cast<ShapedType>(getInputs()[0].
getType()).getRank();
1820 SmallVector<AffineMap> affineMaps(
1823 AffineMap resultMap =
1826 for (int64_t i = 0, e = getNumDpsInits(); i < e; ++i)
1827 affineMaps.push_back(resultMap);
1828 return Builder(
getContext()).getAffineMapArrayAttr(affineMaps);
1831void ReduceOp::getEffects(
1832 SmallVectorImpl<SideEffects::EffectInstance<MemoryEffects::Effect>>
1843 StringRef attributeName) {
1851ParseResult ReduceOp::parse(OpAsmParser &parser, OperationState &
result) {
1852 std::optional<OperationName> payloadOpName;
1853 NamedAttrList payloadOpAttrs;
1856 if (
failed(operationName))
1860 payloadOpName = operationName.value();
1866 parser,
result, [&](OpAsmParser &parser, NamedAttrList &attributes) {
1871 if (payloadOpName.has_value()) {
1873 ArrayRef(
result.operands),
true);
1875 SmallVector<OpAsmParser::Argument> regionArgs;
1881 Region *body =
result.addRegion();
1891 p <<
' ' << attributeName <<
" = [" << attributeValue <<
"] ";
1894void ReduceOp::print(OpAsmPrinter &p) {
1895 Block *mapper = getBody();
1904 if (!useShortForm) {
1910 [&](
auto arg) { p.printRegionArgument(arg); });
1918LogicalResult ReduceOp::verify() {
1919 ArrayRef<int64_t> dimensionsRef = getDimensions();
1926 if (getInputs().size() !=
static_cast<size_t>(getNumDpsInputs()))
1928 <<
"expected equal number of inputs and outputs (required by "
1929 "SameVariadicOperandSize), got "
1930 << getNumDpsInputs() <<
" input(s) and " << getNumDpsInits()
1933 if (getInputs().empty())
1934 return emitOpError() <<
"expected at least one input";
1936 for (int64_t i = 1; i < getNumDpsInputs(); ++i) {
1939 return emitOpError() <<
"expects all inputs to have the same shapes. "
1940 "Shape at input-index "
1942 <<
" is not equal to the shape at input-index 0.";
1945 for (int64_t i = 1; i < getNumDpsInits(); ++i) {
1948 return emitOpError() <<
"expects all outputs to have the same shapes. "
1949 "Shape at output-index "
1951 <<
" is not equal to the shape at output-index 0.";
1954 auto inputType = llvm::cast<ShapedType>(getInputs()[0].
getType());
1955 auto initType = llvm::cast<ShapedType>(getInits()[0].
getType());
1958 for (int64_t dimension : dimensionsRef) {
1959 if (dimension < 0 || dimension >= inputType.getRank()) {
1961 <<
"dimensions for reduction should be in the range [0, "
1962 << inputType.getRank() - 1 <<
"].";
1964 dimensionsToReduce.insert(dimension);
1967 auto inputDims = inputType.getShape();
1968 auto initDims = initType.getShape();
1971 SmallVector<int64_t> reducedInputDims;
1972 for (
const auto &en : llvm::enumerate(inputDims)) {
1973 if (!dimensionsToReduce.count(en.index()))
1974 reducedInputDims.push_back(en.value());
1977 if (reducedInputDims.size() !=
static_cast<size_t>(initType.getRank())) {
1978 return emitOpError() <<
"number of dimensions after reduction "
1979 << reducedInputDims.size()
1980 <<
" doesn't match the init rank "
1981 << initType.getRank();
1984 if (reducedInputDims != initDims)
1985 return emitOpError() <<
"init dimensions [" << initDims
1986 <<
"] doesn't match input dimensions after reduction ["
1987 << reducedInputDims <<
"]";
1989 Block *block = getBody();
1992 <<
"mismatching number of operands and block arguments";
1995 for (
auto [input, bbArg] : llvm::zip(getInputs(), block->
getArguments())) {
1996 Type inputElementType =
1997 llvm::cast<ShapedType>(input.getType()).getElementType();
1998 if (inputElementType != bbArg.getType())
2000 <<
"input element type " << inputElementType
2001 <<
" does not match corresponding block argument type "
2006 for (
auto [output, bbArg] : llvm::zip(
2007 getDpsInits(), block->
getArguments().take_back(getNumDpsInits()))) {
2008 auto outputElementType =
2009 llvm::cast<ShapedType>(output.getType()).getElementType();
2010 if (outputElementType != bbArg.getType())
2012 <<
"output element type " << outputElementType
2013 <<
" does not match corresponding block argument type "
2029 linalg::YieldOp::create(
b, loc, args[0]);
2033void TransposeOp::build(::mlir::OpBuilder &builder,
2034 ::mlir::OperationState &
result, Value input, Value init,
2036 ArrayRef<NamedAttribute> attributes) {
2037 result.addOperands(input);
2038 result.addOperands(init);
2039 result.addAttribute(getPermutationAttrName(
result.name), permutation);
2040 result.addAttributes(attributes);
2043 Type initType = init.
getType();
2044 if (llvm::isa<RankedTensorType>(initType))
2045 result.addTypes(initType);
2051void TransposeOp::build(::mlir::OpBuilder &builder,
2052 ::mlir::OperationState &
result, Value input, Value init,
2053 ArrayRef<int64_t> permutation,
2054 ArrayRef<NamedAttribute> attributes) {
2059ParseResult TransposeOp::parse(OpAsmParser &parser, OperationState &
result) {
2061 parser,
result, [&](OpAsmParser &parser, NamedAttrList &attributes) {
2073void TransposeOp::getAsmResultNames(
2075 if (!getResults().empty())
2076 setNameFn(getResults().front(),
"transposed");
2079void TransposeOp::print(OpAsmPrinter &p) {
2085LogicalResult TransposeOp::verify() {
2086 ArrayRef<int64_t> permutationRef = getPermutation();
2091 auto inputType = getInput().getType();
2092 auto initType = getInit().getType();
2094 int64_t rank = inputType.getRank();
2100 if (rank !=
static_cast<int64_t
>(permutationRef.size()))
2101 return emitOpError() <<
"size of permutation " << permutationRef.size()
2102 <<
" does not match the argument rank " << rank;
2104 auto inputDims = inputType.getShape();
2105 auto initDims = initType.getShape();
2107 for (int64_t i = 0; i < rank; ++i) {
2108 int64_t inputDim = inputDims[permutationRef[i]];
2109 int64_t initDim = initDims[i];
2111 if (inputDim != initDim) {
2112 return emitOpError() <<
"dim(result, " << i <<
") = " << initDim
2113 <<
" doesn't match dim(input, permutation[" << i
2114 <<
"]) = " << inputDim;
2121SmallVector<utils::IteratorType> TransposeOp::getIteratorTypesArray() {
2122 int64_t rank = getInit().getType().getRank();
2123 return SmallVector<utils::IteratorType>(rank, utils::IteratorType::parallel);
2126ArrayAttr TransposeOp::getIndexingMaps() {
2128 int64_t rank = getInit().getType().getRank();
2131 llvm::to_vector_of<unsigned>(getPermutation()),
getContext())),
2135void TransposeOp::getEffects(
2136 SmallVectorImpl<SideEffects::EffectInstance<MemoryEffects::Effect>>
2145LogicalResult TransposeOp::fold(FoldAdaptor adaptor,
2146 SmallVectorImpl<OpFoldResult> &
result) {
2148 if (!isa<TensorType>(getInput().
getType()))
2152 if (getPermutation().empty()) {
2153 result.push_back(getInput());
2158 result.push_back(getInput());
2171 auto defTransposeOp = transposeOp.getInput().getDefiningOp<TransposeOp>();
2172 if (!defTransposeOp)
2177 foldedPerms.reserve(perms.size());
2179 foldedPerms.push_back(defPerms[perm]);
2182 transposeOp, defTransposeOp.getInput(), transposeOp.getInit(),
2195 if (!transposeOp.hasPureTensorSemantics())
2200 if (!splatValue.has_value())
2204 cast<RankedTensorType>(transposeOp.getResult()[0].getType());
2221 Value input = transposeOp.getInput();
2222 BroadcastOp broadcastOp = input.
getDefiningOp<BroadcastOp>();
2233 unsigned dimensionSize = dimensions.size();
2234 for (
unsigned i = 0; i < dimensionSize; ++i)
2235 resultDimensions.push_back(invertPerm[dimensions[i]]);
2238 Value broadcastInput = broadcastOp.getInput();
2239 Location loc = transposeOp.getLoc();
2242 auto broadcastInputTy =
2243 mlir::cast<RankedTensorType>(broadcastInput.
getType());
2244 unsigned inputRank = broadcastInputTy.getRank();
2245 for (
unsigned i = 0; i < inputRank; ++i) {
2246 if (broadcastInputTy.isDynamicDim(i)) {
2247 dims.push_back(tensor::DimOp::create(rewriter, loc, broadcastInput, i)
2250 dims.push_back(IntegerAttr::get(IndexType::get(ctx),
2251 broadcastInputTy.getDimSize(i)));
2256 Value transposeInit = tensor::EmptyOp::create(
2257 rewriter, transposeOp.getLoc(), transposeResultShapes,
2258 broadcastInputTy.getElementType());
2261 Value transposeResult =
2262 TransposeOp::create(rewriter, loc, broadcastOp.getInput(),
2263 transposeInit, resultPerms)
2266 transposeOp, transposeResult, transposeOp.getInit(), resultDimensions);
2271void TransposeOp::getCanonicalizationPatterns(RewritePatternSet &results,
2272 MLIRContext *context) {
2273 results.
add<FoldTransposeWithTranspose, FoldTransposeSplatConstant,
2274 SwapTransposeWithBroadcast>(context);
2281void BroadcastOp::build(::mlir::OpBuilder &builder,
2282 ::mlir::OperationState &
result, Value input, Value init,
2284 ArrayRef<NamedAttribute> attributes) {
2285 result.addOperands(input);
2286 result.addOperands(init);
2287 result.addAttribute(getDimensionsAttrName(
result.name), dimensions);
2288 result.addAttributes(attributes);
2291 Type initType = init.
getType();
2292 if (llvm::isa<RankedTensorType>(initType))
2293 result.addTypes(initType);
2299void BroadcastOp::build(::mlir::OpBuilder &builder,
2300 ::mlir::OperationState &
result, Value input, Value init,
2301 ArrayRef<int64_t> dimensions,
2302 ArrayRef<NamedAttribute> attributes) {
2307ParseResult BroadcastOp::parse(OpAsmParser &parser, OperationState &
result) {
2309 parser,
result, [&](OpAsmParser &parser, NamedAttrList &attributes) {
2321void BroadcastOp::getAsmResultNames(
2323 if (!getResults().empty())
2324 setNameFn(getResults().front(),
"broadcasted");
2327void BroadcastOp::print(OpAsmPrinter &p) {
2333LogicalResult BroadcastOp::verify() {
2334 ArrayRef<int64_t> dimensionsRef = getDimensions();
2336 auto inputType = getInput().getType();
2337 auto initType = getInit().getType();
2339 int64_t inputRank = inputType.getRank();
2340 int64_t initRank = initType.getRank();
2342 auto inputShape = inputType.getShape();
2343 auto initShape = initType.getShape();
2345 if ((
size_t)inputRank + dimensionsRef.size() != (
size_t)initRank)
2346 return emitOpError() <<
"input rank plus added dimensions does not "
2347 "match init rank. input rank: "
2349 <<
", dimensions size: " << dimensionsRef.size()
2350 <<
", init rank: " << initRank;
2352 for (
const auto &[idx, dim] : llvm::enumerate(dimensionsRef)) {
2353 if (dim < 0 || dim >= initRank)
2355 <<
" is out of range. expected range: [0, "
2356 << initRank - 1 <<
"], got: " << dim;
2360 SmallVector<int64_t> dimMap;
2361 for (
auto dim : llvm::seq<int64_t>(0, initRank)) {
2362 if (!llvm::is_contained(dimensionsRef, dim))
2363 dimMap.push_back(dim);
2366 for (
const auto &[inputDimIdx, initDimIdx] : llvm::enumerate(dimMap)) {
2369 if (inputShape[inputDimIdx] != initShape[initDimIdx])
2370 return emitOpError() <<
"input dim " << inputDimIdx
2371 <<
" should match init dim " << initDimIdx
2372 <<
". input: " << inputShape[inputDimIdx]
2373 <<
", init: " << initShape[initDimIdx];
2379SmallVector<utils::IteratorType> BroadcastOp::getIteratorTypesArray() {
2380 int64_t rank = getInit().getType().getRank();
2381 return SmallVector<utils::IteratorType>(rank, utils::IteratorType::parallel);
2384ArrayAttr BroadcastOp::getIndexingMaps() {
2386 int64_t rank = getInit().getType().getRank();
2392void BroadcastOp::getEffects(
2393 SmallVectorImpl<SideEffects::EffectInstance<MemoryEffects::Effect>>
2408 auto defBroadcastOp = broadcastOp.getInput().getDefiningOp<BroadcastOp>();
2409 if (!defBroadcastOp)
2414 Value init = broadcastOp.getInit();
2418 for (
auto dim : llvm::seq<int64_t>(0, initRank)) {
2419 if (!llvm::is_contained(dimensions, dim))
2420 dimMap.push_back(dim);
2422 for (
auto dim : defDimensions)
2423 foldedDims.push_back(dimMap[dim]);
2425 llvm::sort(foldedDims);
2427 broadcastOp, defBroadcastOp.getInput(), init, foldedDims);
2439 if (!broadcastOp.hasPureTensorSemantics())
2445 if (!splatValue.has_value())
2449 cast<RankedTensorType>(broadcastOp.getResult()[0].getType());
2450 if (!resultType.hasStaticShape())
2452 "result type has dynamic shape");
2461void BroadcastOp::getCanonicalizationPatterns(RewritePatternSet &results,
2462 MLIRContext *context) {
2463 results.
add<EraseIdentityLinalgOp<BroadcastOp>, FoldBroadcasts,
2464 FoldBroadcastSplatConstant>(context);
2471void linalg::YieldOp::print(OpAsmPrinter &p) {
2472 if (getNumOperands() > 0)
2473 p <<
' ' << getOperands();
2475 if (getNumOperands() > 0)
2476 p <<
" : " << getOperandTypes();
2479ParseResult YieldOp::parse(OpAsmParser &parser, OperationState &
result) {
2480 SmallVector<OpAsmParser::UnresolvedOperand, 2> opInfo;
2481 SmallVector<Type, 2> types;
2491static LogicalResult
verifyYield(linalg::YieldOp op, LinalgOp linalgOp) {
2492 if (op.getNumOperands() != linalgOp.getNumDpsInits())
2493 return op.emitOpError(
"expected number of yield values (")
2494 << op.getNumOperands()
2495 <<
") to match the number of inits / outs operands of the enclosing "
2496 <<
"LinalgOp (" << linalgOp.getNumDpsInits() <<
")";
2498 for (
OpOperand &opOperand : op->getOpOperands()) {
2500 linalgOp.getDpsInitOperand(opOperand.getOperandNumber());
2502 if (isa<MemRefType, RankedTensorType>(elementType))
2504 if (opOperand.get().getType() != elementType)
2505 return op.emitOpError(
"type of yield operand ")
2506 << (opOperand.getOperandNumber() + 1) <<
" ("
2507 << opOperand.get().getType() <<
") doesn't match "
2508 <<
"the element type of the enclosing linalg.generic op ("
2509 << elementType <<
")";
2514LogicalResult linalg::YieldOp::verify() {
2515 auto *parentOp = (*this)->getParentOp();
2516 if (parentOp->getNumRegions() != 1 || parentOp->getRegion(0).empty())
2517 return emitOpError(
"expected single non-empty parent region");
2519 if (
auto linalgOp = dyn_cast<LinalgOp>(parentOp))
2522 return emitOpError(
"expected parent op with LinalgOp interface");
2529LogicalResult IndexOp::verify() {
2530 auto linalgOp = dyn_cast<LinalgOp>((*this)->getParentOp());
2532 return emitOpError(
"expected parent op with LinalgOp interface");
2533 if (linalgOp.getNumLoops() <= getDim())
2535 << getDim() <<
") to be lower than the number of loops ("
2536 << linalgOp.getNumLoops() <<
") of the enclosing LinalgOp";
2540OpFoldResult IndexOp::fold(FoldAdaptor adaptor) {
2541 auto linalgOp = dyn_cast_or_null<LinalgOp>((*this)->getParentOp());
2546 return OpFoldResult{};
2549 SmallVector<int64_t, 4> loopBounds = linalgOp.getStaticLoopRanges();
2550 uint64_t dim = getDim();
2551 assert(dim < loopBounds.size() &&
"Dim is out of bounds");
2552 if (loopBounds[dim] == 1)
2553 return IntegerAttr::get(IndexType::get(
getContext()), 0);
2555 return OpFoldResult{};
2560#include "mlir/Dialect/Linalg/IR/LinalgNamedStructuredOps.yamlgen.cpp.inc"
2562#define GET_OP_CLASSES
2563#include "mlir/Dialect/Linalg/IR/LinalgOps.cpp.inc"
2565#define GET_OP_CLASSES
2566#include "mlir/Dialect/Linalg/IR/LinalgStructuredOps.cpp.inc"
2567#define GET_OP_CLASSES
2568#include "mlir/Dialect/Linalg/IR/LinalgRelayoutOps.cpp.inc"
2585 for (
unsigned i = 0; i < num; ++i)
2592 auto rangeA = llvm::make_range(a.begin(), a.end());
2593 auto rangeB = llvm::make_range(
b.begin(),
b.end());
2594 auto concatRanges = llvm::concat<const AffineExpr>(rangeA, rangeB);
2595 return llvm::to_vector<4>(concatRanges);
2599 if (
auto memref = llvm::dyn_cast<MemRefType>(t)) {
2601 for (
auto size :
memref.getShape())
2608 if (
auto as =
memref.getMemorySpace()) {
2609 if (
auto attr = llvm::dyn_cast<IntegerAttr>(as))
2610 ss <<
"as" << attr.getInt();
2616 if (
auto vec = llvm::dyn_cast<VectorType>(t)) {
2619 vec.getShape(), [&](
int64_t i) { ss << i; }, [&]() { ss <<
"x"; });
2632 assert(isa<LinalgOp>(op));
2634 std::string fun =
"";
2636 if (UnaryFnAttr ufa = llvm::dyn_cast<UnaryFnAttr>(kv.getValue())) {
2637 fun = stringifyEnum(ufa.getValue()).str() +
"_";
2638 }
else if (BinaryFnAttr bfa = llvm::dyn_cast<BinaryFnAttr>(kv.getValue())) {
2639 fun = stringifyEnum(bfa.getValue()).str() +
"_";
2643 llvm::replace(name,
'.',
'_');
2644 llvm::raw_string_ostream ss(name);
2648 return std::string();
2663 LogicalResult matchAndRewrite(LinalgOp op,
2665 for (
OpOperand &opOperand : op->getOpOperands()) {
2669 auto mt = llvm::dyn_cast<MemRefType>(opOperand.get().getType());
2672 if (llvm::is_contained(op.getShape(&opOperand), 0)) {
2683struct FoldTensorCastConsumerOp :
public OpRewritePattern<tensor::CastOp> {
2684 using OpRewritePattern<tensor::CastOp>::OpRewritePattern;
2686 LogicalResult matchAndRewrite(tensor::CastOp castOp,
2687 PatternRewriter &rewriter)
const override {
2691 auto linalgOp = castOp.getSource().getDefiningOp<LinalgOp>();
2698 if (castOp->getBlock() != linalgOp->getBlock())
2701 OpBuilder::InsertionGuard guard(rewriter);
2704 Location loc = linalgOp.getLoc();
2705 OpResult resultValue = llvm::cast<OpResult>(castOp.getSource());
2708 llvm::cast<RankedTensorType>(castOp->getResult(0).getType());
2714 OpOperand *outOperand = linalgOp.getDpsInitOperand(resultNumber);
2716 tensor::CastOp::create(rewriter, loc, resultType, outOperand->
get());
2717 SmallVector<Value> newOperands = linalgOp.getDpsInputs();
2718 SmallVector<Value> outputOperands(linalgOp.getDpsInits().begin(),
2719 linalgOp.getDpsInits().end());
2720 outputOperands[resultNumber] = newOperand;
2721 newOperands.append(outputOperands.begin(), outputOperands.end());
2723 SmallVector<Type> resultTypes(linalgOp->result_type_begin(),
2724 linalgOp->result_type_end());
2725 resultTypes[resultNumber] = resultType;
2726 Operation *newOp =
clone(rewriter, linalgOp, resultTypes, newOperands);
2729 Value castBack = tensor::CastOp::create(
2733 results[resultNumber] = castBack;
2742static void populateMap(LinalgOp linalgOp, MutableArrayRef<OpOperand> operands,
2743 llvm::DenseMap<AffineExpr, int64_t> &affineExprToSize) {
2744 for (OpOperand &opOperand : operands) {
2745 if (linalgOp.isScalar(&opOperand))
2747 Value src = opOperand.get();
2748 auto sourceType = llvm::cast<RankedTensorType>(src.
getType());
2749 auto sourceMap = linalgOp.getMatchingIndexingMap(&opOperand);
2755 ArrayRef<int64_t> sourceShape = sourceType.getShape();
2757 if (
auto castOp = dyn_cast<tensor::CastOp>(parentOp)) {
2758 Value castSource = castOp.getSource();
2759 auto castSourceType =
2760 llvm::dyn_cast<RankedTensorType>(castSource.
getType());
2761 if (castSourceType && castSourceType.hasStaticShape())
2762 sourceShape = castSourceType.getShape();
2768 for (
unsigned i = 0; i < sourceShape.size(); i++) {
2769 if (sourceType.isDynamicDim(i))
2771 if (
auto affineDimExpr = dyn_cast<AffineDimExpr>(sourceMap.getResult(i)))
2772 affineExprToSize.try_emplace(affineDimExpr, sourceShape[i]);
2782static void createNewOperandWithStaticSizes(
2783 Location loc, PatternRewriter &rewriter, OpOperand *opOperand,
2784 llvm::DenseMap<AffineExpr, int64_t> &affineExprToSize, LinalgOp linalgOp,
2785 SmallVector<Value> &newOperands, SmallVector<Type> &resultTypes,
2786 bool &changeNeeded) {
2787 Value src = opOperand->
get();
2788 newOperands.push_back(src);
2789 if (linalgOp.isScalar(opOperand))
2791 auto sourceType = llvm::cast<RankedTensorType>(src.
getType());
2792 Type resultType = sourceType;
2793 if (sourceType.hasStaticShape() && linalgOp.isDpsInit(opOperand)) {
2794 resultTypes.push_back(resultType);
2797 ArrayRef<int64_t> sourceShape = sourceType.getShape();
2798 AffineMap sourceMap = linalgOp.getMatchingIndexingMap(opOperand);
2799 SmallVector<int64_t> newShape;
2802 bool newOperandNeeded =
false;
2803 for (
unsigned i = 0; i < sourceShape.size(); i++) {
2804 int64_t dimShape = sourceShape[i];
2805 AffineExpr dimExpr = sourceMap.
getResult(i);
2806 if (!affineExprToSize.contains(dimExpr) || !sourceType.isDynamicDim(i)) {
2807 newShape.push_back(dimShape);
2813 newShape.push_back(affineExprToSize[dimExpr]);
2814 newOperandNeeded =
true;
2816 resultType = RankedTensorType::get(newShape, sourceType.getElementType(),
2817 sourceType.getEncoding());
2818 if (newOperandNeeded) {
2819 changeNeeded =
true;
2822 Value newOperand = tensor::CastOp::create(rewriter, loc, resultType, src);
2824 newOperands[index] = newOperand;
2826 if (linalgOp.isDpsInit(opOperand))
2827 resultTypes.push_back(resultType);
2833struct InferStaticShapeOfOperands :
public OpInterfaceRewritePattern<LinalgOp> {
2834 using OpInterfaceRewritePattern<LinalgOp>::OpInterfaceRewritePattern;
2836 LogicalResult matchAndRewrite(LinalgOp linalgOp,
2837 PatternRewriter &rewriter)
const override {
2838 if (!linalgOp.hasPureTensorSemantics())
2842 if (llvm::any_of(linalgOp.getIndexingMapsArray(), [](AffineMap map) {
2843 return !map.isProjectedPermutation();
2848 llvm::DenseMap<AffineExpr, int64_t> affineExprToSize;
2849 Location loc = linalgOp.getLoc();
2853 populateMap(linalgOp, linalgOp->getOpOperands(), affineExprToSize);
2855 SmallVector<Value> newOperands;
2856 SmallVector<Type> resultTypes;
2860 bool changeNeeded =
false;
2861 newOperands.reserve(linalgOp->getNumOperands());
2862 resultTypes.reserve(linalgOp.getNumDpsInits());
2865 for (OpOperand &opOperand : linalgOp->getOpOperands()) {
2866 createNewOperandWithStaticSizes(loc, rewriter, &opOperand,
2867 affineExprToSize, linalgOp, newOperands,
2868 resultTypes, changeNeeded);
2877 Operation *newOp =
clone(rewriter, linalgOp, resultTypes, newOperands);
2878 SmallVector<Value> replacements;
2880 for (
auto it : llvm::zip(linalgOp->getResults(), newOp->
getResults())) {
2881 Value newResult = std::get<1>(it);
2882 Value oldResult = std::get<0>(it);
2883 Type newType = newResult.
getType();
2884 Type oldType = oldResult.
getType();
2885 replacements.push_back(
2886 (newType != oldType)
2887 ? tensor::CastOp::create(rewriter, loc, oldType, newResult)
2890 rewriter.
replaceOp(linalgOp, replacements);
2904LogicalResult SoftmaxOp::verify() {
2905 ShapedType inputType = getInputOperandType();
2906 ShapedType outputType = getOutputOperandType();
2908 ArrayRef<int64_t> inputShape = inputType.getShape();
2909 ArrayRef<int64_t> outputShape = outputType.getShape();
2913 int64_t inputRank = getInputOperandRank();
2914 int64_t dimension = getDimension();
2915 if ((dimension < 0) || (dimension >= inputRank))
2916 return emitOpError(
"incorrect dimension specified");
2921SmallVector<Range> SoftmaxOp::getIterationDomain(OpBuilder &builder) {
2922 int64_t operandRank = getInputOperandRank();
2923 SmallVector<Range> loopBounds(operandRank);
2924 Location loc = getLoc();
2927 Value source = getInput();
2928 for (
auto dim : llvm::seq<int64_t>(0, operandRank)) {
2929 loopBounds[dim].offset = zero;
2930 loopBounds[dim].size =
getDimValue(builder, loc, source, dim);
2931 loopBounds[dim].stride = one;
2936SmallVector<utils::IteratorType> SoftmaxOp::getLoopIteratorTypes() {
2937 SmallVector<utils::IteratorType> iteratorTypes(getInputOperandRank(),
2938 utils::IteratorType::parallel);
2939 iteratorTypes[getDimension()] = utils::IteratorType::reduction;
2940 return iteratorTypes;
2946FailureOr<TilingResult> SoftmaxOp::getTiledImplementation(
2947 OpBuilder &builder, ArrayRef<OpFoldResult> offsets,
2948 ArrayRef<OpFoldResult> sizes, ArrayRef<InnerTileAlignment>) {
2952FailureOr<TilingResult>
2953SoftmaxOp::getTiledImplementation(OpBuilder &builder,
2954 ArrayRef<OpFoldResult> offsets,
2955 ArrayRef<OpFoldResult> sizes) {
2956 int64_t rank = getInputOperandRank();
2958 SmallVector<OpFoldResult> strides(rank, oneAttr);
2959 SmallVector<Value> tiledOperands;
2960 Operation *inputSlice =
2961 getSlice(builder, getLoc(), getInput(), offsets, sizes, strides);
2963 return emitOpError(
"failed to compute input slice");
2965 tiledOperands.emplace_back(inputSlice->
getResult(0));
2966 Operation *outputSlice =
2967 getSlice(builder, getLoc(), getOutput(), offsets, sizes, strides);
2969 return emitOpError(
"failed to compute output slice");
2971 tiledOperands.emplace_back(outputSlice->
getResult(0));
2973 SmallVector<Type, 4> resultTypes;
2974 if (hasPureTensorSemantics())
2975 resultTypes.push_back(tiledOperands[1].
getType());
2976 Operation *tiledOp =
2977 mlir::clone(builder, getOperation(), resultTypes, tiledOperands);
2979 return TilingResult{
2982 llvm::to_vector(ArrayRef<Operation *>{inputSlice, outputSlice})};
2985LogicalResult SoftmaxOp::getResultTilePosition(
2986 OpBuilder &builder,
unsigned resultNumber, ArrayRef<OpFoldResult> offsets,
2987 ArrayRef<OpFoldResult> sizes, SmallVector<OpFoldResult> &resultOffsets,
2988 SmallVector<OpFoldResult> &resultSizes) {
2989 if (resultNumber == 0) {
2990 resultOffsets.assign(offsets.begin(), offsets.end());
2991 resultSizes.assign(sizes.begin(), sizes.end());
2998LogicalResult SoftmaxOp::fold(FoldAdaptor, SmallVectorImpl<OpFoldResult> &) {
3003SoftmaxOp::reifyResultShapes(OpBuilder &
b,
3005 SmallVector<OpFoldResult> shapes;
3006 Location loc = getOperation()->getLoc();
3007 IRRewriter rewriter(
b);
3008 auto inputShapedType = llvm::cast<ShapedType>(getInputOperandType());
3009 auto outputShapedType = llvm::cast<ShapedType>(getOutputOperandType());
3010 for (int64_t dim : llvm::seq<int64_t>(0, getOutputOperandRank())) {
3011 if (!outputShapedType.isDynamicDim(dim)) {
3013 shapes.push_back(
b.getIndexAttr(inputShapedType.getDimSize(dim)));
3020 reifiedReturnShapes.emplace_back(std::move(shapes));
3024void SoftmaxOp::getEffects(
3025 SmallVectorImpl<SideEffects::EffectInstance<MemoryEffects::Effect>>
3027 for (
auto [index, operand] : llvm::enumerate(getDpsInputs())) {
3028 if (!llvm::isa<MemRefType>(operand.
getType()))
3031 &getOperation()->getOpOperand(index), 0,
3036 for (OpOperand &operand : getDpsInitsMutable()) {
3037 if (!llvm::isa<MemRefType>(operand.get().
getType()))
3068static std::tuple<SmallVector<utils::IteratorType>, SmallVector<AffineMap>>
3070 int64_t dim,
bool allParallel =
false) {
3072 utils::IteratorType::parallel);
3074 iteratorTypes[dim] = utils::IteratorType::reduction;
3078 for (
int i = 0; i < inputRank; i++) {
3085 return std::make_tuple(iteratorTypes, indexingMaps);
3090template <
typename T>
3093 auto inputType = cast<ShapedType>(input.
getType());
3095 int64_t inputRank = inputShape.size();
3096 auto [iteratorTypes, indexingMaps] =
3098 assert(indexingMaps.size() == 2 &&
3099 "We should have two maps: 1 for the input, 1 for the output");
3100 assert(indexingMaps[0].isIdentity() &&
"input map should be identity");
3102 auto genericOp = linalg::GenericOp::create(
3103 builder, loc, output.
getType(), input, output, indexingMaps,
3105 Value result = T::create(b, loc, args[0], args[1]);
3106 linalg::YieldOp::create(b, loc, result);
3108 return genericOp.getResult(0);
3116 auto inputType = cast<ShapedType>(input.
getType());
3118 int64_t inputRank = inputShape.size();
3120 builder, inputRank, dim,
true);
3121 assert(indexingMaps.size() == 2 &&
"We should have one map for each input");
3122 assert(indexingMaps[0].isIdentity() &&
"input map should be identity");
3124 indexingMaps.push_back(indexingMaps[0]);
3125 auto genericOp = linalg::GenericOp::create(
3127 indexingMaps, iteratorTypes,
3129 Value diff = arith::SubFOp::create(b, loc, args[0], args[1]);
3130 Value result = math::ExpOp::create(b, loc, diff);
3131 linalg::YieldOp::create(b, loc, result);
3133 return genericOp.getResult(0);
3143 auto inputType = cast<ShapedType>(numerator.
getType());
3145 int64_t inputRank = inputShape.size();
3147 builder, inputRank, dim,
true);
3148 assert(indexingMaps.size() == 2 &&
3149 "We should have one map for each input (2)");
3150 assert(indexingMaps[0].isIdentity() &&
"Numerator map should be identity");
3152 indexingMaps.push_back(indexingMaps[0]);
3153 auto genericOp = linalg::GenericOp::create(
3155 output, indexingMaps, iteratorTypes,
3157 Value result = arith::DivFOp::create(b, loc, args[0], args[1]);
3158 linalg::YieldOp::create(b, loc, result);
3160 return genericOp.getResult(0);
3182FailureOr<SmallVector<Value>> SoftmaxOp::decomposeOperation(OpBuilder &
b) {
3183 OpBuilder::InsertionGuard guard(
b);
3184 b.setInsertionPoint(*
this);
3185 Location loc = getLoc();
3186 Value input = getInput();
3187 ShapedType inputType = getInputOperandType();
3188 Type elementType = inputType.getElementType();
3189 int64_t reductionDim = getDimension();
3191 Value output = getOutput();
3192 dims.erase(dims.begin() + reductionDim);
3194 Value outputReduce = tensor::EmptyOp::create(
b, loc, dims, elementType);
3196 elementType,
b, loc,
3198 Value neutralForMaxFInit =
3199 linalg::FillOp::create(
b, loc, Value{neutralForMaxF}, outputReduce)
3211 linalg::FillOp::create(
b, loc, Value{zero}, outputReduce).
result();
3217 buildDivOp(
b, loc, numerator, denominator, output, reductionDim);
3218 return SmallVector<Value>{
result};
3225LogicalResult WinogradFilterTransformOp::verify() {
3226 auto filterType = cast<ShapedType>(getFilter().
getType());
3227 ArrayRef<int64_t> filterShape = filterType.getShape();
3228 int64_t filterH = filterShape[getFilterHDim()];
3229 int64_t filterW = filterShape[getFilterWDim()];
3230 WinogradConv2DFmr fmr = getFmr();
3234 if (filterH != r && filterH != 1)
3235 return emitOpError(
"expect filter height either equals to r or 1");
3236 if (filterW != r && filterW != 1)
3237 return emitOpError(
"expect filter width either equals to r or 1");
3238 if (filterH == 1 && filterW == 1)
3239 return emitOpError(
"expect either filter height or width equals to r");
3241 SmallVector<int64_t> expectedOutputShape;
3242 expectedOutputShape.push_back(filterH == r ? m + r - 1 : 1);
3243 expectedOutputShape.push_back(filterW == r ? m + r - 1 : 1);
3244 expectedOutputShape.push_back(filterShape[getFilterCDim()]);
3245 expectedOutputShape.push_back(filterShape[getFilterFDim()]);
3247 auto outputType = cast<ShapedType>(getOutput().
getType());
3248 ArrayRef<int64_t> outputShape = outputType.getShape();
3250 return emitOpError(
"the output shape is not expected");
3256WinogradFilterTransformOp::getIterationDomain(OpBuilder &builder) {
3257 Location loc = getLoc();
3260 Value filter = getFilter();
3261 int64_t filterRank = getFilterOperandRank();
3262 SmallVector<Range> loopBounds(filterRank);
3263 for (
unsigned dim = 0; dim < filterRank; ++dim) {
3264 loopBounds[dim].offset = zeroAttr;
3265 loopBounds[dim].size =
getDimValue(builder, loc, filter, dim);
3266 loopBounds[dim].stride = oneAttr;
3271SmallVector<utils::IteratorType>
3272WinogradFilterTransformOp::getLoopIteratorTypes() {
3273 int64_t filterRank = getFilterOperandRank();
3274 SmallVector<utils::IteratorType> iteratorTypes(filterRank,
3275 utils::IteratorType::parallel);
3276 return iteratorTypes;
3279LogicalResult WinogradFilterTransformOp::getResultTilePosition(
3280 OpBuilder &builder,
unsigned resultNumber, ArrayRef<OpFoldResult> offsets,
3281 ArrayRef<OpFoldResult> sizes, SmallVector<OpFoldResult> &resultOffsets,
3282 SmallVector<OpFoldResult> &resultSizes) {
3284 ShapedType filterType = getFilterOperandType();
3285 ArrayRef<int64_t> filterShape = filterType.getShape();
3286 int64_t filterH = filterShape[getFilterHDim()];
3287 int64_t filterW = filterShape[getFilterWDim()];
3288 WinogradConv2DFmr fmr = getFmr();
3291 int64_t alpha = m + r - 1;
3292 int64_t alphaH = filterH != 1 ? alpha : 1;
3293 int64_t alphaW = filterW != 1 ? alpha : 1;
3297 resultOffsets.append(
3298 {zeroAttr, zeroAttr, offsets[getFilterCDim()], offsets[getFilterFDim()]});
3300 {alphaHAttr, alphaWAttr, sizes[getFilterCDim()], sizes[getFilterFDim()]});
3308FailureOr<TilingResult> WinogradFilterTransformOp::getTiledImplementation(
3309 OpBuilder &builder, ArrayRef<OpFoldResult> offsets,
3310 ArrayRef<OpFoldResult> sizes, ArrayRef<InnerTileAlignment>) {
3320FailureOr<TilingResult> WinogradFilterTransformOp::getTiledImplementation(
3321 OpBuilder &builder, ArrayRef<OpFoldResult> offsets,
3322 ArrayRef<OpFoldResult> sizes) {
3325 ShapedType filterType = getFilterOperandType();
3326 ArrayRef<int64_t> filterShape = filterType.getShape();
3327 int64_t filterH = filterShape[getFilterHDim()];
3328 int64_t filterW = filterShape[getFilterWDim()];
3331 SmallVector<Value> tiledOperands;
3332 SmallVector<OpFoldResult> sliceOffsets, sliceSizes;
3334 sliceOffsets.append(
3335 {offsets[getFilterFDim()], zeroAttr, zeroAttr, offsets[getFilterCDim()]});
3336 sliceSizes.append({sizes[getFilterFDim()], filterHAttr, filterWAttr,
3337 sizes[getFilterCDim()]});
3338 int64_t filterRank = getFilterOperandRank();
3339 SmallVector<OpFoldResult> filterStrides(filterRank, oneAttr);
3340 Location loc = getLoc();
3341 auto filterSlice = tensor::ExtractSliceOp::create(
3342 builder, loc, getFilter(), sliceOffsets, sliceSizes, filterStrides);
3343 tiledOperands.emplace_back(filterSlice);
3345 SmallVector<OpFoldResult> resultOffsets, resultSizes;
3350 int64_t outputRank = getOutputOperandRank();
3351 SmallVector<OpFoldResult> outputStrides(outputRank, oneAttr);
3352 auto outputSlice = tensor::ExtractSliceOp::create(
3353 builder, loc, getOutput(), resultOffsets, resultSizes, outputStrides);
3354 tiledOperands.emplace_back(outputSlice);
3356 SmallVector<Type> resultTypes;
3357 resultTypes.push_back(tiledOperands[1].
getType());
3358 Operation *tiledOp =
3359 mlir::clone(builder, getOperation(), resultTypes, tiledOperands);
3361 return TilingResult{
3364 llvm::to_vector(ArrayRef<Operation *>{filterSlice, outputSlice})};
3371LogicalResult WinogradInputTransformOp::verify() {
3372 auto inputType = cast<ShapedType>(getInput().
getType());
3373 ArrayRef<int64_t> inputShape = inputType.getShape();
3374 int64_t inputH = inputShape[getInputHDim()];
3375 int64_t inputW = inputShape[getInputWDim()];
3376 WinogradConv2DFmr fmr = getFmr();
3379 int64_t tileSize = m + r - 1;
3381 auto outputType = cast<ShapedType>(getOutput().
getType());
3382 ArrayRef<int64_t> outputShape = outputType.getShape();
3383 bool leftTransform = outputShape[getOutputAlphaHDim()] != 1;
3384 bool rightTransform = outputShape[getOutputAlphaWDim()] != 1;
3386 SmallVector<int64_t> expectedOutputShape(6, inputH);
3387 if (ShapedType::isDynamic(inputH)) {
3388 expectedOutputShape[getOutputAlphaHDim()] = tileSize;
3389 expectedOutputShape[getOutputTileHDim()] = ShapedType::kDynamic;
3391 expectedOutputShape[getOutputAlphaHDim()] = leftTransform ? tileSize : 1;
3392 expectedOutputShape[getOutputTileHDim()] =
3393 leftTransform ? (inputH - (r - 1)) / m : inputH;
3395 if (ShapedType::isDynamic(inputW)) {
3396 expectedOutputShape[getOutputAlphaWDim()] = tileSize;
3397 expectedOutputShape[getOutputTileWDim()] = ShapedType::kDynamic;
3399 expectedOutputShape[getOutputAlphaWDim()] = rightTransform ? tileSize : 1;
3400 expectedOutputShape[getOutputTileWDim()] =
3401 rightTransform ? (inputW - (r - 1)) / m : inputW;
3403 expectedOutputShape[getOutputNDim()] = inputShape[getInputNDim()];
3404 expectedOutputShape[getOutputCDim()] = inputShape[getInputCDim()];
3407 return emitOpError(
"the output shape is not expected");
3413WinogradInputTransformOp::getIterationDomain(OpBuilder &builder) {
3414 Location loc = getLoc();
3417 Value output = getOutput();
3418 int64_t outputRank = getOutputOperandRank();
3419 SmallVector<Range> loopBounds(outputRank);
3420 for (
unsigned dim = 0; dim < outputRank; ++dim) {
3421 loopBounds[dim].offset = zeroAttr;
3423 loopBounds[dim].size =
getDimValue(builder, loc, output, dim);
3424 loopBounds[dim].stride = oneAttr;
3429SmallVector<utils::IteratorType>
3430WinogradInputTransformOp::getLoopIteratorTypes() {
3431 int64_t outputRank = getOutputOperandRank();
3432 SmallVector<utils::IteratorType> iteratorTypes(outputRank,
3433 utils::IteratorType::parallel);
3434 return iteratorTypes;
3437LogicalResult WinogradInputTransformOp::getResultTilePosition(
3438 OpBuilder &builder,
unsigned resultNumber, ArrayRef<OpFoldResult> offsets,
3439 ArrayRef<OpFoldResult> sizes, SmallVector<OpFoldResult> &resultOffsets,
3440 SmallVector<OpFoldResult> &resultSizes) {
3442 ShapedType outputType = getOutputOperandType();
3443 ArrayRef<int64_t> outputShape = outputType.getShape();
3444 int64_t outputAlphaH = outputShape[getOutputAlphaHDim()];
3445 int64_t outputAlphaW = outputShape[getOutputAlphaWDim()];
3447 WinogradConv2DFmr fmr = getFmr();
3450 int64_t alpha = m + r - 1;
3451 int64_t alphaH = outputAlphaH != 1 ? alpha : 1;
3452 int64_t alphaW = outputAlphaW != 1 ? alpha : 1;
3457 resultOffsets.append({zeroAttr, zeroAttr, offsets[getOutputTileHDim()],
3458 offsets[getOutputTileWDim()], offsets[getOutputNDim()],
3459 offsets[getOutputCDim()]});
3460 resultSizes.append({alphaHAttr, alphaWAttr, sizes[getOutputTileHDim()],
3461 sizes[getOutputTileWDim()], sizes[getOutputNDim()],
3462 sizes[getOutputCDim()]});
3470FailureOr<TilingResult> WinogradInputTransformOp::getTiledImplementation(
3471 OpBuilder &builder, ArrayRef<OpFoldResult> offsets,
3472 ArrayRef<OpFoldResult> sizes, ArrayRef<InnerTileAlignment>) {
3482FailureOr<TilingResult>
3483WinogradInputTransformOp::getTiledImplementation(OpBuilder &builder,
3484 ArrayRef<OpFoldResult> offsets,
3485 ArrayRef<OpFoldResult> sizes) {
3487 WinogradConv2DFmr fmr = getFmr();
3491 ShapedType outputType = getOutputOperandType();
3492 ArrayRef<int64_t> outputShape = outputType.getShape();
3493 int64_t alphaH = outputShape[getOutputAlphaHDim()];
3494 int64_t alphaW = outputShape[getOutputAlphaWDim()];
3496 Location loc = getLoc();
3498 auto identityAffineMap =
3500 auto offsetAffineMap =
3503 builder, loc, (alphaH != 1 ? offsetAffineMap : identityAffineMap),
3504 offsets[getOutputTileHDim()]);
3506 builder, loc, (alphaW != 1 ? offsetAffineMap : identityAffineMap),
3507 offsets[getOutputTileWDim()]);
3511 builder, loc, sizeAffineMap, sizes[getOutputTileHDim()]);
3513 builder, loc, sizeAffineMap, sizes[getOutputTileWDim()]);
3515 SmallVector<Value> tiledOperands;
3516 SmallVector<OpFoldResult> sliceOffsets, sliceSizes;
3518 OpFoldResult offsetH = OpFoldResult(mappedOffsetH);
3519 OpFoldResult offsetW = OpFoldResult(mappedOffsetW);
3520 sliceOffsets.append(
3521 {offsets[getOutputNDim()], offsetH, offsetW, offsets[getOutputCDim()]});
3522 OpFoldResult sizeH =
3523 alphaH != 1 ? OpFoldResult(mappedSizeH) : OpFoldResult(oneAttr);
3524 OpFoldResult sizeW =
3525 alphaW != 1 ? OpFoldResult(mappedSizeW) : OpFoldResult(oneAttr);
3527 {sizes[getOutputNDim()], sizeH, sizeW, sizes[getOutputCDim()]});
3528 int64_t inputRank = getInputOperandRank();
3529 SmallVector<OpFoldResult> inputStrides(inputRank, oneAttr);
3530 auto inputSlice = tensor::ExtractSliceOp::create(
3531 builder, loc, getInput(), sliceOffsets, sliceSizes, inputStrides);
3532 tiledOperands.emplace_back(inputSlice);
3534 SmallVector<OpFoldResult> resultOffsets, resultSizes;
3539 int64_t outputRank = getOutputOperandRank();
3540 SmallVector<OpFoldResult> outputStrides(outputRank, oneAttr);
3541 auto outputSlice = tensor::ExtractSliceOp::create(
3542 builder, loc, getOutput(), resultOffsets, resultSizes, outputStrides);
3543 tiledOperands.emplace_back(outputSlice);
3545 SmallVector<Type> resultTypes;
3546 resultTypes.push_back(tiledOperands[1].
getType());
3547 Operation *tiledOp =
3548 mlir::clone(builder, getOperation(), resultTypes, tiledOperands);
3550 return TilingResult{
3553 llvm::to_vector(ArrayRef<Operation *>{inputSlice, outputSlice})};
3560LogicalResult WinogradOutputTransformOp::verify() {
3561 auto valueType = cast<ShapedType>(getValue().
getType());
3562 ArrayRef<int64_t> valueShape = valueType.getShape();
3563 int64_t valueH = valueShape[getValueAlphaHDim()];
3564 int64_t valueW = valueShape[getValueAlphaWDim()];
3565 int64_t valueTileH = valueShape[getValueTileHDim()];
3566 int64_t valueTileW = valueShape[getValueTileWDim()];
3567 WinogradConv2DFmr fmr = getFmr();
3570 bool leftTransform = valueH != 1;
3571 bool rightTransform = valueW != 1;
3573 int64_t outputRank = getOutputOperandRank();
3574 SmallVector<int64_t> expectedOutputShape(outputRank, valueH);
3575 if (ShapedType::isDynamic(valueH) || ShapedType::isDynamic(valueTileH)) {
3576 expectedOutputShape[getOutputHDim()] = ShapedType::kDynamic;
3578 if (valueH != (leftTransform ? m + r - 1 : 1))
3579 return emitOpError(
"expect input height equals to input tile size");
3580 expectedOutputShape[getOutputHDim()] = (leftTransform ? m : 1) * valueTileH;
3582 if (ShapedType::isDynamic(valueW) || ShapedType::isDynamic(valueTileW)) {
3583 expectedOutputShape[getOutputWDim()] = ShapedType::kDynamic;
3585 if (valueW != (rightTransform ? m + r - 1 : 1))
3586 return emitOpError(
"expect input width equals to input tile size");
3587 expectedOutputShape[getOutputWDim()] =
3588 (rightTransform ? m : 1) * valueTileW;
3590 expectedOutputShape[getOutputNDim()] = valueShape[getValueNDim()];
3591 expectedOutputShape[getOutputFDim()] = valueShape[getValueFDim()];
3593 auto outputType = cast<ShapedType>(getOutput().
getType());
3594 ArrayRef<int64_t> outputShape = outputType.getShape();
3596 return emitOpError(
"the output shape is not expected");
3602WinogradOutputTransformOp::getIterationDomain(OpBuilder &builder) {
3603 Location loc = getLoc();
3606 Value value = getValue();
3607 int64_t valueRank = getValueOperandRank();
3608 SmallVector<Range> loopBounds(valueRank);
3609 for (
unsigned dim = 0; dim < valueRank; ++dim) {
3610 loopBounds[dim].offset = zeroAttr;
3612 loopBounds[dim].size =
getDimValue(builder, loc, value, dim);
3613 loopBounds[dim].stride = oneAttr;
3618SmallVector<utils::IteratorType>
3619WinogradOutputTransformOp::getLoopIteratorTypes() {
3620 int64_t valueRank = getValueOperandRank();
3621 SmallVector<utils::IteratorType> iteratorTypes(valueRank,
3622 utils::IteratorType::parallel);
3623 return iteratorTypes;
3626LogicalResult WinogradOutputTransformOp::getResultTilePosition(
3627 OpBuilder &builder,
unsigned resultNumber, ArrayRef<OpFoldResult> offsets,
3628 ArrayRef<OpFoldResult> sizes, SmallVector<OpFoldResult> &resultOffsets,
3629 SmallVector<OpFoldResult> &resultSizes) {
3630 WinogradConv2DFmr fmr = getFmr();
3634 Location loc = getLoc();
3636 auto identityAffineMap =
3641 ShapedType valueType = getValueOperandType();
3642 ArrayRef<int64_t> valueShape = valueType.getShape();
3643 int64_t valueH = valueShape[0];
3644 int64_t valueW = valueShape[1];
3646 builder, loc, (valueH != 1 ? affineMap : identityAffineMap),
3647 offsets[getValueTileHDim()]);
3649 builder, loc, (valueW != 1 ? affineMap : identityAffineMap),
3650 offsets[getValueTileWDim()]);
3652 builder, loc, affineMap, sizes[getValueTileHDim()]);
3654 builder, loc, affineMap, sizes[getValueTileWDim()]);
3657 OpFoldResult offsetH = OpFoldResult(mappedOffsetH);
3658 OpFoldResult offsetW = OpFoldResult(mappedOffsetW);
3659 OpFoldResult sizeH =
3660 valueH != 1 ? OpFoldResult(mappedSizeH) : OpFoldResult(oneAttr);
3661 OpFoldResult sizeW =
3662 valueW != 1 ? OpFoldResult(mappedSizeW) : OpFoldResult(oneAttr);
3664 resultOffsets.append(
3665 {offsets[getValueNDim()], offsetH, offsetW, offsets[getValueFDim()]});
3667 {sizes[getValueNDim()], sizeH, sizeW, sizes[getValueFDim()]});
3674FailureOr<TilingResult> WinogradOutputTransformOp::getTiledImplementation(
3675 OpBuilder &builder, ArrayRef<OpFoldResult> offsets,
3676 ArrayRef<OpFoldResult> sizes, ArrayRef<InnerTileAlignment>) {
3686FailureOr<TilingResult> WinogradOutputTransformOp::getTiledImplementation(
3687 OpBuilder &builder, ArrayRef<OpFoldResult> offsets,
3688 ArrayRef<OpFoldResult> sizes) {
3691 Location loc = getLoc();
3692 SmallVector<Value> tiledOperands;
3693 SmallVector<OpFoldResult> sliceOffsets, sliceSizes;
3695 ShapedType valueType = getValueOperandType();
3696 ArrayRef<int64_t> valueShape = valueType.getShape();
3697 int64_t alphaH = valueShape[getValueAlphaHDim()];
3698 int64_t alphaW = valueShape[getValueAlphaWDim()];
3702 sliceOffsets.append({zeroAttr, zeroAttr, offsets[getValueTileHDim()],
3703 offsets[getValueTileWDim()], offsets[getValueNDim()],
3704 offsets[getValueFDim()]});
3705 sliceSizes.append({alphaHAttr, alphaWAttr, sizes[getValueTileHDim()],
3706 sizes[getValueTileWDim()], sizes[getValueNDim()],
3707 sizes[getValueFDim()]});
3708 int64_t valueRank = getValueOperandRank();
3709 SmallVector<OpFoldResult> sliceStrides(valueRank, oneAttr);
3710 auto valueSlice = tensor::ExtractSliceOp::create(
3711 builder, loc, getValue(), sliceOffsets, sliceSizes, sliceStrides);
3712 tiledOperands.emplace_back(valueSlice);
3714 SmallVector<OpFoldResult> resultOffsets, resultSizes;
3719 int64_t outputRank = getOutputOperandRank();
3720 SmallVector<OpFoldResult> strides(outputRank, oneAttr);
3721 auto outputSlice = tensor::ExtractSliceOp::create(
3722 builder, loc, getOutput(), resultOffsets, resultSizes, strides);
3723 tiledOperands.emplace_back(outputSlice);
3725 SmallVector<Type> resultTypes;
3726 resultTypes.push_back(tiledOperands[1].
getType());
3727 Operation *tiledOp =
3728 mlir::clone(builder, getOperation(), resultTypes, tiledOperands);
3730 return TilingResult{
3733 llvm::to_vector(ArrayRef<Operation *>{valueSlice, outputSlice})};
3747 llvm::set_union(explicitSet, defaultSet);
3748 return explicitSet == defaultSet;
3768 matmulOp.getDefaultIndexingMaps(matmulOp->getContext());
3770 auto opIndexingMap = opIndexingMaps[opIndex];
3771 auto defaultIndexingMap = defaultIndexingMaps[opIndex];
3774 return matmulOp->emitOpError()
3775 <<
"Unexpected dim expression in map result.";
3778 if (!matmulOp.isValidLhsRhsBroadcastMap(opIndexingMap)) {
3779 return matmulOp->emitOpError()
3780 <<
"Invalid broadcast requested, should be (d2).";
3789template <
typename OpTy>
3792 AffineMap defaultIndexingMap,
bool isLHS) {
3793 assert((isa<BatchMatmulOp>(batchVariantMatmulOp) ||
3794 isa<BatchReduceMatmulOp>(batchVariantMatmulOp)) &&
3795 "Expected BatchMatmulOp or BatchReduceMatmulOp");
3798 return batchVariantMatmulOp->emitOpError()
3799 <<
"Unexpected result dim expression (outside the set of default "
3804 return batchVariantMatmulOp->emitOpError()
3805 <<
"no. of result dim expressions exceeds 3.";
3807 auto hasValidBatchDim = [](
AffineMap map) {
3814 if (!batchVariantMatmulOp.isValidLhsRhsBroadcastMap(opIndexingMap, isLHS))
3815 return batchVariantMatmulOp->emitOpError()
3816 <<
"Invalid broadcast requested.";
3817 }
else if (!hasValidBatchDim(opIndexingMap)) {
3818 return batchVariantMatmulOp->emitOpError()
3819 <<
"Invalid batch dimension expression.";
3827template <
typename OpTy>
3830 assert((isa<BatchMatmulOp>(batchVariantMatmulOp) ||
3831 isa<BatchReduceMatmulOp>(batchVariantMatmulOp)) &&
3832 "Expected BatchMatmulOp or BatchReduceMatmulOp");
3833 if (isa<BatchMatmulOp>(batchVariantMatmulOp) &&
3836 return batchVariantMatmulOp->emitOpError()
3837 <<
"expects 3 dims, but got (" << opIndexingMap.
getNumResults()
3840 if (isa<BatchReduceMatmulOp>(batchVariantMatmulOp) &&
3842 return batchVariantMatmulOp->emitOpError()
3843 <<
"expects 2 dims, but got (" << opIndexingMap.
getNumResults()
3847 auto areValidOutputResultDim = [&](
AffineMap outputMap) {
3848 return isa<BatchMatmulOp>(batchVariantMatmulOp)
3849 ? outputMap.getResult(0).isFunctionOfDim(0) &&
3850 outputMap.getResult(1).isFunctionOfDim(1) &&
3851 outputMap.getResult(2).isFunctionOfDim(2)
3852 : outputMap.getResult(0).isFunctionOfDim(1) &&
3853 outputMap.getResult(1).isFunctionOfDim(2);
3856 if (!areValidOutputResultDim(opIndexingMap)) {
3857 return batchVariantMatmulOp->emitOpError()
3858 <<
"Invalid output map result dimension.";
3867template <
typename OpTy>
3872 batchVariantMatmulOp.getIndexingMapsArray();
3874 batchVariantMatmulOp.getDefaultIndexingMaps(
3875 batchVariantMatmulOp->getContext());
3877 if (opIndexingMaps.size() != 3)
3878 return batchVariantMatmulOp->emitOpError()
3879 <<
"Indexing_map attribute must have 3 affine maps.";
3881 auto opIndexingMap = opIndexingMaps[opIndex];
3882 auto defaultIndexingMap = defaultIndexingMaps[opIndex];
3890 defaultIndexingMap, opIndex == 0)))
3900 if (m == 2 && r == 3)
3901 return WinogradConv2DFmr::F_2_3;
3902 if (m == 4 && r == 3)
3903 return WinogradConv2DFmr::F_4_3;
3904 if (m == 2 && r == 5)
3905 return WinogradConv2DFmr::F_2_5;
3906 return std::nullopt;
3911 case WinogradConv2DFmr::F_2_3:
3913 case WinogradConv2DFmr::F_4_3:
3915 case WinogradConv2DFmr::F_2_5:
3918 llvm_unreachable(
"Unkown WinogradConv2DFmr");
3925static FailureOr<SmallVector<SmallVector<int64_t>>>
3928 for (
auto map : maps) {
3929 AffineMapAttr attr = dyn_cast<AffineMapAttr>(map);
3933 for (
auto result : attr.getAffineMap().getResults()) {
3934 auto dim = dyn_cast<AffineDimExpr>(
result);
3937 pos.push_back(dim.getPosition());
3939 positions.push_back(pos);
3952 return indexingMaps;
3955bool MatmulOp::isDefaultIndexingMaps(Attribute attr) {
3956 ArrayAttr maps = dyn_cast<ArrayAttr>(attr);
3959 if (maps.size() != 3)
3964 return (*positions)[0] == SmallVector<int64_t>{0, 2} &&
3965 (*positions)[1] == SmallVector<int64_t>{2, 1} &&
3966 (*positions)[2] == SmallVector<int64_t>{0, 1};
3969SmallVector<utils::IteratorType> MatmulOp::getIteratorTypesArray() {
3970 return SmallVector<utils::IteratorType>{utils::IteratorType::parallel,
3971 utils::IteratorType::parallel,
3972 utils::IteratorType::reduction};
3975unsigned MatmulOp::getNumRegionArgs() {
return 3; }
3977std::string MatmulOp::getLibraryCallName() {
3981bool MatmulOp::hasDynamicIndexingMaps() {
return true; }
3985bool MatmulOp::hasUserDefinedMaps() {
3986 SmallVector<AffineMap, 3> defaultMaps =
3988 SmallVector<AffineMap, 3> explicitMaps = getIndexingMapsArray();
3989 return defaultMaps != explicitMaps;
3994void MatmulOp::regionBuilder(ImplicitLocOpBuilder &
b,
Block &block,
3995 ArrayRef<NamedAttribute> attrs,
3998 emitError() <<
"MatmulOp regionBuilder expects 3 args, got "
4003 "MatmulOp regionBuilder expects 3 args");
4004 RegionBuilderHelper helper(
b, block);
4005 SmallVector<Value> yields;
4007 TypeFn castVal = TypeFn::cast_signed;
4008 const auto *castIter = llvm::find_if(attrs, [&](
const NamedAttribute &attr) {
4009 return attr.
getName() ==
"cast";
4011 if (castIter != attrs.end()) {
4012 if (
auto attr = llvm::dyn_cast<TypeFnAttr>(castIter->getValue()))
4020 Value value3 = helper.buildBinaryFn(BinaryFn::mul, value1, value2,
emitError);
4021 if (!value1 || !value2 || !value3)
4023 Value value4 = helper.buildBinaryFn(BinaryFn::add, block.
getArgument(2),
4027 yields.push_back(value4);
4028 helper.yieldOutputs(yields);
4038bool MatmulOp::isValidLhsRhsBroadcastMap(AffineMap bcastMap) {
4039 assert(bcastMap.
getNumResults() == 1 &&
"Expected single result dim expr.");
4040 AffineExpr expr = bcastMap.
getResult(0);
4050 ArrayAttr arrayAttr;
4054 if (llvm::any_of(arrayAttr,
4055 [](
auto elt) {
return !dyn_cast<AffineMapAttr>(elt); }))
4057 <<
"element of indexing_maps array is not an affine_map";
4064 if (failed(indexingMapsAttr))
4067 if (*indexingMapsAttr ==
nullptr) {
4068 auto indexingMapAttrs = llvm::map_to_vector(
4069 MatmulOp::getDefaultIndexingMaps(parser.
getContext()),
4074 result.addAttribute(
"indexing_maps", *indexingMapsAttr);
4076 MatmulOp::getRegionBuilder());
4079void MatmulOp::print(OpAsmPrinter &p) {
4080 SmallVector<Attribute, 3> indexingMaps = llvm::map_to_vector<3>(
4081 MatmulOp::getDefaultIndexingMaps(
getContext()),
4082 [](AffineMap map) -> Attribute {
return AffineMapAttr::get(map); });
4083 if (!llvm::equal(getIndexingMaps(), indexingMaps))
4084 p <<
" indexing_maps = " << llvm::interleaved_array(getIndexingMaps());
4086 std::array<StringRef, 3> elidedAttrs = {
4087 "operandSegmentSizes",
"linalg.memoized_indexing_maps",
"indexing_maps"};
4093LogicalResult MatmulOp::verify() {
4095 if (!hasUserDefinedMaps())
4098 for (
unsigned opIndex = 0; opIndex < 2; opIndex++) {
4105LogicalResult MatmulOp::fold(FoldAdaptor, SmallVectorImpl<OpFoldResult> &) {
4109void MatmulOp::getEffects(
4110 SmallVectorImpl<SideEffects::EffectInstance<MemoryEffects::Effect>>
4112 if (hasPureTensorSemantics())
4121SmallVector<AffineMap>
4122MatmulTransposeAOp::getDefaultIndexingMaps(OpBuilder &builder) {
4123 AffineExpr d0, d1, d2;
4129 return {mapLHS, mapRHS, mapOut};
4133 ArrayAttr maps = dyn_cast<ArrayAttr>(attr);
4136 if (maps.size() != 3)
4139 if (failed(positions))
4151 MatmulOp::getRegionBuilder(), getDefaultIndexingMaps(builder));
4159 build(builder, state, inputs, outputs, attributes);
4160 auto res = dyn_cast<MatmulTransposeAOp>(builder.
create(state));
4161 assert(res &&
"builder didn't return the right type");
4171 MatmulOp::getRegionBuilder(), getDefaultIndexingMaps(builder));
4180 build(builder, state, resultTensorTypes, inputs, outputs, attributes);
4181 auto res = dyn_cast<MatmulTransposeAOp>(builder.
create(state));
4182 assert(res &&
"builder didn't return the right type");
4192 result.addAttribute(
"cast", cast);
4194 MatmulOp::getRegionBuilder(), getDefaultIndexingMaps(builder));
4203 build(builder, state, resultTensorTypes, inputs, outputs, cast, attributes);
4204 auto res = dyn_cast<MatmulTransposeAOp>(builder.
create(state));
4205 assert(res &&
"builder didn't return the right type");
4210 return dyn_cast_or_null<linalg::MatmulOp>(op) &&
4212 op->
getAttr(
"indexing_maps"));
4216MatmulTransposeBOp::getDefaultIndexingMaps(
OpBuilder &builder) {
4223 return {mapLHS, mapRHS, mapOut};
4227 ArrayAttr maps = dyn_cast<ArrayAttr>(attr);
4230 if (maps.size() != 3)
4233 if (failed(positions))
4245 MatmulOp::getRegionBuilder(), getDefaultIndexingMaps(builder));
4253 build(builder, state, inputs, outputs, attributes);
4254 auto res = dyn_cast<MatmulTransposeBOp>(builder.
create(state));
4255 assert(res &&
"builder didn't return the right type");
4265 MatmulOp::getRegionBuilder(), getDefaultIndexingMaps(builder));
4274 build(builder, state, resultTensorTypes, inputs, outputs, attributes);
4275 auto res = dyn_cast<MatmulTransposeBOp>(builder.
create(state));
4276 assert(res &&
"builder didn't return the right type");
4286 result.addAttribute(
"cast", cast);
4288 MatmulOp::getRegionBuilder(), getDefaultIndexingMaps(builder));
4297 build(builder, state, resultTensorTypes, inputs, outputs, cast, attributes);
4298 auto res = dyn_cast<MatmulTransposeBOp>(builder.
create(state));
4299 assert(res &&
"builder didn't return the right type");
4304 return dyn_cast_or_null<linalg::MatmulOp>(op) &&
4306 op->
getAttr(
"indexing_maps"));
4310BatchMatmulTransposeAOp::getDefaultIndexingMaps(
OpBuilder &builder) {
4317 return {mapLHS, mapRHS, mapOut};
4321 ArrayAttr maps = dyn_cast<ArrayAttr>(attr);
4324 if (maps.size() != 3)
4327 if (failed(positions))
4338 BatchMatmulOp::getRegionBuilder(),
4339 getDefaultIndexingMaps(builder));
4347 build(builder, state, inputs, outputs, attributes);
4348 auto res = dyn_cast<BatchMatmulTransposeAOp>(builder.
create(state));
4349 assert(res &&
"builder didn't return the right type");
4358 BatchMatmulOp::getRegionBuilder(),
4359 getDefaultIndexingMaps(builder));
4368 build(builder, state, resultTensorTypes, inputs, outputs, attributes);
4369 auto res = dyn_cast<BatchMatmulTransposeAOp>(builder.
create(state));
4370 assert(res &&
"builder didn't return the right type");
4378 result.addAttribute(
"cast", cast);
4380 BatchMatmulOp::getRegionBuilder(),
4381 getDefaultIndexingMaps(builder));
4390 build(builder, state, resultTensorTypes, inputs, outputs, cast, attributes);
4391 auto res = dyn_cast<BatchMatmulTransposeAOp>(builder.
create(state));
4392 assert(res &&
"builder didn't return the right type");
4397 return dyn_cast_or_null<linalg::BatchMatmulOp>(op) &&
4399 op->
getAttr(
"indexing_maps"));
4403BatchMatmulTransposeBOp::getDefaultIndexingMaps(
OpBuilder &builder) {
4410 return {mapLHS, mapRHS, mapOut};
4414 ArrayAttr maps = dyn_cast<ArrayAttr>(attr);
4417 if (maps.size() != 3)
4420 if (failed(positions))
4431 BatchMatmulOp::getRegionBuilder(),
4432 getDefaultIndexingMaps(builder));
4440 build(builder, state, inputs, outputs, attributes);
4441 auto res = dyn_cast<BatchMatmulTransposeBOp>(builder.
create(state));
4442 assert(res &&
"builder didn't return the right type");
4451 BatchMatmulOp::getRegionBuilder(),
4452 getDefaultIndexingMaps(builder));
4461 build(builder, state, resultTensorTypes, inputs, outputs, attributes);
4462 auto res = dyn_cast<BatchMatmulTransposeBOp>(builder.
create(state));
4463 assert(res &&
"builder didn't return the right type");
4471 result.addAttribute(
"cast", cast);
4473 BatchMatmulOp::getRegionBuilder(),
4474 getDefaultIndexingMaps(builder));
4483 build(builder, state, resultTensorTypes, inputs, outputs, cast, attributes);
4484 auto res = dyn_cast<BatchMatmulTransposeBOp>(builder.
create(state));
4485 assert(res &&
"builder didn't return the right type");
4490 return dyn_cast_or_null<linalg::BatchMatmulOp>(op) &&
4492 op->
getAttr(
"indexing_maps"));
4500 AffineMap outAffineMap = getIndexingMapsArray().pop_back_val();
4511 auto dimExpr = dyn_cast<AffineDimExpr>(
result);
4512 assert(dimExpr &&
"affine_map is a projected permutation");
4513 dimsInOutput[dimExpr.getPosition()] =
true;
4517 for (
auto dimOccursInOutput : dimsInOutput)
4518 iteratorTypes.push_back(dimOccursInOutput ? utils::IteratorType::parallel
4519 : utils::IteratorType::reduction);
4521 return iteratorTypes;
4524unsigned ContractOp::getNumRegionArgs() {
return 3; }
4527void ContractOp::regionBuilder(ImplicitLocOpBuilder &
b,
Block &block,
4528 ArrayRef<NamedAttribute> attrs,
4531 emitError() <<
"ContractOp regionBuilder expects 3 args, got "
4536 "ContractOp regionBuilder expects 3 args");
4537 RegionBuilderHelper helper(
b, block);
4539 TypeFn castSignedness = TypeFn::cast_signed;
4540 auto castIter = llvm::find_if(attrs, [&](
const NamedAttribute &attr) {
4541 return attr.
getName() ==
"cast";
4543 if (castIter != attrs.end()) {
4544 if (
auto attr = llvm::dyn_cast<TypeFnAttr>(castIter->getValue()))
4550 Value lhsAtOutType =
4551 helper.buildTypeFn(castSignedness, outType, block.
getArgument(0));
4552 Value rhsAtOutType =
4553 helper.buildTypeFn(castSignedness, outType, block.
getArgument(1));
4554 Value productAtOutType = helper.buildBinaryFn(BinaryFn::mul, lhsAtOutType,
4556 if (!productAtOutType)
4562 helper.yieldOutputs({
result});
4565ParseResult ContractOp::parse(OpAsmParser &parser, OperationState &
result) {
4567 if (
failed(indexingMapsAttr) || *indexingMapsAttr ==
nullptr)
4569 "expected 'indexing_maps' attribute");
4570 result.addAttribute(
"indexing_maps", *indexingMapsAttr);
4576void ContractOp::print(OpAsmPrinter &p) {
4577 p <<
" indexing_maps = " << llvm::interleaved_array(getIndexingMaps());
4579 p, getOperation(), getInputs(), getOutputs(),
4580 {
"indexing_maps",
"operandSegmentSizes"});
4583LogicalResult ContractOp::verify() {
4584 int iterationSpaceDims = -1;
4589 SmallVector<size_t> inOccurrences;
4590 SmallVector<size_t> outOccurrences;
4593 auto checkAffineMapAndType = [&](AffineMap affineMap, Type operandType,
4594 bool isInput) -> LogicalResult {
4597 return emitError(
"provided affine_map is not a projected permutation");
4600 if (
auto shapedType = dyn_cast<ShapedType>(operandType)) {
4602 return emitError(
"ranks of shaped operand and results of corresponding "
4603 "affine_map differ");
4605 return emitError(
"affine_map specifies shaped access while operand has "
4610 if (iterationSpaceDims == -1) {
4612 inOccurrences = SmallVector<size_t>(iterationSpaceDims, 0);
4613 outOccurrences = SmallVector<size_t>(iterationSpaceDims, 0);
4614 }
else if (iterationSpaceDims != (
int)affineMap.
getNumDims()) {
4615 return emitError(
"iteration spaces of provided affine_maps differ");
4619 for (AffineExpr affineExpr : affineMap.
getResults()) {
4620 auto affineDimExpr = dyn_cast<AffineDimExpr>(affineExpr);
4622 llvm_unreachable(
"affine_map is a projected permutation");
4625 inOccurrences[affineDimExpr.getPosition()] += 1;
4627 outOccurrences[affineDimExpr.getPosition()] += 1;
4633 for (
auto &&[affineMap, operandType, isInput] :
4634 llvm::zip(getIndexingMapsArray(), getOperandTypes(),
4635 SmallVector<bool>{
true,
true,
false})) {
4636 if (
failed(checkAffineMapAndType(affineMap, operandType, isInput)))
4640 bool hasContractingDim =
false;
4641 for (
size_t dimIndex = 0; dimIndex < (size_t)iterationSpaceDims; dimIndex++) {
4642 size_t inOccCount = inOccurrences[dimIndex];
4643 size_t outOccCount = outOccurrences[dimIndex];
4646 hasContractingDim |= inOccCount == 2 && outOccCount == 0;
4648 if (inOccCount == 0 && outOccCount == 0)
4649 return emitError() <<
"iteration space dim at index " << dimIndex
4650 <<
" not used to access any operand";
4661 if (inOccCount == 1 && outOccCount != 1)
4663 <<
"iteration space dim at index " << dimIndex
4664 <<
" is neither a contracting dim nor of parallel iteration type";
4667 if (!hasContractingDim)
4668 return emitError(
"'indexing_maps' do not specify a contracting dimension");
4673LogicalResult ContractOp::fold(FoldAdaptor, SmallVectorImpl<OpFoldResult> &) {
4677void ContractOp::getEffects(
4678 SmallVectorImpl<SideEffects::EffectInstance<MemoryEffects::Effect>>
4680 if (hasPureTensorSemantics())
4692SmallVector<AffineMap>
4693BatchMatmulOp::getDefaultIndexingMaps(MLIRContext *context) {
4694 AffineExpr d0, d1, d2, d3;
4695 SmallVector<AffineMap> indexingMaps;
4697 indexingMaps.push_back(
AffineMap::get(4, 0, {d0, d1, d3}, context));
4698 indexingMaps.push_back(
AffineMap::get(4, 0, {d0, d3, d2}, context));
4699 indexingMaps.push_back(
AffineMap::get(4, 0, {d0, d1, d2}, context));
4700 return indexingMaps;
4703bool BatchMatmulOp::isDefaultIndexingMaps(Attribute attr) {
4704 ArrayAttr maps = dyn_cast<ArrayAttr>(attr);
4707 if (maps.size() != 3)
4712 return (*positions)[0] == SmallVector<int64_t>{0, 1, 3} &&
4713 (*positions)[1] == SmallVector<int64_t>{0, 3, 2} &&
4714 (*positions)[2] == SmallVector<int64_t>{0, 1, 2};
4717SmallVector<utils::IteratorType> BatchMatmulOp::getIteratorTypesArray() {
4718 return SmallVector<utils::IteratorType>{
4719 utils::IteratorType::parallel, utils::IteratorType::parallel,
4720 utils::IteratorType::parallel, utils::IteratorType::reduction};
4723unsigned BatchMatmulOp::getNumRegionArgs() {
return 3; }
4725std::string BatchMatmulOp::getLibraryCallName() {
4731bool BatchMatmulOp::hasUserDefinedMaps() {
4732 SmallVector<AffineMap, 3> defaultMaps =
4734 SmallVector<AffineMap, 3> explicitMaps = getIndexingMapsArray();
4735 return defaultMaps != explicitMaps;
4745bool BatchMatmulOp::isValidLhsRhsBroadcastMap(AffineMap bcastMap,
bool isLHS) {
4747 "Expected less than 3 result dim expr.");
4748 bool isValid =
false;
4749 enum Indices { batchPos, mPos, nPos, kPos };
4751 AffineExpr expr = bcastMap.
getResult(0);
4754 AffineExpr expr0 = bcastMap.
getResult(0);
4755 AffineExpr expr1 = bcastMap.
getResult(1);
4760 : ((expr0.isFunctionOfDim(batchPos) &&
4761 expr1.isFunctionOfDim(kPos)) ||
4762 (expr0.isFunctionOfDim(kPos) && expr1.isFunctionOfDim(nPos)));
4767void BatchMatmulOp::regionBuilder(
4768 ImplicitLocOpBuilder &
b,
Block &block, ArrayRef<NamedAttribute> attrs,
4771 emitError() <<
"BatchMatmulOp regionBuilder expects 3 args, got "
4776 "BatchMatmulOp regionBuilder expects 3 args");
4777 RegionBuilderHelper helper(
b, block);
4778 SmallVector<Value> yields;
4780 TypeFn castVal = TypeFn::cast_signed;
4781 auto castIter = llvm::find_if(attrs, [&](
const NamedAttribute &attr) {
4782 return attr.
getName() ==
"cast";
4784 if (castIter != attrs.end()) {
4785 if (
auto attr = llvm::dyn_cast<TypeFnAttr>(castIter->getValue()))
4790 Value castValA = helper.buildTypeFn(castVal, toType, block.
getArgument(0));
4791 Value castValB = helper.buildTypeFn(castVal, toType, block.
getArgument(1));
4793 helper.buildBinaryFn(BinaryFn::mul, castValA, castValB,
emitError);
4794 if (!castValA || !castValB || !mulVal)
4796 Value addVal = helper.buildBinaryFn(BinaryFn::add, block.
getArgument(2),
4800 yields.push_back(addVal);
4801 helper.yieldOutputs(yields);
4804ParseResult BatchMatmulOp::parse(OpAsmParser &parser, OperationState &
result) {
4805 SmallVector<Attribute, 3> indexingMapsAttr;
4817 if (!isa<AffineMapAttr>(mapAttr)) {
4819 "expected affine map attribute");
4821 indexingMapsAttr.push_back(mapAttr);
4831 if (indexingMapsAttr.empty()) {
4832 indexingMapsAttr = llvm::map_to_vector(
4833 BatchMatmulOp::getDefaultIndexingMaps(parser.
getContext()),
4834 [](AffineMap map) -> Attribute { return AffineMapAttr::get(map); });
4836 result.addAttribute(
"indexing_maps",
4839 return ::parseNamedStructuredOp(parser,
result,
4840 BatchMatmulOp::getNumRegionArgs(),
4841 BatchMatmulOp::getRegionBuilder());
4844void BatchMatmulOp::print(OpAsmPrinter &p) {
4845 SmallVector<Attribute, 3> indexingMaps = llvm::map_to_vector<3>(
4846 BatchMatmulOp::getDefaultIndexingMaps(
getContext()),
4847 [](AffineMap map) -> Attribute {
return AffineMapAttr::get(map); });
4848 if (!llvm::equal(getIndexingMaps(), indexingMaps))
4849 p <<
" indexing_maps = " << llvm::interleaved_array(getIndexingMaps());
4851 std::array<StringRef, 3> elidedAttrs = {
4852 "operandSegmentSizes",
"linalg.memoized_indexing_maps",
"indexing_maps"};
4858LogicalResult BatchMatmulOp::verify() {
4861 if (!hasUserDefinedMaps())
4864 for (
unsigned opIndex = 0; opIndex < 3; opIndex++) {
4871LogicalResult BatchMatmulOp::fold(FoldAdaptor,
4872 SmallVectorImpl<OpFoldResult> &) {
4876void BatchMatmulOp::getEffects(
4877 SmallVectorImpl<SideEffects::EffectInstance<MemoryEffects::Effect>>
4879 if (hasPureTensorSemantics())
4893struct ArityGroupAndKind {
4895 ElementwiseArityGroup arityGroup;
4901 TernaryFn ternaryFn;
4905unsigned getArityGroupAsUInt(ElementwiseArityGroup arityGroup) {
4906 return static_cast<unsigned>(arityGroup);
4911 constexpr int lastUnary =
static_cast<int>(ElementwiseCaseLimits::LastUnary);
4912 constexpr int lastBinary =
4913 static_cast<int>(ElementwiseCaseLimits::LastBinary);
4914 constexpr int lastTernary =
4915 static_cast<int>(ElementwiseCaseLimits::LastTernary);
4917 int val =
static_cast<int>(kind);
4918 ArityGroupAndKind
result;
4920 if (val < lastUnary) {
4921 result.arityGroup = ElementwiseArityGroup::Unary;
4922 result.kind.unaryFn =
static_cast<UnaryFn
>(val);
4925 if (val < lastBinary) {
4926 result.arityGroup = ElementwiseArityGroup::Binary;
4927 result.kind.binaryFn =
static_cast<BinaryFn
>(val - lastUnary);
4930 if (val >= lastTernary) {
4931 llvm_unreachable(
"unhandled ElementwiseFn");
4933 result.arityGroup = ElementwiseArityGroup::Ternary;
4934 result.kind.ternaryFn =
static_cast<TernaryFn
>(val - lastBinary);
4939 auto rank = getResultRank();
4944ElementwiseOp::getDefaultIndexingMaps(
unsigned numMaps,
unsigned numDims,
4950ParseResult ElementwiseOp::parse(OpAsmParser &parser, OperationState &
result) {
4953 mlir::linalg::ElementwiseKind elemwiseKindVal;
4958 auto elemwiseKindAttr = dyn_cast<ElementwiseKindAttr>(attr);
4959 if (!elemwiseKindAttr)
4961 "expected ElementwiseKind attribute");
4962 elemwiseKindVal = elemwiseKindAttr.getValue();
4965 "expected operation 'kind' attribute");
4968 "kind", ElementwiseKindAttr::get(parser.
getContext(), elemwiseKindVal));
4971 SmallVector<Attribute, 3> indexingMapsAttr;
4981 if (!isa<AffineMapAttr>(mapAttr))
4983 "expected affine map attribute");
4984 indexingMapsAttr.push_back(mapAttr);
4995 getArityGroupAsUInt(arityGroupAndKind.arityGroup) + 1 ;
4997 ElementwiseOp::getRegionBuilder())) {
4999 "unable to parse elemwise op");
5003 if (indexingMapsAttr.empty()) {
5006 auto resultType =
result.operands[
result.operands.size() - 1].getType();
5007 auto shapedType = llvm::dyn_cast<ShapedType>(resultType);
5010 "return type needs to be shaped type");
5011 auto numDims = shapedType.getRank();
5012 indexingMapsAttr = llvm::map_to_vector(
5013 ElementwiseOp::getDefaultIndexingMaps(numRegionArgs, numDims,
5015 [](AffineMap map) -> Attribute { return AffineMapAttr::get(map); });
5018 result.addAttribute(
"indexing_maps",
5023void ElementwiseOp::print(OpAsmPrinter &p) {
5026 SmallVector<StringRef, 3> elidedAttrs = {
"operandSegmentSizes",
"kind",
5030 unsigned numDims = getResultRank();
5032 SmallVector<Attribute, 3> indexingMaps = llvm::map_to_vector<3>(
5033 ElementwiseOp::getDefaultIndexingMaps(arity + 1 , numDims,
5035 [](AffineMap map) -> Attribute {
return AffineMapAttr::get(map); });
5037 if (!llvm::equal(getIndexingMaps(), indexingMaps))
5038 p <<
" indexing_maps = " << llvm::interleaved_array(getIndexingMaps());
5046void ElementwiseOp::regionBuilder(
5047 ImplicitLocOpBuilder &
b,
Block &block, ArrayRef<NamedAttribute> attrs,
5049 ElementwiseKind elemwiseKind;
5050 for (
auto attr : attrs) {
5051 if (attr.getName() ==
b.getStringAttr(
"kind")) {
5052 auto kindAttr = dyn_cast<ElementwiseKindAttr>(attr.getValue());
5053 assert(kindAttr &&
"op kind attribute incorrectly set");
5054 elemwiseKind = kindAttr.getValue();
5060 auto arityGroup = groupAndKind.arityGroup;
5061 auto kind = groupAndKind.kind;
5063 getArityGroupAsUInt(arityGroup) + 1 ) {
5064 emitError() <<
"Elementwise regionBuilder expects "
5065 << (getArityGroupAsUInt(arityGroup) + 1) <<
" args, got "
5070 getArityGroupAsUInt(arityGroup) + 1
5071 &&
"Elementwise regionBuilder number of block args mismatch");
5073 RegionBuilderHelper helper(
b, block);
5074 SmallVector<Value> yields;
5077 if (arityGroup == ElementwiseArityGroup::Unary) {
5080 }
else if (arityGroup == ElementwiseArityGroup::Binary) {
5084 }
else if (arityGroup == ElementwiseArityGroup::Ternary) {
5089 assert(
false &&
"found unhandled category in elemwise");
5092 yields.push_back(
result);
5093 helper.yieldOutputs(yields);
5096LogicalResult ElementwiseOp::fold(FoldAdaptor,
5097 SmallVectorImpl<OpFoldResult> &) {
5101void ElementwiseOp::getEffects(
5102 SmallVectorImpl<SideEffects::EffectInstance<MemoryEffects::Effect>>
5104 if (hasPureTensorSemantics())
5117template <
typename OpTy,
typename>
5120 ShapedType packedType = (std::is_same<OpTy, PackOp>::value)
5121 ? packOrUnPack.getDestType()
5122 : packOrUnPack.getSourceType();
5123 ShapedType unpackedType = (std::is_same<OpTy, PackOp>::value)
5124 ? packOrUnPack.getSourceType()
5125 : packOrUnPack.getDestType();
5127 packedType.getShape().take_front(unpackedType.getRank()));
5128 if (!packOrUnPack.getOuterDimsPerm().empty()) {
5149 for (
auto it : llvm::zip(cast<ShapedType>(newPackedTy)
5151 .take_back(mixedTiles.size()),
5153 int64_t dimSize = std::get<0>(it);
5154 if (dimSize == ShapedType::kDynamic) {
5155 newMixedTileSizes.push_back(std::get<1>(it));
5158 newMixedTileSizes.push_back(rewriter.
getIndexAttr(dimSize));
5161 return newMixedTileSizes;
5164template <
typename OpTy>
5168 static_assert(llvm::is_one_of<OpTy, PackOp, UnPackOp>::value,
5169 "applies to only pack or unpack operations");
5170 int64_t destRank = op.getDestRank();
5172 for (
auto dim : llvm::seq<int64_t>(0, destRank))
5173 reifiedReturnShapes[0][dim] =
5178template <
typename OpTy>
5180 static_assert(llvm::is_one_of<OpTy, PackOp, UnPackOp>::value,
5181 "applies to only pack or unpack operations");
5185 assert(tiles.size() == dimsToTile.size() &&
5186 "tiles must match indices of dimension to block");
5188 for (
auto i : llvm::seq<int64_t>(0, dimsToTile.size()))
5189 dimAndTileMapping[dimsToTile[i]] = tiles[i];
5190 return dimAndTileMapping;
5193template <
typename OpTy>
5195 static_assert(llvm::is_one_of<OpTy, PackOp, UnPackOp>::value,
5196 "applies to only pack or unpack operations");
5199 unsigned dynamicValIndex = 0;
5200 for (
int64_t staticTile : op.getStaticInnerTiles()) {
5201 if (ShapedType::isStatic(staticTile))
5204 mixedInnerTiles.push_back(op.getInnerTiles()[dynamicValIndex++]);
5206 return mixedInnerTiles;
5209template <
typename OpTy>
5211 static_assert(llvm::is_one_of<OpTy, PackOp, UnPackOp>::value,
5212 "applies to only pack or unpack operations");
5225 size_t dimsPosSize = dimsPos.size();
5226 if (dimsPosSize > rank)
5229 if (dimsPosSize != uniqued.size())
5231 return llvm::any_of(dimsPos, [rank](
int64_t dimPos) {
5232 return dimPos < 0 || dimPos >=
static_cast<int64_t>(rank);
5236template <
typename OpTy>
5238 static_assert(llvm::is_one_of<OpTy, PackOp, UnPackOp>::value,
5239 "applies to only pack or unpack operations");
5240 Operation *op = packOrUnPack.getOperation();
5250 if (!packOrUnPack.getSourceType().hasRank() ||
5251 !packOrUnPack.getDestType().hasRank())
5252 return op->
emitError(
"expected both source and destination to have rank");
5255 if (!packOrUnPack.hasPureBufferSemantics() &&
5256 !packOrUnPack.hasPureTensorSemantics())
5257 return op->
emitError(
"mixing tensor and buffer semantics is not allowed");
5258 const unsigned numResults = packOrUnPack.getNumResults();
5259 if (packOrUnPack.hasPureTensorSemantics() && numResults != 1)
5260 return op->
emitError(
"expected 1 result, got ") << numResults;
5261 if (packOrUnPack.hasPureBufferSemantics() && numResults != 0)
5262 return op->
emitError(
"expected 0 results, got ") << numResults;
5266 if (hasZeros(mixedTiles))
5267 return op->
emitError(
"invalid zero tile factor");
5270 ShapedType unpackedType = (std::is_same<OpTy, PackOp>::value)
5271 ? packOrUnPack.getSourceType()
5272 : packOrUnPack.getDestType();
5273 size_t unpackedRank = unpackedType.getRank();
5277 return op->
emitError(
"invalid inner_dims_pos vector");
5279 return op->
emitError(
"invalid outer_dims_perm vector");
5280 if (!outerDimPerm.empty() && outerDimPerm.size() != unpackedRank)
5281 return op->
emitError(
"outer_dims_perm must be a permutation or empty");
5285 if (mixedTiles.size() > unpackedRank) {
5286 return op->
emitError(
"tiling factors must be less than or equal to the "
5287 "input rank for pack or output rank for unpack");
5289 if (mixedTiles.size() != innerDimsPos.size()) {
5291 "tiling factors must equal the number of dimensions to tile");
5294 ShapedType packedType = (std::is_same<OpTy, PackOp>::value)
5295 ? packOrUnPack.getDestType()
5296 : packOrUnPack.getSourceType();
5297 size_t packedRank = packedType.getRank();
5299 size_t expectedPackedRank = unpackedRank + mixedTiles.size();
5300 if (expectedPackedRank != packedRank) {
5302 "packed rank != (unpacked rank + num tiling factors), got ")
5303 << packedRank <<
" != " << expectedPackedRank;
5310 unpackedType.getShape(), packOrUnPack.getStaticTiles(),
5311 packOrUnPack.getInnerDimsPos(), packOrUnPack.getOuterDimsPerm());
5312 for (
auto it : llvm::enumerate(llvm::zip(
5313 packedType.getShape().take_back(mixedTiles.size()), mixedTiles))) {
5314 int64_t dimSize = std::get<0>(it.value());
5316 llvm::dyn_cast_if_present<Attribute>(std::get<1>(it.value()))) {
5317 IntegerAttr intAttr = dyn_cast_or_null<IntegerAttr>(attr);
5318 int64_t staticTileSize = intAttr.getValue().getSExtValue();
5319 if (dimSize != staticTileSize)
5321 "mismatch in inner tile sizes specified and shaped of "
5322 "tiled dimension in the packed type at index ")
5323 << it.index() <<
": got " << dimSize <<
" != " << staticTileSize;
5324 }
else if (!ShapedType::isDynamic(dimSize)) {
5325 return op->
emitError(
"mismatch in inner tile sizes specified at index ")
5326 << it.index() <<
": got static shape " << dimSize
5327 <<
" but dynamic tile size";
5332 auto elementType = unpackedType.getElementType();
5333 Type expectedType, actualType;
5334 if (packOrUnPack.hasPureTensorSemantics()) {
5335 expectedType = RankedTensorType::get(expectedPackedShape, elementType);
5336 actualType = RankedTensorType::get(packedType.getShape(), elementType);
5338 expectedType = MemRefType::get(expectedPackedShape, elementType);
5339 actualType = MemRefType::get(packedType.getShape(), elementType);
5342 << expectedType <<
" for the packed domain value, got "
5355struct PackOrUnPackTransposeResult {
5362template <
typename OpTy>
5363static PackOrUnPackTransposeResult
5367 static_assert(llvm::is_one_of<OpTy, PackOp, UnPackOp>::value,
5368 "applies to only pack or unpack operations");
5369 assert((!innerPermutation.empty() || !outerPermutation.empty()) &&
5370 "some permutation must be non-empty");
5371 PackOrUnPackTransposeResult metadata;
5372 metadata.innerDimsPos =
5374 metadata.innerTiles =
5376 int64_t numOuterDims = std::is_same<OpTy, PackOp>::value
5377 ? packOrUnPackOp.getSourceRank()
5378 : packOrUnPackOp.getDestRank();
5379 metadata.outerDimsPerm =
5380 packOrUnPackOp.getOuterDimsPerm().empty()
5381 ? llvm::to_vector(llvm::seq<int64_t>(0, numOuterDims))
5383 if (!innerPermutation.empty()) {
5384 assert(innerPermutation.size() == metadata.innerDimsPos.size() &&
5386 "invalid inner permutation");
5390 if (!outerPermutation.empty()) {
5391 assert(outerPermutation.size() == metadata.outerDimsPerm.size() &&
5393 "invalid outer permutation");
5404 if (!getResults().empty())
5405 setNameFn(getResult(),
"pack");
5415 Type sourceType, destType, resultType;
5432 SmallVector<int64_t> outerDimsPermVec;
5435 if (parser.parseInteger(value))
5437 outerDimsPermVec.push_back(value);
5447 SmallVector<int64_t> innerDimsPosVec;
5450 if (parser.parseInteger(value))
5452 innerDimsPosVec.push_back(value);
5464 for (
auto val : staticTilesAttr.
asArrayRef())
5465 staticTiles.push_back(val);
5482 bool isMemRef = llvm::isa<MemRefType>(sourceType);
5485 "pack/unpack requires '->' and destination type");
5489 resultType = destType;
5495 if (!paddingValue.empty() &&
5500 if (!dynamicTiles.empty() &&
5505 result.addAttribute(
"static_inner_tiles",
5507 result.addAttribute(
"inner_dims_pos", innerDimsPos);
5509 result.addAttribute(
"outer_dims_perm", outerDimsPerm);
5511 SmallVector<int32_t> segmentSizes = {
5512 1, 1,
static_cast<int32_t
>(paddingValue.size()),
5513 static_cast<int32_t
>(dynamicTiles.size())};
5514 result.addAttribute(
"operandSegmentSizes",
5518 result.addTypes(resultType);
5523void PackOp::print(OpAsmPrinter &p) {
5524 p <<
" " << getSource();
5526 if (getPaddingValue()) {
5527 p <<
" padding_value(" << getPaddingValue() <<
" : "
5528 << getPaddingValue().getType() <<
")";
5531 if (!getOuterDimsPerm().empty()) {
5532 p <<
" outer_dims_perm = [";
5533 llvm::interleaveComma(getOuterDimsPerm(), p);
5537 p <<
" inner_dims_pos = [";
5538 llvm::interleaveComma(getInnerDimsPos(), p);
5541 p <<
" inner_tiles = ";
5544 p <<
" into " << getDest();
5547 {
"static_inner_tiles",
"inner_dims_pos",
5548 "outer_dims_perm",
"operandSegmentSizes"});
5550 p <<
" : " << getSource().getType();
5551 p <<
" -> " << getDest().getType();
5554void PackOp::build(OpBuilder &builder, OperationState &state, Value source,
5555 Value dest, ArrayRef<int64_t> innerDimsPos,
5556 ArrayRef<OpFoldResult> innerTiles,
5557 std::optional<Value> paddingValue,
5558 ArrayRef<int64_t> outerDimsPerm) {
5559 assert(innerDimsPos.size() == innerTiles.size() &&
5560 "number of tile sizes specified must match the specified number of "
5561 "original dimensions to be tiled");
5562 SmallVector<int64_t> staticTileSizes;
5563 SmallVector<Value> dynamicTileSizes;
5565 build(builder, state, dest.
getType(), source, dest,
5566 paddingValue ? *paddingValue :
nullptr,
5567 outerDimsPerm.empty() ?
nullptr
5574PackOp::reifyResultShapes(OpBuilder &builder,
5583SmallVector<OpFoldResult> PackOp::getMixedTiles() {
5587SmallVector<int64_t> PackOp::getStaticTiles() {
5591ArrayRef<int64_t> PackOp::getAllOuterDims() {
5592 ShapedType inputType = getSourceType();
5593 int64_t inputRank = inputType.getRank();
5594 return getDestType().getShape().take_front(inputRank);
5597SmallVector<int64_t> PackOp::getTiledOuterDims() {
5598 auto innerDimsPos = getInnerDimsPos();
5599 SmallVector<int64_t> outerDims(getAllOuterDims());
5600 SmallVector<int64_t> res;
5603 SmallVector<int64_t> outerDimPermInv(getOuterDimsPerm());
5605 if (!outerDimPermInv.empty())
5609 for (
auto index : innerDimsPos)
5610 res.push_back(outerDims[index]);
5615bool PackOp::requirePaddingValue(ArrayRef<int64_t> inputShape,
5616 ArrayRef<int64_t> innerDimsPos,
5617 ArrayRef<int64_t> outputShape,
5618 ArrayRef<int64_t> outerDimsPerm,
5619 ArrayRef<OpFoldResult> innerTiles) {
5620 SmallVector<int64_t> outputTileSizes(
5621 outputShape.take_front(inputShape.size()));
5622 if (!outerDimsPerm.empty()) {
5623 assert(outerDimsPerm.size() == outputTileSizes.size() &&
5624 "expected output and outer_dims_perm to have same size");
5628 for (
auto [pos, tileSize] : llvm::zip_equal(innerDimsPos, innerTiles)) {
5629 if (ShapedType::isDynamic(inputShape[pos]))
5632 if (!constantTile) {
5633 if (ShapedType::isStatic(outputTileSizes[pos]) &&
5634 (inputShape[pos] % outputTileSizes[pos] != 0))
5637 assert(*constantTile != 0 &&
"static tile size can't be zero");
5638 if (inputShape[pos] % (*constantTile) != 0) {
5646bool PackOp::requirePaddingValueStrict(ArrayRef<int64_t> inputShape,
5647 ArrayRef<int64_t> innerDimsPos,
5648 ArrayRef<int64_t> outputShape,
5649 ArrayRef<int64_t> outerDimsPerm,
5650 ArrayRef<OpFoldResult> innerTiles) {
5651 SmallVector<int64_t> outputTileSizes(
5652 outputShape.take_front(inputShape.size()));
5653 if (!outerDimsPerm.empty()) {
5654 assert(outerDimsPerm.size() == outputTileSizes.size() &&
5655 "expected output and outer_dims_perm to have same size");
5659 for (
auto [pos, tileSize] : llvm::zip_equal(innerDimsPos, innerTiles)) {
5660 if (ShapedType::isDynamic(inputShape[pos]) ||
5661 ShapedType::isDynamic(outputTileSizes[pos]))
5666 assert(*constantTile != 0 &&
"static tile size can't be zero");
5667 if (inputShape[pos] % (*constantTile) != 0)
5673LogicalResult PackOp::verify() {
5680 auto paddingValue = getPaddingValue();
5684 << getSourceType().getElementType()
5685 <<
" but got: " << paddingValue.getType();
5688 if (!paddingValue &&
5689 requirePaddingValue(getSourceType().
getShape(), getInnerDimsPos(),
5690 getDestType().
getShape(), getOuterDimsPerm(),
5693 "invalid tile factor or output size provided. Only full tiles are "
5694 "supported when padding_value is not set");
5701static SmallVector<int64_t>
5704 for (
auto o : ofrs) {
5706 if (llvm::dyn_cast_if_present<Value>(o))
5707 result.push_back(ShapedType::kDynamic);
5719 for (
auto tiledDim : llvm::enumerate(llvm::to_vector(innerDimsPos))) {
5720 if (ShapedType::isDynamic(resultShape[tiledDim.value()]))
5722 if (ShapedType::isDynamic(innerTileSizes[tiledDim.index()])) {
5723 resultShape[tiledDim.value()] = ShapedType::kDynamic;
5726 resultShape[tiledDim.value()] = llvm::divideCeilSigned(
5727 resultShape[tiledDim.value()], innerTileSizes[tiledDim.index()]);
5731 if (!outerDimsPerm.empty())
5735 resultShape.append(innerTileSizes.begin(), innerTileSizes.end());
5739SmallVector<OpFoldResult> PackOp::getResultShape(
5740 OpBuilder &builder, Location loc, ArrayRef<OpFoldResult> sourceDims,
5741 ArrayRef<OpFoldResult> innerTileSizes, ArrayRef<int64_t> innerDimsPos,
5742 ArrayRef<int64_t> outerDimsPerm) {
5743 SmallVector<OpFoldResult> resultDims = llvm::to_vector(sourceDims);
5747 AffineExpr ceilDivExpr = s0.
ceilDiv(s1);
5748 for (
auto tiledDim : llvm::enumerate(llvm::to_vector(innerDimsPos))) {
5750 builder, loc, ceilDivExpr,
5751 {resultDims[tiledDim.value()], innerTileSizes[tiledDim.index()]});
5753 if (!outerDimsPerm.empty())
5755 resultDims.append(innerTileSizes.begin(), innerTileSizes.end());
5757 SmallVector<int64_t> resultTypeShape =
5760 innerDimsPos, outerDimsPerm);
5766 for (
unsigned i = 0; i < resultDims.size(); ++i) {
5767 if (ShapedType::isStatic(resultTypeShape[i]))
5776RankedTensorType PackOp::inferPackedTensorType(
5777 RankedTensorType sourceType, ArrayRef<int64_t> innerTileSizes,
5778 ArrayRef<int64_t> innerDimsPos, ArrayRef<int64_t> outerDimsPerm) {
5779 SmallVector<int64_t> resultShape = inferPackedShape(
5780 sourceType.getShape(), innerTileSizes, innerDimsPos, outerDimsPerm);
5781 return RankedTensorType::get(resultShape, sourceType.getElementType());
5784MemRefType PackOp::inferPackedMemRefType(MemRefType sourceType,
5785 ArrayRef<int64_t> innerTileSizes,
5786 ArrayRef<int64_t> innerDimsPos,
5787 ArrayRef<int64_t> outerDimsPerm) {
5788 SmallVector<int64_t> resultShape = inferPackedShape(
5789 sourceType.getShape(), innerTileSizes, innerDimsPos, outerDimsPerm);
5790 return MemRefType::get(resultShape, sourceType.getElementType());
5793Value PackOp::createDestinationTensor(OpBuilder &
b, Location loc, Value source,
5794 ArrayRef<OpFoldResult> innerTileSizes,
5795 ArrayRef<int64_t> innerDimsPos,
5796 ArrayRef<int64_t> outerDimsPerm) {
5797 AffineExpr dim0, dim1;
5799 auto ceilDiv = [&](OpFoldResult v1, OpFoldResult v2) -> OpFoldResult {
5804 SmallVector<OpFoldResult> mixedSizes;
5805 for (
auto [index, value] : llvm::enumerate(
5806 llvm::cast<RankedTensorType>(source.
getType()).getShape())) {
5807 if (ShapedType::isDynamic(value))
5808 mixedSizes.push_back(
5809 tensor::DimOp::create(
b, loc, source, index).getResult());
5811 mixedSizes.push_back(
b.getIndexAttr(value));
5813 for (
auto it : llvm::zip(innerDimsPos, innerTileSizes)) {
5814 int64_t dimPos = std::get<0>(it);
5815 OpFoldResult tileSize = std::get<1>(it);
5816 mixedSizes[dimPos] = ceilDiv(mixedSizes[dimPos], tileSize);
5818 if (!outerDimsPerm.empty())
5821 mixedSizes.append(innerTileSizes.begin(), innerTileSizes.end());
5822 auto elemType = llvm::cast<ShapedType>(source.
getType()).getElementType();
5823 return tensor::EmptyOp::create(
b, loc, mixedSizes, elemType);
5826PackOp PackOp::createTransposedClone(OpBuilder &
b, Location loc,
5827 ArrayRef<int64_t> innerPermutation,
5828 ArrayRef<int64_t> outerPermutation) {
5830 *
this, innerPermutation, outerPermutation);
5831 Value transposedDest =
5832 createDestinationTensor(
b, loc, getSource(), metadata.innerTiles,
5833 metadata.innerDimsPos, metadata.outerDimsPerm);
5834 return PackOp::create(
b, loc, getSource(), transposedDest,
5835 metadata.innerDimsPos, metadata.innerTiles,
5836 getPaddingValue(), metadata.outerDimsPerm);
5839template <
typename OpTy>
5844 if (op.hasPureTensorSemantics())
5847 for (
OpOperand &opOperand : op.getOperation()->getOpOperands()) {
5848 if (!llvm::isa<MemRefType>(opOperand.
get().
getType()))
5851 if (&opOperand == &op.getSourceMutable()) {
5855 }
else if (&opOperand == &op.getDestMutable()) {
5866void PackOp::getEffects(
5872void UnPackOp::getEffects(
5879template <
typename OpTy>
5881 static_assert(llvm::is_one_of<OpTy, PackOp, UnPackOp>::value,
5882 "applies to only pack or unpack operations");
5883 ShapedType packedType = (std::is_same<OpTy, PackOp>::value)
5885 : op.getSourceType();
5887 for (
auto [dimDest,
tile] : llvm::zip(
5888 packedType.getShape().take_back(mixedTiles.size()), mixedTiles)) {
5890 if (!constTileSize || ShapedType::isDynamic(dimDest))
5897 if (!hasPureTensorSemantics())
5899 if (getPaddingValue())
5914 if (packOp.getInnerDimsPos() != unPackOp.getInnerDimsPos())
5916 if (packOp.getOuterDimsPerm() == unPackOp.getOuterDimsPerm())
5928 auto packTiles = packOp.getMixedTiles();
5929 auto unPackTiles = unPackOp.getMixedTiles();
5930 if (packTiles.size() != unPackTiles.size())
5932 for (
size_t i = 0, e = packTiles.size(); i < e; i++) {
5941 auto srcType = op.getSourceType();
5942 auto innerDimsPos = op.getInnerDimsPos();
5943 auto innerTiles = op.getStaticInnerTiles();
5944 if (ShapedType::isDynamicShape(innerTiles))
5946 for (
auto [pos, tileSize] : llvm::zip_equal(innerDimsPos, innerTiles)) {
5947 if (srcType.isDynamicDim(pos) && tileSize != 1)
5950 return !PackOp::requirePaddingValue(
5951 srcType.getShape(), op.getInnerDimsPos(), op.getDestType().getShape(),
5952 op.getOuterDimsPerm(), op.getMixedTiles());
5959 bool changeNeeded =
false;
5960 srcShape.assign(packOp.getSourceType().getShape().begin(),
5961 packOp.getSourceType().getShape().end());
5962 destShape.assign(packOp.getDestType().getShape().begin(),
5963 packOp.getDestType().getShape().end());
5964 llvm::SmallSetVector<int64_t, 4> innerDims;
5965 innerDims.insert_range(packOp.getInnerDimsPos());
5967 if (!packOp.getOuterDimsPerm().empty())
5969 int srcRank = packOp.getSourceRank();
5970 for (
auto i : llvm::seq<int64_t>(0, srcRank)) {
5971 if (innerDims.contains(i))
5975 if (!inverseOuterDimsPerm.empty())
5976 destPos = inverseOuterDimsPerm[srcPos];
5977 if (ShapedType::isDynamic(srcShape[srcPos]) ==
5978 ShapedType::isDynamic(destShape[destPos])) {
5981 int64_t size = srcShape[srcPos];
5982 if (ShapedType::isDynamic(size))
5983 size = destShape[destPos];
5984 srcShape[srcPos] = size;
5985 destShape[destPos] = size;
5986 changeNeeded =
true;
5988 return changeNeeded;
5991LogicalResult PackOp::canonicalize(PackOp packOp,
PatternRewriter &rewriter) {
5993 if (!packOp.hasPureTensorSemantics())
5997 if (
auto unPackOp = packOp.getSource().getDefiningOp<UnPackOp>()) {
5998 if (unPackOp.getSourceType() == packOp.getDestType() &&
5999 !packOp.getPaddingValue() &&
6002 rewriter.
replaceOp(packOp, unPackOp.getSource());
6010 packOp.getPaddingValueMutable().clear();
6016 SmallVector<int64_t> srcShape, destShape;
6018 Location loc = packOp.getLoc();
6019 Value source = packOp.getSource();
6020 if (srcShape != packOp.getSourceType().getShape()) {
6021 auto newSrcType = packOp.getSourceType().clone(srcShape);
6023 tensor::CastOp::create(rewriter, loc, newSrcType, packOp.getSource());
6025 Value dest = packOp.getDest();
6026 ShapedType originalResultType = packOp.getDestType();
6027 bool needUpdateDestType = (destShape != originalResultType.getShape());
6028 if (needUpdateDestType) {
6029 auto newDestType = packOp.getDestType().clone(destShape);
6031 tensor::CastOp::create(rewriter, loc, newDestType, packOp.getDest());
6034 packOp.getSourceMutable().assign(source);
6035 packOp.getDestMutable().assign(dest);
6036 packOp.getResult().setType(cast<RankedTensorType>(dest.
getType()));
6039 if (needUpdateDestType) {
6041 auto castOp = tensor::CastOp::create(rewriter, loc, originalResultType,
6042 packOp.getResult());
6051template <
typename PackOrUnpackOp>
6053 static_assert(std::is_same<PackOrUnpackOp, PackOp>::value ||
6054 std::is_same<PackOrUnpackOp, UnPackOp>::value,
6055 "Function meant for pack/unpack");
6060 int64_t numPackedDims = innerDimsPos.size();
6061 auto orderedDims = llvm::to_vector<4>(llvm::seq<int64_t>(0, numPackedDims));
6062 if (orderedDims != innerDimsPos) {
6068 int64_t packedRank = packedTensorType.getRank();
6078 return llvm::all_of(
6079 llvm::seq<int64_t>(0, packedRank - numPackedDims),
6080 [&packedShape](
int64_t i) {
return packedShape[i] == 1; });
6083bool PackOp::isLikePad() {
6084 auto packedTensorType =
6085 llvm::cast<ShapedType>((*this)->getResultTypes().front());
6089::mlir::LogicalResult
6090PackOp::fold(FoldAdaptor adaptor,
6092 if (!hasPureTensorSemantics())
6094 std::optional<Attribute> paddingValue;
6095 if (
auto pad = adaptor.getPaddingValue())
6097 if (
OpFoldResult reshapedSource = reshapeConstantSource(
6098 llvm::dyn_cast_if_present<DenseElementsAttr>(adaptor.getSource()),
6099 cast<TensorType>(getDestType()), paddingValue)) {
6100 results.push_back(reshapedSource);
6126 if (!op.hasPureTensorSemantics())
6147 PackOp::create(rewriter, op.getLoc(), newOperands[0], newOperands[1],
6148 op.getInnerDimsPos(), newMixedTileSizes,
6149 op.getPaddingValue(), op.getOuterDimsPerm());
6150 newOp->setDiscardableAttrs(op->getDiscardableAttrDictionary());
6153 Value oldResult = op.getResult();
6154 Value newResult = newOp.getResult();
6157 ? tensor::CastOp::create(rewriter, op->getLoc(),
6158 oldResult.
getType(), newResult)
6171void UnPackOp::getAsmResultNames(
6173 if (!getResults().empty())
6174 setNameFn(getResult(),
"unpack");
6183 Type sourceType, destType, resultType;
6195 if (parser.parseInteger(value))
6197 outerDimsPermVec.push_back(value);
6207 SmallVector<int64_t> innerDimsPosVec;
6210 if (parser.parseInteger(value))
6212 innerDimsPosVec.push_back(value);
6224 for (
auto val : staticTilesAttr.
asArrayRef())
6225 staticTiles.push_back(val);
6242 bool isMemRef = llvm::isa<MemRefType>(sourceType);
6245 "pack/unpack requires '->' and destination type");
6249 resultType = destType;
6255 if (!dynamicTiles.empty() &&
6260 result.addAttribute(
"static_inner_tiles",
6262 result.addAttribute(
"inner_dims_pos", innerDimsPos);
6264 result.addAttribute(
"outer_dims_perm", outerDimsPerm);
6266 SmallVector<int32_t> segmentSizes = {
6267 1, 1, 0,
static_cast<int32_t
>(dynamicTiles.size())};
6268 result.addAttribute(
"operandSegmentSizes",
6272 result.addTypes(resultType);
6277void UnPackOp::print(OpAsmPrinter &p) {
6278 p <<
" " << getSource();
6280 if (!getOuterDimsPerm().empty()) {
6281 p <<
" outer_dims_perm = [";
6282 llvm::interleaveComma(getOuterDimsPerm(), p);
6286 p <<
" inner_dims_pos = [";
6287 llvm::interleaveComma(getInnerDimsPos(), p);
6290 p <<
" inner_tiles = ";
6293 p <<
" into " << getDest();
6296 {
"static_inner_tiles",
"inner_dims_pos",
6297 "outer_dims_perm",
"operandSegmentSizes"});
6299 p <<
" : " << getSource().getType();
6300 p <<
" -> " << getDest().getType();
6304UnPackOp::reifyResultShapes(OpBuilder &builder,
6313SmallVector<OpFoldResult> UnPackOp::getMixedTiles() {
6317SmallVector<int64_t> UnPackOp::getStaticTiles() {
6321ArrayRef<int64_t> UnPackOp::getAllOuterDims() {
6322 ShapedType destType = getDestType();
6323 int64_t destRank = destType.getRank();
6324 return getSourceType().getShape().take_front(destRank);
6327SmallVector<int64_t> UnPackOp::getTiledOuterDims() {
6328 auto innerDimsPos = getInnerDimsPos();
6329 SmallVector<int64_t> outerDims(getAllOuterDims());
6330 SmallVector<int64_t> res;
6333 SmallVector<int64_t> outerDimPermInv(getOuterDimsPerm());
6335 if (!outerDimPermInv.empty())
6339 for (
auto index : innerDimsPos)
6340 res.push_back(outerDims[index]);
6345LogicalResult UnPackOp::verify() {
6350 if (!hasPureTensorSemantics())
6359void UnPackOp::build(OpBuilder &builder, OperationState &state, Value source,
6360 Value dest, ArrayRef<int64_t> innerDimsPos,
6361 ArrayRef<OpFoldResult> innerTiles,
6362 ArrayRef<int64_t> outerDimsPerm) {
6363 assert(innerDimsPos.size() == innerTiles.size() &&
6364 "number of tile sizes specified must match the specified number of "
6365 "original dimensions to be tiled");
6366 SmallVector<int64_t> staticTileSizes;
6367 SmallVector<Value> dynamicTileSizes;
6369 build(builder, state, dest.
getType(), source, dest,
6370 outerDimsPerm.empty() ?
nullptr
6376Value UnPackOp::createDestinationTensor(OpBuilder &
b, Location loc,
6378 ArrayRef<OpFoldResult> innerTileSizes,
6379 ArrayRef<int64_t> innerDimsPos,
6380 ArrayRef<int64_t> outerDimsPerm) {
6381 AffineExpr sym0, sym1;
6383 auto dimMul = [&](OpFoldResult v1, OpFoldResult v2) -> OpFoldResult {
6387 SmallVector<OpFoldResult> mixedSizes;
6388 auto srcType = llvm::cast<RankedTensorType>(source.
getType());
6390 llvm::seq<unsigned>(0, srcType.getRank() - innerTileSizes.size())) {
6391 if (srcType.isDynamicDim(i))
6392 mixedSizes.push_back(
6393 tensor::DimOp::create(
b, loc, source, i).getResult());
6395 mixedSizes.push_back(
b.getIndexAttr(srcType.getDimSize(i)));
6397 if (!outerDimsPerm.empty()) {
6402 for (
auto [dimPos, tileSize] : llvm::zip_equal(innerDimsPos, innerTileSizes))
6403 mixedSizes[dimPos] = dimMul(mixedSizes[dimPos], tileSize);
6405 auto elemType = srcType.getElementType();
6406 return tensor::EmptyOp::create(
b, loc, mixedSizes, elemType);
6409UnPackOp UnPackOp::createTransposedClone(OpBuilder &
b, Location loc,
6410 Value transposedSource,
6411 ArrayRef<int64_t> innerPermutation,
6412 ArrayRef<int64_t> outerPermutation) {
6414 *
this, innerPermutation, outerPermutation);
6415 return UnPackOp::create(
b, loc, transposedSource, getDest(),
6416 metadata.innerDimsPos, metadata.innerTiles,
6417 metadata.outerDimsPerm);
6424 bool changeNeeded =
false;
6425 srcShape.assign(op.getSourceType().getShape().begin(),
6426 op.getSourceType().getShape().end());
6427 destShape.assign(op.getDestType().getShape().begin(),
6428 op.getDestType().getShape().end());
6429 llvm::SmallSetVector<int64_t, 4> innerDims;
6430 innerDims.insert_range(op.getInnerDimsPos());
6432 if (!op.getOuterDimsPerm().empty())
6434 int destRank = op.getDestRank();
6435 for (
auto i : llvm::seq<int64_t>(0, destRank)) {
6436 if (innerDims.contains(i))
6440 if (!inverseOuterDimsPerm.empty())
6441 srcPos = inverseOuterDimsPerm[destPos];
6442 if (ShapedType::isDynamic(srcShape[srcPos]) ==
6443 ShapedType::isDynamic(destShape[destPos])) {
6446 int64_t size = srcShape[srcPos];
6447 if (ShapedType::isDynamic(size))
6448 size = destShape[destPos];
6449 srcShape[srcPos] = size;
6450 destShape[destPos] = size;
6451 changeNeeded =
true;
6453 return changeNeeded;
6456LogicalResult UnPackOp::canonicalize(UnPackOp unPackOp,
6459 if (!unPackOp.hasPureTensorSemantics())
6463 if (PackOp packOp = unPackOp.getSource().getDefiningOp<PackOp>()) {
6464 if (packOp.getSourceType() != unPackOp.getDestType())
6466 if (packOp.getPaddingValue() ||
6470 rewriter.
replaceOp(unPackOp, packOp.getSource());
6474 if (
auto dstStyleOp =
6475 unPackOp.getDest().getDefiningOp<DestinationStyleOpInterface>()) {
6476 auto destValue = cast<OpResult>(unPackOp.getDest());
6477 Value newDest = dstStyleOp.getDpsInits()[destValue.getResultNumber()];
6479 [&]() { unPackOp.setDpsInitOperand(0, newDest); });
6483 if (unPackOp->hasOneUse()) {
6484 auto extractSliceUser =
6485 dyn_cast<tensor::ExtractSliceOp>(*unPackOp->getUsers().begin());
6486 if (extractSliceUser && unPackOp.canFoldSliceOp(extractSliceUser)) {
6487 OpBuilder::InsertionGuard g(rewriter);
6489 auto newDest = tensor::ExtractSliceOp::create(
6490 rewriter, unPackOp->getLoc(), unPackOp.getDest(),
6491 extractSliceUser.getMixedOffsets(), extractSliceUser.getMixedSizes(),
6492 extractSliceUser.getMixedStrides());
6494 unPackOp.setDpsInitOperand(0, newDest);
6495 unPackOp.getResult().setType(newDest.
getType());
6497 rewriter.
replaceOp(extractSliceUser, unPackOp);
6503 SmallVector<int64_t> srcShape, destShape;
6505 Location loc = unPackOp.getLoc();
6506 Value source = unPackOp.getSource();
6507 if (srcShape != unPackOp.getSourceType().getShape()) {
6508 auto newSrcType = unPackOp.getSourceType().clone(srcShape);
6509 source = tensor::CastOp::create(rewriter, loc, newSrcType,
6510 unPackOp.getSource());
6512 Value dest = unPackOp.getDest();
6513 if (destShape != unPackOp.getDestType().getShape()) {
6514 auto newDestType = unPackOp.getDestType().clone(destShape);
6515 dest = tensor::CastOp::create(rewriter, loc, newDestType,
6516 unPackOp.getDest());
6518 UnPackOp newOp = UnPackOp::create(
6519 rewriter, loc, source, dest, unPackOp.getInnerDimsPos(),
6520 unPackOp.getMixedTiles(), unPackOp.getOuterDimsPerm());
6522 unPackOp, unPackOp.getResult().
getType(), newOp.getResult());
6529bool UnPackOp::canFoldSliceOp(tensor::ExtractSliceOp sliceOp) {
6531 if (sliceOp.getResultType().getRank() != this->getDestType().getRank())
6536 RankedTensorType unpackedTypeAfterFold = sliceOp.getResultType();
6537 SmallVector<int64_t> outerShapeWithoutTranspose =
6539 SmallVector<bool> areOuterDimsTiled(outerShapeWithoutTranspose.size(),
false);
6540 for (
auto [pos, tileSize] :
6541 llvm::zip_equal(this->getInnerDimsPos(), this->getStaticInnerTiles())) {
6542 areOuterDimsTiled[pos] =
true;
6543 if (unpackedTypeAfterFold.isDynamicDim(pos))
6545 if (ShapedType::isDynamic(outerShapeWithoutTranspose[pos]))
6547 if (ShapedType::isDynamic(tileSize))
6549 int64_t paddingSize = outerShapeWithoutTranspose[pos] * tileSize -
6550 unpackedTypeAfterFold.getDimSize(pos);
6551 if (paddingSize >= tileSize)
6555 for (int64_t pos = 0, e = outerShapeWithoutTranspose.size(); pos < e; ++pos) {
6556 if (areOuterDimsTiled[pos])
6558 int64_t dim = outerShapeWithoutTranspose[pos];
6559 if (ShapedType::isDynamic(dim))
6561 if (dim != unpackedTypeAfterFold.getDimSize(pos))
6567bool UnPackOp::isLikeUnPad() {
6568 ShapedType packedTensorType = getSourceType();
6572::mlir::LogicalResult
6573UnPackOp::fold(FoldAdaptor adaptor,
6574 ::llvm::SmallVectorImpl<OpFoldResult> &results) {
6576 if (!hasPureTensorSemantics())
6579 if (OpFoldResult reshapedSource = reshapeConstantSource(
6580 llvm::dyn_cast_if_present<DenseElementsAttr>(adaptor.getSource()),
6581 cast<TensorType>(getResult().
getType()))) {
6582 results.push_back(reshapedSource);
6608 if (!op.hasPureTensorSemantics())
6617 Value sourceTensor = newOperands[0];
6621 rewriter, sourceTensor.
getType(), op.getMixedTiles());
6627 UnPackOp newOp = UnPackOp::create(rewriter, op.getLoc(), sourceTensor,
6628 newOperands[1], op.getInnerDimsPos(),
6629 newMixedTileSizes, op.getOuterDimsPerm());
6630 newOp->setDiscardableAttrs(op->getDiscardableAttrDictionary());
6633 Value oldResult = op.getResult();
6634 Value newResult = newOp.getResult();
6637 ? tensor::CastOp::create(rewriter, op->getLoc(),
6638 oldResult.
getType(), newResult)
6652 utils::IteratorType::reduction, utils::IteratorType::parallel,
6653 utils::IteratorType::parallel, utils::IteratorType::reduction};
6656SmallVector<AffineMap>
6657BatchReduceMatmulOp::getDefaultIndexingMaps(MLIRContext *context) {
6658 AffineExpr d0, d1, d2, d3;
6659 SmallVector<AffineMap> indexingMaps;
6661 indexingMaps.push_back(
AffineMap::get(4, 0, {d0, d1, d3}, context));
6662 indexingMaps.push_back(
AffineMap::get(4, 0, {d0, d3, d2}, context));
6664 return indexingMaps;
6667bool BatchReduceMatmulOp::isDefaultIndexingMaps(Attribute attr) {
6668 ArrayAttr maps = dyn_cast<ArrayAttr>(attr);
6671 if (maps.size() != 3)
6676 return (*positions)[0] == SmallVector<int64_t>{0, 1, 3} &&
6677 (*positions)[1] == SmallVector<int64_t>{0, 3, 2} &&
6678 (*positions)[2] == SmallVector<int64_t>{1, 2};
6680unsigned BatchReduceMatmulOp::getNumRegionArgs() {
return 3; }
6682std::string BatchReduceMatmulOp::getLibraryCallName() {
6688bool BatchReduceMatmulOp::hasUserDefinedMaps() {
6689 SmallVector<AffineMap, 3> defaultMaps =
6691 SmallVector<AffineMap, 3> explicitMaps = getIndexingMapsArray();
6692 return defaultMaps != explicitMaps;
6702bool BatchReduceMatmulOp::isValidLhsRhsBroadcastMap(AffineMap bcastMap,
6705 "Expected less than 3 result dim expr.");
6706 bool isValid =
false;
6707 enum Indices { batchPos, mPos, nPos, kPos };
6709 AffineExpr expr = bcastMap.
getResult(0);
6712 AffineExpr expr0 = bcastMap.
getResult(0);
6713 AffineExpr expr1 = bcastMap.
getResult(1);
6718 : ((expr0.isFunctionOfDim(batchPos) &&
6719 expr1.isFunctionOfDim(kPos)) ||
6720 (expr0.isFunctionOfDim(kPos) && expr1.isFunctionOfDim(nPos)));
6725void BatchReduceMatmulOp::regionBuilder(
6726 ImplicitLocOpBuilder &
b,
Block &block, ArrayRef<NamedAttribute> attrs,
6729 emitError() <<
"BatchReduceMatmulOp regionBuilder expects 3 args, got "
6734 "BatchReduceMatmulOp regionBuilder expects 3 args");
6735 RegionBuilderHelper helper(
b, block);
6736 SmallVector<Value> yields;
6740 helper.buildTypeFn(TypeFn::cast_signed, toType, block.
getArgument(0));
6742 helper.buildTypeFn(TypeFn::cast_signed, toType, block.
getArgument(1));
6744 helper.buildBinaryFn(BinaryFn::mul, castValA, castValB,
emitError);
6745 if (!castValA || !castValB || !mulVal)
6748 helper.buildBinaryFn(BinaryFn::add, block.
getArgument(2), mulVal);
6751 yields.push_back(addVal);
6752 helper.yieldOutputs(yields);
6755ParseResult BatchReduceMatmulOp::parse(OpAsmParser &parser,
6756 OperationState &
result) {
6757 SmallVector<Attribute, 3> indexingMapsAttr;
6768 if (!isa<AffineMapAttr>(mapAttr)) {
6770 "expected affine map attribute");
6772 indexingMapsAttr.push_back(mapAttr);
6782 if (indexingMapsAttr.empty()) {
6783 indexingMapsAttr = llvm::map_to_vector(
6784 BatchReduceMatmulOp::getDefaultIndexingMaps(parser.
getContext()),
6785 [](AffineMap map) -> Attribute { return AffineMapAttr::get(map); });
6787 result.addAttribute(
"indexing_maps",
6789 return ::parseNamedStructuredOp(parser,
result,
6790 BatchReduceMatmulOp::getNumRegionArgs(),
6791 BatchReduceMatmulOp::getRegionBuilder());
6794void BatchReduceMatmulOp::print(OpAsmPrinter &p) {
6795 SmallVector<Attribute, 3> indexingMaps = llvm::map_to_vector(
6796 BatchReduceMatmulOp::getDefaultIndexingMaps(
getContext()),
6797 [](AffineMap map) -> Attribute {
return AffineMapAttr::get(map); });
6799 if (!llvm::equal(getIndexingMaps(), indexingMaps)) {
6800 p <<
" indexing_maps = [";
6801 llvm::interleaveComma(getIndexingMaps(), p,
6806 SmallVector<StringRef, 3> elidedAttrs = {
6807 "operandSegmentSizes",
"linalg.memoized_indexing_maps",
"indexing_maps"};
6813LogicalResult BatchReduceMatmulOp::verify() {
6816 if (!hasUserDefinedMaps())
6819 for (
unsigned opIndex = 0; opIndex < 3; opIndex++) {
6825LogicalResult BatchReduceMatmulOp::fold(FoldAdaptor,
6826 SmallVectorImpl<OpFoldResult> &) {
6829void BatchReduceMatmulOp::getEffects(
6830 SmallVectorImpl<SideEffects::EffectInstance<MemoryEffects::Effect>>
6832 if (hasPureTensorSemantics())
6848void LinalgDialect::getCanonicalizationPatterns(
6857 return arith::ConstantOp::materialize(builder, value, type, loc);
p<< " : "<< getMemRefType()<< ", "<< getType();}static LogicalResult verifyVectorMemoryOp(Operation *op, MemRefType memrefType, VectorType vectorType) { if(memrefType.getElementType() !=vectorType.getElementType()) return op-> emitOpError("requires memref and vector types of the same elemental type")
Given a list of lists of parsed operands, populates uniqueOperands with unique operands.
static Type getElementType(Type type)
Determine the element type of type.
static LogicalResult verifyExtendedMatmulSemantic(MatmulOp matmulOp, unsigned opIndex)
Verifies the broadcast and transpose semantic sepecified by the explicit indexing map for the MatmulO...
static void fillStructuredOpRegion(OpBuilder &opBuilder, Region ®ion, TypeRange inputTypes, TypeRange outputTypes, ArrayRef< NamedAttribute > attrs, function_ref< InFlightDiagnostic()> emitError, RegionBuilderFn regionBuilder)
Fills the region of a structured operation using the provided regionBuilder.
static void buildIdentityRegion(OpBuilder &builder, Location loc, Region ®ion, ValueRange inputs, ValueRange outputs)
static void buildBatchMatmulOp(OpBuilder &b, OperationState &state, std::optional< TypeRange > resultTensorTypes, ValueRange inputs, ValueRange outputs, ArrayRef< NamedAttribute > attributes, RegionBuilderFn regionBuilder, ArrayRef< AffineMap > defaultIndexingMaps)
static Value buildDivOp(OpBuilder &builder, Location loc, Value numerator, Value denominator, Value output, int64_t dim)
Produce a linalg generic that computes the final step of the softmax decomposition.
static bool areResultExprsSubsetOf(AffineMap subMap, AffineMap fullMap)
static LogicalResult appendMangledType(llvm::raw_string_ostream &ss, Type t)
static bool canUseShortForm(Block *body, bool initFirst=false, bool mapInit=true)
static bool isBroadcasted(AffineMap explictMap, AffineMap defaultMap)
Check if the user defined map is valid broadcast map.
static void printCommonStructuredOpParts(OpAsmPrinter &p, ValueRange inputs, ValueRange outputs)
llvm::function_ref< void( ImplicitLocOpBuilder &, Block &, ArrayRef< NamedAttribute >, function_ref< InFlightDiagnostic()>)> RegionBuilderFn
static ParseResult parseDenseI64ArrayAttr(OpAsmParser &parser, NamedAttrList &attributes, StringRef attributeName)
static void printDenseI64ArrayAttr(OpAsmPrinter &p, StringRef attributeName, ArrayRef< int64_t > attributeValue)
static Value buildSubAndExpOp(OpBuilder &builder, Location loc, Value input, Value max, Value output, int64_t dim)
Produce a linalg generic that computes the second step of the softmax decomposition: res = exp(input ...
static void printShortForm(OpAsmPrinter &p, Operation *payloadOp)
static LogicalResult verifyOutputMap(OpTy batchVariantMatmulOp, AffineMap opIndexingMap)
This function checks if the given AffineMap for the output of a BatchMatmulOp/BatchReduceMatmulOp has...
static std::optional< TypedAttr > getScalarConstantAttrFromDenseSplat(Value input)
static void buildStructuredOp(OpBuilder &b, OperationState &state, std::optional< TypeRange > resultTensorTypes, ValueRange inputs, ValueRange outputs, ArrayRef< NamedAttribute > attributes, RegionBuilderFn regionBuilder)
Creates a structured operation given inputs, outputs, and attributes.
static ParseResult parseDstStyleOp(OpAsmParser &parser, OperationState &result, function_ref< ParseResult(OpAsmParser &, NamedAttrList &)> parseAttrsFn=nullptr)
static LogicalResult verifyInputMaps(OpTy batchVariantMatmulOp, AffineMap opIndexingMap, AffineMap defaultIndexingMap, bool isLHS)
static Value reduce(OpBuilder &builder, Location loc, Value input, Value output, int64_t dim)
static Speculation::Speculatability getGenericSpeculatabilityImpl(LinalgOp linalgOp)
static LogicalResult verifyYield(linalg::YieldOp op, LinalgOp linalgOp)
static ParseResult parseNamedStructuredOp(OpAsmParser &parser, OperationState &result, unsigned numRegionArgs, RegionBuilderFn regionBuilder)
static void getGenericEffectsImpl(SmallVectorImpl< SideEffects::EffectInstance< MemoryEffects::Effect > > &effects, LinalgOp linalgOp)
static void buildGenericRegion(OpBuilder &builder, Location loc, Region ®ion, ValueRange inputs, ValueRange outputs, function_ref< void(OpBuilder &, Location, ValueRange)> bodyBuild)
static ParseResult parseNamedStructuredOpResults(OpAsmParser &parser, SmallVectorImpl< Type > &resultTypes)
static OpFoldResult getDimValue(OpBuilder &builder, Location loc, Value v, int64_t dim)
Return a memref.dim or tensor.dim for the shape of v at dim.
static void addBodyWithPayloadOp(OpAsmParser &parser, OperationState &result, const OperationName &payloadOpName, const NamedAttrList &payloadOpAttrs, ArrayRef< Value > operands, bool initFirst=false, bool mapInit=true)
static std::tuple< SmallVector< utils::IteratorType >, SmallVector< AffineMap > > computeIteratorTypesAndIndexingMaps(OpBuilder &builder, int64_t inputRank, int64_t dim, bool allParallel=false)
static void buildBatchReduceMatmulOp(OpBuilder &b, OperationState &state, std::optional< TypeRange > resultTensorTypes, ValueRange inputs, ValueRange outputs, ArrayRef< NamedAttribute > attributes, RegionBuilderFn regionBuilder, ArrayRef< AffineMap > indexingMaps)
static void printNamedStructuredOpResults(OpAsmPrinter &p, TypeRange resultTypes)
static void buildMatmulOp(OpBuilder &b, OperationState &state, std::optional< TypeRange > resultTensorTypes, ValueRange inputs, ValueRange outputs, ArrayRef< NamedAttribute > attributes, RegionBuilderFn regionBuilder, ArrayRef< AffineMap > defaultIndexingMaps)
static LogicalResult verifyExtendedBatchVariantMatmulSemantic(OpTy batchVariantMatmulOp, unsigned opIndex)
Verifies the broadcast and transpose semantic specified by the explicit indexing map for the BatchMat...
static void printNamedStructuredOp(OpAsmPrinter &p, Operation *op, ValueRange inputs, ValueRange outputs, ArrayRef< StringRef > elidedAttrs={})
static ParseResult parseCommonStructuredOpParts(OpAsmParser &parser, OperationState &result, SmallVectorImpl< Type > &inputTypes, SmallVectorImpl< Type > &outputTypes, bool addOperandSegmentSizes=true)
Common parsing used for both named structured ops created by ods-gen and by manually defined C++ ops.
static ParseResult parseNamedStructuredOpRegion(OpAsmParser &parser, Region ®ion, unsigned numRegionArgs, TypeRange inputTypes, TypeRange outputTypes, ArrayRef< NamedAttribute > attrs, RegionBuilderFn regionBuilder, SMLoc loc)
*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 the output argument nBegin is set to its * replacement(set to `begin` if no invalidation happens). Since outgoing *copies could have been inserted at `end`
static Value max(ImplicitLocOpBuilder &builder, Value value, Value bound)
static LogicalResult getResultTilePosition(RewriterBase &rewriter, ReductionTilingStrategy reductionStrategy, int64_t index, Value tiledResult, TilingInterface op, ArrayRef< OpFoldResult > offsets, ArrayRef< OpFoldResult > sizes, ValueRange ivs, ArrayRef< OpFoldResult > numThreads, ArrayRef< OpFoldResult > givenTileSizes, const SetVector< unsigned > &reductionDims, SmallVector< OpFoldResult > &resultOffset, SmallVector< OpFoldResult > &resultSize)
static FailureOr< TilingResult > getTiledImplementation(RewriterBase &rewriter, TilingInterface op, ReductionTilingStrategy reductionStrategy, ValueRange regionIterArg, ArrayRef< OpFoldResult > offsets, ArrayRef< OpFoldResult > sizes, ValueRange ivs, ArrayRef< OpFoldResult > numThreads, ArrayRef< OpFoldResult > givenTileSizes, ArrayRef< InnerTileAlignment > innerTileAlignments, const SetVector< unsigned > &reductionDims)
static ArrayRef< int64_t > getShape(Type type)
Returns the shape of the given type.
Base type for affine expression.
bool isFunctionOfDim(unsigned position) const
Return true if the affine expression involves AffineDimExpr position.
AffineExpr ceilDiv(uint64_t v) const
A multi-dimensional affine map Affine map's are immutable like Type's, and they are uniqued.
AffineMap dropResults(ArrayRef< int64_t > positions) const
static AffineMap getMultiDimIdentityMap(unsigned numDims, MLIRContext *context)
Returns an AffineMap with 'numDims' identity result dim exprs.
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 getNumDims() const
ArrayRef< AffineExpr > getResults() const
unsigned getNumResults() const
AffineExpr getResult(unsigned idx) const
static AffineMap getPermutationMap(ArrayRef< unsigned > permutation, MLIRContext *context)
Returns an AffineMap representing a permutation.
@ Paren
Parens surrounding zero or more operands.
@ 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 parseCommaSeparatedList(Delimiter delimiter, function_ref< ParseResult()> parseElementFn, StringRef contextMessage=StringRef())=0
Parse a list of comma-separated items with an optional delimiter.
virtual ParseResult parseOptionalAttrDict(NamedAttrList &result)=0
Parse a named dictionary into 'result' if it is present.
virtual ParseResult parseOptionalKeyword(StringRef keyword)=0
Parse the given keyword if present.
MLIRContext * getContext() const
virtual ParseResult parseRParen()=0
Parse a ) token.
virtual InFlightDiagnostic emitError(SMLoc loc, const Twine &message={})=0
Emit a diagnostic at the specified location and return failure.
virtual ParseResult parseLSquare()=0
Parse a [ token.
virtual ParseResult parseRSquare()=0
Parse a ] token.
virtual ParseResult parseOptionalArrow()=0
Parse a '->' token if present.
virtual ParseResult parseRBrace()=0
Parse a } token.
virtual ParseResult parseEqual()=0
Parse a = token.
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 ParseResult parseColon()=0
Parse a : token.
virtual ParseResult parseOptionalLess()=0
Parse a '<' token if present.
virtual ParseResult parseGreater()=0
Parse a '>' token.
virtual ParseResult parseLParen()=0
Parse a ( token.
virtual ParseResult parseType(Type &result)=0
Parse a type.
virtual ParseResult parseOptionalArrowTypeList(SmallVectorImpl< Type > &result)=0
Parse an optional arrow followed by a type list.
ParseResult parseTypeList(SmallVectorImpl< Type > &result)
Parse a type list.
ParseResult parseKeyword(StringRef keyword)
Parse a given keyword.
virtual ParseResult parseAttribute(Attribute &result, Type type={})=0
Parse an arbitrary attribute of a given type and return it in result.
virtual ParseResult parseOptionalLBrace()=0
Parse a { token if present.
virtual void decreaseIndent()
Decrease indentation.
virtual void increaseIndent()
Increase indentation.
void printOptionalArrowTypeList(TypeRange &&types)
Print an optional arrow followed by a type list.
virtual void printAttribute(Attribute attr)
virtual void printNewline()
Print a newline and indent the printer to the start of the current operation/attribute/type.
Attributes are known-constant values of operations.
Block represents an ordered list of Operations.
BlockArgument getArgument(unsigned i)
unsigned getNumArguments()
OpListType & getOperations()
Operation * getTerminator()
Get the terminator operation of this block.
BlockArgument addArgument(Type type, Location loc)
Add one value to the argument list.
BlockArgListType getArguments()
Operation * getParentOp()
Returns the closest surrounding operation that contains this block.
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)
DenseI64ArrayAttr getDenseI64ArrayAttr(ArrayRef< int64_t > values)
AffineMap getMultiDimIdentityMap(unsigned rank)
IntegerAttr getI64IntegerAttr(int64_t value)
StringAttr getStringAttr(const Twine &bytes)
AffineExpr getAffineDimExpr(unsigned position)
ArrayAttr getArrayAttr(ArrayRef< Attribute > value)
MLIRContext * getContext() const
ArrayAttr getAffineMapArrayAttr(ArrayRef< AffineMap > values)
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.
IRValueT get() const
Return the current value being used by this operand.
ImplicitLocOpBuilder maintains a 'current location', allowing use of the create<> method without spec...
This class represents a diagnostic that is inflight and set to be reported.
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...
ArrayRef< NamedAttribute > getAttrs() const
Return all of the attributes on this operation.
DictionaryAttr getDictionary(MLIRContext *context) const
Return a dictionary attribute for the underlying dictionary.
void append(StringRef name, Attribute attr)
Add an attribute with the specified name.
Attribute set(StringAttr name, Attribute value)
If the an attribute exists with the specified name, change it to the new value.
NamedAttribute represents a combination of a name and an Attribute value.
StringAttr getName() const
Return the name of the attribute.
Attribute getValue() const
Return the value of the attribute.
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.
virtual ParseResult parseArgumentList(SmallVectorImpl< Argument > &result, Delimiter delimiter=Delimiter::None, bool allowType=false, bool allowAttrs=false)=0
Parse zero or more arguments with a specified surrounding delimiter.
virtual ParseResult resolveOperand(const UnresolvedOperand &operand, Type type, SmallVectorImpl< Value > &result)=0
Resolve an operand to an SSA value, emitting an error on failure.
virtual FailureOr< OperationName > parseCustomOperationName()=0
Parse the name of an operation, in the custom form.
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 printRegion(Region &blocks, bool printEntryBlockArgs=true, bool printBlockTerminators=true, bool printEmptyBlock=false)=0
Prints a region.
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.
void setInsertionPointToStart(Block *block)
Sets the insertion point to the start of the specified block.
void setInsertionPoint(Block *block, Block::iterator insertPoint)
Set the insertion point to the specified location.
Operation * create(const OperationState &state)
Creates an operation given the fields represented as an OperationState.
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 represents an operand of an operation.
unsigned getOperandNumber() const
Return which operand this is in the OpOperand list of the Operation.
unsigned getResultNumber() const
Returns the number of this result.
StringRef getStringRef() const
Return the name of this operation. This always succeeds.
Operation is the basic unit of execution within MLIR.
Attribute getAttr(StringAttr name)
Return the specified attribute if present, null otherwise.
result_iterator result_begin()
ArrayRef< NamedAttribute > getAttrs()
Return all of the attributes on this operation.
OpResult getResult(unsigned idx)
Get the 'idx'th result of this operation.
Location getLoc()
The source location the operation was defined or derived from.
unsigned getNumOperands()
InFlightDiagnostic emitError(const Twine &message={})
Emit an error about fatal conditions with this operation, reporting up to any diagnostic handlers tha...
OperationName getName()
The name of an operation is the key identifier for it.
operand_type_range getOperandTypes()
result_iterator result_end()
result_type_range getResultTypes()
operand_range getOperands()
Returns an iterator on the underlying Value's.
result_range getResults()
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.
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 finalizeOpModification(Operation *op)
This method is used to signal the end of an in-place modification of the given operation.
virtual void eraseOp(Operation *op)
This method erases an operation that is known to have no uses.
void replaceAllUsesExcept(Value from, Value to, Operation *exceptedUser)
Find uses of from and replace them with to except if the user is exceptedUser.
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.
virtual void startOpModification(Operation *op)
This method is used to notify the rewriter that an in-place operation modification is about to happen...
OpTy replaceOpWithNewOp(Operation *op, Args &&...args)
Replace the results of the given (original) op with a new op that is created without verification (re...
This class represents a specific instance of an effect.
static DerivedEffect * get()
static DefaultResource * get()
This class provides an abstraction over the various different ranges of value types.
Instances of the Type class are uniqued, have an immutable identifier and an optional mutable compone...
unsigned getIntOrFloatBitWidth() const
Return the bit width of an integer or a float type, assert failure on other types.
bool isSignlessIntOrIndexOrFloat() const
Return true if this is a signless integer, index, or float type.
This class provides an abstraction over the different types of ranges over Values.
type_range getTypes() const
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.
Block * getParentBlock()
Return the Block in which this Value is defined.
bool hasOneUse() const
Returns true if this value has exactly one use.
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.
static ConstantIndexOp create(OpBuilder &builder, Location location, int64_t value)
ArrayRef< T > asArrayRef() const
static Attribute parse(AsmParser &parser, Type type)
Specialization of linalg.batch_matmul op that has a transpose map on A.
static bool isDefaultIndexingMaps(Attribute attr)
Checks if the affine map is the expected one for this operation.
static bool classof(Operation *op)
static void build(OpBuilder &builder, OperationState &result, ValueRange inputs, ValueRange outputs, ArrayRef< NamedAttribute > attributes={})
Build a transpose A matmul.
static BatchMatmulTransposeAOp create(OpBuilder &builder, Location location, ValueRange inputs, ValueRange outputs, ArrayRef< NamedAttribute > attributes={})
Specialization of linalg.batch_matmul op that has a transpose map on B.
static void build(OpBuilder &builder, OperationState &result, ValueRange inputs, ValueRange outputs, ArrayRef< NamedAttribute > attributes={})
Build a transpose B matmul.
static bool classof(Operation *op)
static BatchMatmulTransposeBOp create(OpBuilder &builder, Location location, ValueRange inputs, ValueRange outputs, ArrayRef< NamedAttribute > attributes={})
static bool isDefaultIndexingMaps(Attribute attr)
Checks if the affine map is the expected one for this operation.
Specialization of linalg.matmul op that has a transpose map on A.
static bool isDefaultIndexingMaps(Attribute attr)
Checks if the affine map is the expected one for this operation.
static MatmulTransposeAOp create(OpBuilder &builder, Location location, ValueRange inputs, ValueRange outputs, ArrayRef< NamedAttribute > attributes={})
static void build(OpBuilder &builder, OperationState &result, ValueRange inputs, ValueRange outputs, ArrayRef< NamedAttribute > attributes={})
Build a transpose A matmul.
static bool classof(Operation *op)
Specialization of linalg.matmul op that has a transpose map on B.
static void build(OpBuilder &builder, OperationState &result, ValueRange inputs, ValueRange outputs, ArrayRef< NamedAttribute > attributes={})
Build a transpose B matmul.
static MatmulTransposeBOp create(OpBuilder &builder, Location location, ValueRange inputs, ValueRange outputs, ArrayRef< NamedAttribute > attributes={})
static bool isDefaultIndexingMaps(Attribute attr)
Checks if the affine map is the expected one for this operation.
static bool classof(Operation *op)
constexpr auto RecursivelySpeculatable
Speculatability
This enum is returned from the getSpeculatability method in the ConditionallySpeculatable op interfac...
constexpr auto Speculatable
constexpr auto NotSpeculatable
AffineApplyOp makeComposedAffineApply(OpBuilder &b, Location loc, AffineMap map, ArrayRef< OpFoldResult > operands, bool composeAffineMin=false)
Returns a composed AffineApplyOp by composing map and operands with other AffineApplyOps supplying th...
OpFoldResult makeComposedFoldedAffineApply(OpBuilder &b, Location loc, AffineMap map, ArrayRef< OpFoldResult > operands, bool composeAffineMin=false)
Constructs an AffineApplyOp that applies map to operands after composing the map with the maps of any...
Value getIdentityValue(AtomicRMWKind op, Type resultType, OpBuilder &builder, Location loc, bool useOnlyFiniteValue=false)
Returns the identity value associated with an AtomicRMWKind op.
static SmallVector< int64_t > asShapeWithAnyValueAsDynamic(ArrayRef< OpFoldResult > ofrs)
Converts OpFoldResults to int64_t shape entries, unconditionally mapping all Value's to kDynamic,...
static LogicalResult reifyResultShapesImpl(OpTy op, OpBuilder &builder, ReifiedRankedShapedTypeDims &reifiedReturnShapes)
static bool inferStaticShape(PackOp packOp, SmallVectorImpl< int64_t > &srcShape, SmallVectorImpl< int64_t > &destShape)
Returns true if the srcShape or destShape is different from the one in packOp and populates each with...
static SmallVector< int64_t > getStaticTilesImpl(OpTy op)
static void getPackUnPackEffectsImpl(OpTy op, SmallVectorImpl< SideEffects::EffectInstance< MemoryEffects::Effect > > &effects)
static bool isInvalidPackingPosSpecification(ArrayRef< int64_t > dimsPos, size_t rank)
Returns true if dimsPos is invalid.
static SmallVector< OpFoldResult > getMixedTilesImpl(OpTy op)
static DenseMap< int64_t, OpFoldResult > getDimAndTileMappingImpl(OpTy op)
SmallVector< AffineExpr, 4 > concat(ArrayRef< AffineExpr > a, ArrayRef< AffineExpr > b)
Return the vector that is the concatenation of a and b.
static ArityGroupAndKind getArityGroupAndKind(ElementwiseKind kind)
static PackOrUnPackTransposeResult commonPermutationOfPackAndUnPackOp(OpTy packOrUnPackOp, ArrayRef< int64_t > innerPermutation, ArrayRef< int64_t > outerPermutation)
OpFoldResult createFoldedDimOp(OpBuilder &b, Location loc, Value val, int64_t dim)
Create one memref::DimOp or tensor::DimOp depending on the type of val.
static SmallVector< OpFoldResult > getNewMixedTileSizes(PatternRewriter &rewriter, Type newPackedTy, ArrayRef< OpFoldResult > mixedTiles)
static bool areTilesAndTiledDimsAllConstant(OpTy op)
Returns true if the tiles and the tiled dims are constant.
std::string generateLibraryCallName(Operation *op)
Returns the name mangled library call name to disambiguate between different overloads at the C level...
template SmallVector< int64_t > getPackedOuterShapeWithoutTransposition< UnPackOp >(UnPackOp)
static bool paddingIsNotNeeded(PackOp op)
Returns true if the pack op does not need a padding value.
static bool isLikePadUnPad(PackOrUnpackOp packOp, ShapedType packedTensorType)
AffineMap extractOrIdentityMap(std::optional< AffineMap > maybeMap, unsigned rank, MLIRContext *context)
Returns maybeMap.get() if maybeMap is set, otherwise returns the symbol-less identity map of rank.
SmallVector< AffineExpr, 4 > makeAffineDimExprs(unsigned num, unsigned &startIdx, MLIRContext *context)
Returns num AffineDimExpr dimensions at positions [startIdx, startIdx + num) and increments startIdx ...
static FailureOr< SmallVector< SmallVector< int64_t > > > getAffineResultPositions(ArrayAttr maps)
static bool haveSameTiles(PackOp packOp, UnPackOp unPackOp)
Value createOrFoldDimOp(OpBuilder &b, Location loc, Value val, int64_t dim)
Create one memref::DimOp or tensor::DimOp depending on the type of val.
static bool hasSameInnerOuterAttribute(PackOp packOp, UnPackOp unPackOp)
template SmallVector< int64_t > getPackedOuterShapeWithoutTransposition< PackOp >(PackOp)
std::pair< int64_t, int64_t > getFmrFromWinogradConv2DFmr(WinogradConv2DFmr fmr)
Converts the given WinogradConv2DFmr enumeration value to a pair of m and r parameters.
std::optional< WinogradConv2DFmr > getWinogradConv2DFmr(int64_t m, int64_t r)
Converts the given m and r parameters to a WinogradConv2DFmr enumeration value.
static LogicalResult commonVerifierPackAndUnPackOp(OpTy packOrUnPack)
static FailureOr< ArrayAttr > parseIndexingMapsAttr(OpAsmParser &parser)
SmallVector< int64_t > getPackedOuterShapeWithoutTransposition(OpTy packOrUnPack)
Returns the outer shape in the packed domain before applying the transposition.
LogicalResult foldMemRefCast(Operation *op, Value inner=nullptr)
This is a common utility used for patterns of the form "someop(memref.cast) -> someop".
SparseTensorEncodingAttr getSparseTensorEncoding(Type type)
Convenience method to get a sparse encoding attribute from a type.
bool hasFoldableTensorCastOperand(Operation *op)
Return true if any of the operands of op is a CastOp that can be folded into its consumer,...
bool canFoldIntoProducerOp(CastOp castOp)
Determines whether the tensor::CastOp casts to a more static version of the source tensor.
SmallVector< Value > getUpdatedOperandsAfterCastOpFolding(DestinationStyleOpInterface op, SmallVector< Type > &newResTy)
Assuming that op contains at least one operand that is a foldable CastOp (i.e.
SmallVector< OpFoldResult > getMixedSizes(OpBuilder &builder, Location loc, Value value)
Return the dimensions of the given tensor value.
Include the generated interface declarations.
bool matchPattern(Value value, const Pattern &pattern)
Entry point for matching a pattern over a Value.
Value convertScalarToDtype(OpBuilder &b, Location loc, Value operand, Type toType, bool isUnsignedCast)
Converts a scalar value operand to type toType.
detail::DenseArrayAttrImpl< int64_t > DenseI64ArrayAttr
function_ref< void(Value, StringRef)> OpAsmSetValueNameFn
A functor used to set the name of the start of a result group of an operation.
std::optional< int64_t > getConstantIntValue(OpFoldResult ofr)
If ofr is a constant integer or an IntegerAttr, return the integer.
LogicalResult reifyResultShapes(OpBuilder &b, Operation *op, ReifiedRankedShapedTypeDims &reifiedReturnShapes)
Reify the shape of the result of an operation (typically in terms of the shape of its operands).
ParseResult parseDynamicIndexList(OpAsmParser &parser, SmallVectorImpl< OpAsmParser::UnresolvedOperand > &values, DenseI64ArrayAttr &integers, DenseBoolArrayAttr &scalableFlags, SmallVectorImpl< Type > *valueTypes=nullptr, AsmParser::Delimiter delimiter=AsmParser::Delimiter::Square)
Parser hooks for custom directive in assemblyFormat.
bool areAllConstantIntValue(ArrayRef< OpFoldResult > ofrs, int64_t value)
Return true if all of ofrs are constant integers equal to value.
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.
void bindDims(MLIRContext *ctx, AffineExprTy &...exprs)
Bind a list of AffineExpr references to DimExpr at positions: [0 .
SmallVector< T > applyPermutation(ArrayRef< T > input, ArrayRef< int64_t > permutation)
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...
Attribute parseAttribute(llvm::StringRef attrStr, MLIRContext *context, Type type={}, size_t *numRead=nullptr, bool isKnownNullTerminated=false)
This parses a single MLIR attribute to an MLIR context if it was valid.
SmallVector< SmallVector< OpFoldResult > > ReifiedRankedShapedTypeDims
bool isIdentityPermutation(ArrayRef< int64_t > permutation)
Returns true if permutation is an identity permutation.
Type getElementTypeOrSelf(Type type)
Return the element type or return the type itself.
bool isZeroInteger(OpFoldResult v)
Return "true" if v is an integer value/attribute with constant value 0.
void bindSymbols(MLIRContext *ctx, AffineExprTy &...exprs)
Bind a list of AffineExpr references to SymbolExpr at positions: [0 .
void dispatchIndexOpFoldResults(ArrayRef< OpFoldResult > ofrs, SmallVectorImpl< Value > &dynamicVec, SmallVectorImpl< int64_t > &staticVec)
Helper function to dispatch multiple OpFoldResults according to the behavior of dispatchIndexOpFoldRe...
llvm::TypeSwitch< T, ResultT > TypeSwitch
Value getValueOrCreateConstantIndexOp(OpBuilder &b, Location loc, OpFoldResult ofr)
Converts an OpFoldResult to a Value.
LogicalResult verifyRanksMatch(Operation *op, ShapedType lhs, ShapedType rhs, StringRef lhsName, StringRef rhsName)
Verify that two shaped types have matching ranks.
Operation * clone(OpBuilder &b, Operation *op, TypeRange newResultTypes, ValueRange newOperands)
SmallVector< Loops, 8 > tile(ArrayRef< scf::ForOp > forOps, ArrayRef< Value > sizes, ArrayRef< scf::ForOp > targets)
Performs tiling fo imperfectly nested loops (with interchange) by strip-mining the forOps by sizes an...
auto get(MLIRContext *context, Ts &&...params)
Helper method that injects context only if needed, this helps unify some of the attribute constructio...
llvm::DenseMap< KeyT, ValueT, KeyInfoT, BucketT > DenseMap
OpFoldResult getAsOpFoldResult(Value val)
Given a value, try to extract a constant Attribute.
LogicalResult verifyCompatibleShape(ArrayRef< int64_t > shape1, ArrayRef< int64_t > shape2)
Returns success if the given two shapes are compatible.
SetVector< Operation * > getSlice(Operation *op, const BackwardSliceOptions &backwardSliceOptions={}, const ForwardSliceOptions &forwardSliceOptions={})
Iteratively computes backward slices and forward slices until a fixed point is reached.
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.
SmallVector< int64_t > dropDims(ArrayRef< int64_t > inputPerm, ArrayRef< int64_t > dropPositions)
Returns a permutation vector that drop the input dims in dropPositions from inputPerm.
llvm::function_ref< Fn > function_ref
bool isPermutationVector(ArrayRef< int64_t > interchange)
Method to check if an interchange vector is a permutation.
void printDynamicIndexList(OpAsmPrinter &printer, Operation *op, OperandRange values, ArrayRef< int64_t > integers, ArrayRef< bool > scalableFlags, TypeRange valueTypes=TypeRange(), AsmParser::Delimiter delimiter=AsmParser::Delimiter::Square)
Printer hooks for custom directive in assemblyFormat.
SmallVector< int64_t > invertPermutationVector(ArrayRef< int64_t > permutation)
Helper method to apply to inverse a permutation.
Rewrite a broadcast of a dense splat constant into a dense splat constant of the broadcast output sha...
LogicalResult matchAndRewrite(linalg::BroadcastOp broadcastOp, PatternRewriter &rewriter) const override
Fold back-to-back broadcasts together.
LogicalResult matchAndRewrite(linalg::BroadcastOp broadcastOp, PatternRewriter &rewriter) const override
Rewrite a transpose of a dense splat constant into a dense splat constant of the transposed output sh...
LogicalResult matchAndRewrite(linalg::TransposeOp transposeOp, PatternRewriter &rewriter) const override
Fold transpose with transpose.
LogicalResult matchAndRewrite(linalg::TransposeOp transposeOp, PatternRewriter &rewriter) const override
This pattern canonicalize transpose by swapping the order of broadcast and transpose: transpose(broad...
LogicalResult matchAndRewrite(linalg::TransposeOp transposeOp, PatternRewriter &rewriter) const override
This is the representation of an operand reference.
OpInterfaceRewritePattern is a wrapper around RewritePattern that allows for matching and rewriting a...
OpRewritePattern is a wrapper around RewritePattern that allows for matching and rewriting against an...
OpRewritePattern(MLIRContext *context, PatternBenefit benefit=1, ArrayRef< StringRef > generatedNames={})
Patterns must specify the root operation name they match against, and can also specify the benefit of...
This represents an operation in an abstracted form, suitable for use with the builder APIs.
void addOperands(ValueRange newOperands)
void addAttributes(ArrayRef< NamedAttribute > newAttributes)
Add an array of named attributes.
void addAttribute(StringRef name, Attribute attr)
Add an attribute with the specified name.
void addTypes(ArrayRef< Type > newTypes)
Region * addRegion()
Create a region that should be attached to the operation.
Folds a tensor.cast op into a consuming PackOp op if the tensor.cast has source that is more static t...
LogicalResult matchAndRewrite(PackOp op, PatternRewriter &rewriter) const override
Folds a tensor.cast op into a consuming UnPackOp op if the tensor.cast has source that is more static...
LogicalResult matchAndRewrite(UnPackOp op, PatternRewriter &rewriter) const override