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())
116 if (llvm::isa<UnrankedMemRefType, MemRefType>(source.
getType()))
117 return b.createOrFold<memref::DimOp>(loc, source, dim);
118 if (llvm::isa<UnrankedTensorType, RankedTensorType>(source.
getType()))
119 return b.createOrFold<tensor::DimOp>(loc, source, dim);
120 llvm_unreachable(
"Expected MemRefType or TensorType");
125 auto shapedType = llvm::cast<ShapedType>(source.
getType());
126 if (!shapedType.hasRank() || shapedType.isDynamicDim(dim))
128 return b.getIndexAttr(shapedType.getDimSize(dim));
151 for (
auto containers : {inputTypes, outputTypes}) {
152 for (
auto t : containers) {
164 opBuilder.
createBlock(®ion, {}, argTypes, argLocs);
180 std::optional<TypeRange> resultTensorTypes,
187 if (!resultTensorTypes)
188 copy_if(outputs.
getTypes(), std::back_inserter(derivedResultTypes),
189 llvm::IsaPred<RankedTensorType>);
197 "operandSegmentSizes",
198 b.getDenseI32ArrayAttr({static_cast<int32_t>(inputs.size()),
199 static_cast<int32_t>(outputs.size())}));
209 std::optional<TypeRange> resultTensorTypes,
216 return attr.
getName() ==
"indexing_maps";
219 indexingMapsAttrVal = llvm::map_to_vector(
222 state.
addAttribute(
"indexing_maps",
b.getArrayAttr(indexingMapsAttrVal));
225 attributes, regionBuilder);
229 std::optional<TypeRange> resultTensorTypes,
236 return attr.
getName() ==
"indexing_maps";
239 indexingMapsAttrVal = llvm::map_to_vector(
242 state.
addAttribute(
"indexing_maps",
b.getArrayAttr(indexingMapsAttrVal));
245 attributes, regionBuilder);
249 std::optional<TypeRange> resultTensorTypes,
256 indexingMapsAttrVal =
258 return AffineMapAttr::get(map);
260 state.
addAttribute(
"indexing_maps",
b.getArrayAttr(indexingMapsAttrVal));
262 attributes, regionBuilder);
271 bool addOperandSegmentSizes =
true) {
272 SMLoc attrsLoc, inputsOperandsLoc, outputsOperandsLoc;
301 if (parser.
resolveOperands(inputsOperands, inputTypes, inputsOperandsLoc,
303 parser.
resolveOperands(outputsOperands, outputTypes, outputsOperandsLoc,
307 if (addOperandSegmentSizes) {
314 if (
result.propertiesAttr) {
316 attrs.
append(
"operandSegmentSizes",
318 {static_cast<int32_t>(inputsOperands.size()),
319 static_cast<int32_t>(outputsOperands.size())}));
322 result.addAttribute(
"operandSegmentSizes",
324 {static_cast<int32_t>(inputsOperands.size()),
325 static_cast<int32_t>(outputsOperands.size())}));
328 if (!
result.propertiesAttr) {
329 std::optional<RegisteredOperationName> info =
330 result.name.getRegisteredInfo();
332 if (failed(info->verifyInherentAttrs(
result.attributes, [&]() {
333 return parser.emitError(attrsLoc)
334 <<
"'" << result.name.getStringRef() <<
"' op ";
345 p <<
" ins(" << inputs <<
" : " << inputs.
getTypes() <<
")";
346 if (!outputs.empty())
347 p <<
" outs(" << outputs <<
" : " << outputs.
getTypes() <<
")";
358 if (numRegionArgs != inputTypes.size() + outputTypes.size()) {
361 llvm::formatv(
"[parseNamedStructuredOpRegion] ods-gen generated "
362 "region expects {0} args, got {1}",
363 numRegionArgs, inputTypes.size() + outputTypes.size()));
369 opBuilder, region, inputTypes, outputTypes, attrs,
388 unsigned numRegionArgs,
405 result.addTypes(outputTensorsTypes);
407 std::unique_ptr<Region> region = std::make_unique<Region>();
409 outputTypes,
result.attributes.getAttrs(),
412 result.addRegion(std::move(region));
419 if (resultTypes.empty())
429 op, [&](StringRef name,
Attribute &attr) { attrs.append(name, attr); });
467class RegionBuilderHelper {
469 RegionBuilderHelper(OpBuilder &builder,
Block &block)
470 : builder(builder), block(block) {}
473 Value buildUnaryFn(UnaryFn unaryFn, Value arg,
475 if (!isFloatingPoint(arg)) {
477 emitError() <<
"unsupported non numeric type";
480 llvm_unreachable(
"unsupported non numeric type");
482 OpBuilder::InsertionGuard g(builder);
483 builder.setInsertionPointToEnd(&block);
486 return math::ExpOp::create(builder, arg.
getLoc(), arg);
488 return math::LogOp::create(builder, arg.
getLoc(), arg);
490 return math::AbsFOp::create(builder, arg.
getLoc(), arg);
492 return math::CeilOp::create(builder, arg.
getLoc(), arg);
494 return math::FloorOp::create(builder, arg.
getLoc(), arg);
496 return arith::NegFOp::create(builder, arg.
getLoc(), arg);
497 case UnaryFn::reciprocal: {
498 Attribute oneAttr = builder.getOneAttr(arg.
getType());
499 auto one = arith::ConstantOp::create(builder, arg.
getLoc(),
500 ::cast<TypedAttr>(oneAttr));
501 return arith::DivFOp::create(builder, arg.
getLoc(), one, arg);
504 return math::RoundOp::create(builder, arg.
getLoc(), arg);
506 return math::SqrtOp::create(builder, arg.
getLoc(), arg);
508 return math::RsqrtOp::create(builder, arg.
getLoc(), arg);
509 case UnaryFn::square:
510 return arith::MulFOp::create(builder, arg.
getLoc(), arg, arg);
512 return math::TanhOp::create(builder, arg.
getLoc(), arg);
514 return math::ErfOp::create(builder, arg.
getLoc(), arg);
516 return math::SinOp::create(builder, arg.
getLoc(), arg);
518 return math::CosOp::create(builder, arg.
getLoc(), arg);
520 return math::TanOp::create(builder, arg.
getLoc(), arg);
522 return math::AcosOp::create(builder, arg.
getLoc(), arg);
524 return math::AcoshOp::create(builder, arg.
getLoc(), arg);
526 return math::AsinOp::create(builder, arg.
getLoc(), arg);
528 return math::AsinhOp::create(builder, arg.
getLoc(), arg);
530 return math::AtanOp::create(builder, arg.
getLoc(), arg);
532 return math::AtanhOp::create(builder, arg.
getLoc(), arg);
534 return math::Log10Op::create(builder, arg.
getLoc(), arg);
536 return math::Log1pOp::create(builder, arg.
getLoc(), arg);
538 return math::Log2Op::create(builder, arg.
getLoc(), arg);
541 emitError() <<
"unsupported unary function";
544 llvm_unreachable(
"unsupported unary function");
551 Value buildBinaryFn(BinaryFn binaryFn, Value arg0, Value arg1,
553 bool allComplex = isComplex(arg0) && isComplex(arg1);
554 bool allFloatingPoint = isFloatingPoint(arg0) && isFloatingPoint(arg1);
555 bool allInteger = isInteger(arg0) && isInteger(arg1);
558 if (!allComplex && !allFloatingPoint && !allInteger) {
561 <<
"Cannot build binary Linalg operation: expects allComplex, "
562 "allFloatingPoint, or allInteger, got "
566 llvm_unreachable(
"unsupported non numeric type");
568 OpBuilder::InsertionGuard g(builder);
569 builder.setInsertionPointToEnd(&block);
573 return complex::AddOp::create(builder, arg0.
getLoc(), arg0, arg1);
574 if (allFloatingPoint)
575 return arith::AddFOp::create(builder, arg0.
getLoc(), arg0, arg1);
577 return arith::OrIOp::create(builder, arg0.
getLoc(), arg0, arg1);
578 return arith::AddIOp::create(builder, arg0.
getLoc(), arg0, arg1);
581 return complex::SubOp::create(builder, arg0.
getLoc(), arg0, arg1);
582 if (allFloatingPoint)
583 return arith::SubFOp::create(builder, arg0.
getLoc(), arg0, arg1);
586 emitError() <<
"unsupported operation: sub with bools";
589 llvm_unreachable(
"unsupported operation: sub with bools");
591 return arith::SubIOp::create(builder, arg0.
getLoc(), arg0, arg1);
594 return complex::MulOp::create(builder, arg0.
getLoc(), arg0, arg1);
595 if (allFloatingPoint)
596 return arith::MulFOp::create(builder, arg0.
getLoc(), arg0, arg1);
598 return arith::AndIOp::create(builder, arg0.
getLoc(), arg0, arg1);
599 return arith::MulIOp::create(builder, arg0.
getLoc(), arg0, arg1);
602 return complex::DivOp::create(builder, arg0.
getLoc(), arg0, arg1);
603 if (allFloatingPoint)
604 return arith::DivFOp::create(builder, arg0.
getLoc(), arg0, arg1);
607 emitError() <<
"unsupported operation: div with bools";
610 llvm_unreachable(
"unsupported operation: div with bools");
612 return arith::DivSIOp::create(builder, arg0.
getLoc(), arg0, arg1);
613 case BinaryFn::div_unsigned:
614 if (!allInteger || allBool) {
616 emitError() <<
"unsupported operation: unsigned div not on uint";
619 llvm_unreachable(
"unsupported operation: unsigned div not on uint");
621 return arith::DivUIOp::create(builder, arg0.
getLoc(), arg0, arg1);
622 case BinaryFn::max_signed:
624 if (allFloatingPoint)
625 return arith::MaximumFOp::create(builder, arg0.
getLoc(), arg0, arg1);
626 return arith::MaxSIOp::create(builder, arg0.
getLoc(), arg0, arg1);
627 case BinaryFn::min_signed:
629 if (allFloatingPoint)
630 return arith::MinimumFOp::create(builder, arg0.
getLoc(), arg0, arg1);
631 return arith::MinSIOp::create(builder, arg0.
getLoc(), arg0, arg1);
632 case BinaryFn::max_unsigned:
634 if (!allInteger || allBool) {
636 emitError() <<
"unsupported operation: unsigned max not on uint";
639 llvm_unreachable(
"unsupported operation: unsigned max not on uint");
641 return arith::MaxUIOp::create(builder, arg0.
getLoc(), arg0, arg1);
642 case BinaryFn::min_unsigned:
644 if (!allInteger || allBool) {
646 emitError() <<
"unsupported operation: unsigned min not on uint";
649 llvm_unreachable(
"unsupported operation: unsigned min not on uint");
651 return arith::MinUIOp::create(builder, arg0.
getLoc(), arg0, arg1);
653 assert(allFloatingPoint);
654 return math::PowFOp::create(builder, arg0.
getLoc(), arg0, arg1);
657 emitError() <<
"unsupported binary function";
660 llvm_unreachable(
"unsupported binary function");
664 Value buildTernaryFn(TernaryFn ternaryFn, Value arg0, Value arg1, Value arg2,
666 OpBuilder::InsertionGuard g(builder);
667 builder.setInsertionPointToEnd(&block);
669 case TernaryFn::select:
670 return arith::SelectOp::create(builder, arg0.
getLoc(), arg0, arg1, arg2);
673 emitError() <<
"unsupported ternary function";
676 llvm_unreachable(
"unsupported ternary function");
680 Value buildTypeFn(TypeFn typeFn, Type toType, Value operand,
683 case TypeFn::cast_signed:
684 return cast(toType, operand,
false);
685 case TypeFn::cast_unsigned:
686 return cast(toType, operand,
true);
689 emitError() <<
"unsupported type conversion function";
692 llvm_unreachable(
"unsupported type conversion function");
696 OpBuilder::InsertionGuard g(builder);
697 builder.setInsertionPointToEnd(&block);
698 Location loc = builder.getUnknownLoc();
699 YieldOp::create(builder, loc, values);
702 Value constant(
const std::string &value) {
703 OpBuilder::InsertionGuard g(builder);
704 builder.setInsertionPointToEnd(&block);
705 Location loc = builder.getUnknownLoc();
706 Attribute valueAttr =
parseAttribute(value, builder.getContext());
707 return arith::ConstantOp::create(builder, loc,
708 ::cast<TypedAttr>(valueAttr));
711 Value index(int64_t dim) {
712 OpBuilder::InsertionGuard g(builder);
713 builder.setInsertionPointToEnd(&block);
714 return IndexOp::create(builder, builder.getUnknownLoc(), dim);
717 Type getIntegerType(
unsigned width) {
718 return IntegerType::get(builder.getContext(), width);
721 Type getFloat32Type() {
return Float32Type::get(builder.getContext()); }
722 Type getFloat64Type() {
return Float64Type::get(builder.getContext()); }
729 Value cast(Type toType, Value operand,
bool isUnsignedCast) {
730 OpBuilder::InsertionGuard g(builder);
731 builder.setInsertionPointToEnd(&block);
732 auto loc = operand.
getLoc();
733 if (isa<UnknownLoc>(loc)) {
743 bool isComplex(Value value) {
744 return llvm::isa<ComplexType>(value.
getType());
746 bool isFloatingPoint(Value value) {
747 return llvm::isa<FloatType>(value.
getType());
749 bool isInteger(Value value) {
750 return llvm::isa<IntegerType>(value.
getType());
766 using OpRewritePattern<CopyOp>::OpRewritePattern;
767 LogicalResult matchAndRewrite(CopyOp copyOp,
768 PatternRewriter &rewriter)
const override {
769 if (copyOp.getInputs() != copyOp.getOutputs())
771 if (copyOp.hasPureBufferSemantics())
774 rewriter.
replaceOp(copyOp, copyOp.getInputs());
784 results.
add<EraseSelfCopy>(context);
797template <
typename TensorReshapeOp>
798struct FoldFillWithTensorReshape : OpRewritePattern<TensorReshapeOp> {
799 using OpRewritePattern<TensorReshapeOp>::OpRewritePattern;
800 LogicalResult matchAndRewrite(TensorReshapeOp reshapeOp,
801 PatternRewriter &rewriter)
const override {
802 auto oldFill = reshapeOp.getSrc().template getDefiningOp<FillOp>();
806 Location loc = oldFill.getLoc();
807 TensorReshapeOp newInit;
808 if constexpr (std::is_same<TensorReshapeOp, tensor::ExpandShapeOp>::value) {
810 newInit = TensorReshapeOp::create(
811 rewriter, loc, reshapeOp.getResultType(), oldFill.output(),
812 reshapeOp.getReassociation(), reshapeOp.getOutputShape(),
813 reshapeOp.getStaticOutputShape());
815 newInit = TensorReshapeOp::create(
816 rewriter, loc, reshapeOp.getResultType(), oldFill.output(),
817 reshapeOp.getReassociation());
827struct FoldFillWithPad final :
public OpRewritePattern<tensor::PadOp> {
830 LogicalResult matchAndRewrite(tensor::PadOp padOp,
831 PatternRewriter &rewriter)
const override {
832 auto fillOp = padOp.getSource().getDefiningOp<linalg::FillOp>();
838 Value padValue = padOp.getConstantPaddingValue();
839 if (!padValue || fillOp.value() != padValue)
845 padOp,
"failed to reify tensor.pad op result shape");
848 tensor::EmptyOp::create(rewriter, padOp.getLoc(), reifiedShape.front(),
849 padOp.getResultType().getElementType());
851 FillOp::create(rewriter, fillOp.getLoc(),
ValueRange{padValue},
854 if (
replacement.getType() != padOp.getResultType()) {
855 replacement = tensor::CastOp::create(rewriter, fillOp.getLoc(),
866struct FoldInsertPadIntoFill :
public OpRewritePattern<tensor::InsertSliceOp> {
869 LogicalResult matchAndRewrite(tensor::InsertSliceOp insertOp,
870 PatternRewriter &rewriter)
const override {
871 auto srcPadOp = insertOp.getSource().getDefiningOp<tensor::PadOp>();
875 if (insertOp.getType().getRank() != insertOp.getSourceType().getRank())
880 Value firstDest = insertOp.getDest();
881 while (
auto prevOp = firstDest.
getDefiningOp<tensor::InsertSliceOp>()) {
882 if (prevOp.getType().getRank() != prevOp.getSourceType().getRank())
887 bool disjoint =
false;
888 for (
int i = 0, e = prevOp.getType().getRank(); i < e; ++i) {
891 if (insertOp.isDynamicOffset(i) || insertOp.isDynamicSize(i) ||
892 insertOp.isDynamicStride(i) || prevOp.isDynamicOffset(i) ||
893 prevOp.isDynamicSize(i) || prevOp.isDynamicStride(i))
897 int64_t prevStart = prevOp.getStaticOffset(i);
898 int64_t prevEnd = prevStart + (prevOp.getStaticSize(i) - 1) *
899 prevOp.getStaticStride(i);
900 int64_t nextStart = insertOp.getStaticOffset(i);
901 int64_t nextEnd = nextStart + (insertOp.getStaticSize(i) - 1) *
902 insertOp.getStaticStride(i);
903 if (prevEnd < nextStart || nextEnd < prevStart) {
911 firstDest = prevOp.getDest();
922 Value padValue = srcPadOp.getConstantPaddingValue();
923 if (!padValue || dstFillOp.value() != padValue)
926 SmallVector<OpFoldResult> lowPads = srcPadOp.getMixedLowPad();
927 SmallVector<OpFoldResult> oldOffsets = insertOp.getMixedOffsets();
929 Location loc = insertOp.getLoc();
932 AffineExpr sym0, sym1;
938 SmallVector<OpFoldResult, 4> newOffsets;
939 for (
const auto &p : llvm::zip(lowPads, oldOffsets)) {
941 rewriter, loc, addMap, {std::get<0>(p), std::get<1>(p)}));
944 RankedTensorType srcPadType = srcPadOp.getSourceType();
945 SmallVector<OpFoldResult, 4> newSizes;
946 for (
int i = 0, e = srcPadType.getRank(); i < e; ++i) {
947 if (srcPadType.isDynamicDim(i)) {
949 tensor::DimOp::create(rewriter, loc, srcPadOp.getSource(), i)
952 newSizes.push_back(rewriter.
getIndexAttr(srcPadType.getDimSize(i)));
957 insertOp, srcPadOp.getSource(), insertOp.getDest(), newOffsets,
958 newSizes, insertOp.getMixedStrides());
964struct FoldFillWithTensorExtract :
public OpRewritePattern<tensor::ExtractOp> {
966 using OpRewritePattern<tensor::ExtractOp>::OpRewritePattern;
968 LogicalResult matchAndRewrite(tensor::ExtractOp extractOp,
969 PatternRewriter &rewriter)
const override {
972 auto fillOp = extractOp.getTensor().getDefiningOp<linalg::FillOp>();
977 Value extractedScalar = fillOp.getInputs()[0];
980 rewriter.
replaceOp(extractOp, extractedScalar);
988static FailureOr<FillOp> foldFillPackIntoFillOp(RewriterBase &rewriter,
989 linalg::PackOp packOp) {
990 auto fillOp = packOp.getSource().getDefiningOp<FillOp>();
994 if (
auto paddingValue = packOp.getPaddingValue())
998 Value packOpDest = packOp.getDest();
1002 return linalg::FillOp::create(rewriter, packOp.getLoc(), fillOp.getInputs(),
1007struct FoldFillWithPack :
public OpRewritePattern<linalg::PackOp> {
1009 FoldFillWithPack(MLIRContext *context)
1010 : OpRewritePattern<linalg::PackOp>(context) {}
1012 LogicalResult matchAndRewrite(linalg::PackOp packOp,
1013 PatternRewriter &rewriter)
const override {
1014 auto fillOp = foldFillPackIntoFillOp(rewriter, packOp);
1017 rewriter.
replaceOp(packOp, fillOp.value().result());
1023struct FoldFillWithCopy : OpRewritePattern<linalg::CopyOp> {
1024 using OpRewritePattern<linalg::CopyOp>::OpRewritePattern;
1026 LogicalResult matchAndRewrite(linalg::CopyOp copyOp,
1027 PatternRewriter &rewriter)
const override {
1028 if (
auto fillOp = copyOp.getInputs().front().getDefiningOp<FillOp>()) {
1031 copyOp.getOutputs());
1034 if (
auto fillOp = copyOp.getOutputs().front().getDefiningOp<FillOp>()) {
1036 fillOp.getOutputs());
1044struct FoldFillWithTranspose : OpRewritePattern<linalg::TransposeOp> {
1045 using OpRewritePattern<linalg::TransposeOp>::OpRewritePattern;
1047 LogicalResult matchAndRewrite(linalg::TransposeOp transposeOp,
1048 PatternRewriter &rewriter)
const override {
1049 if (
auto fillOp = transposeOp.getInput().getDefiningOp<FillOp>()) {
1051 transposeOp, transposeOp.getResultTypes(), fillOp.getInputs(),
1052 transposeOp.getDpsInitOperand(0)->get());
1061struct FoldConcatsOfFill :
public OpRewritePattern<tensor::ConcatOp> {
1064 LogicalResult matchAndRewrite(tensor::ConcatOp concatOp,
1065 PatternRewriter &rewriter)
const override {
1066 auto concatOperands = concatOp.getInputs();
1067 if (concatOperands.empty()) {
1071 auto firstFillOp = concatOperands.front().getDefiningOp<linalg::FillOp>();
1076 OpFoldResult firstFillVal =
1079 SmallVector<Value> allOuts;
1080 allOuts.push_back(firstFillOp.getDpsInitOperand(0)->get());
1082 auto isDefinedByCompatibleFillOp = [&](Value v) ->
bool {
1083 auto fillOp = v.getDefiningOp<linalg::FillOp>();
1088 OpFoldResult fillVal =
1090 if (fillVal != firstFillVal)
1093 allOuts.push_back(fillOp.getDpsInitOperand(0)->get());
1096 if (!llvm::all_of(concatOperands.drop_front(),
1097 isDefinedByCompatibleFillOp)) {
1099 concatOp,
"not all operands are defined by a compatible fill op");
1102 Value outsConcat = tensor::ConcatOp::create(rewriter, concatOp.getLoc(),
1103 concatOp.getDim(), allOuts);
1105 concatOp, firstFillOp.getDpsInputOperand(0)->
get(), outsConcat);
1112void FillOp::getCanonicalizationPatterns(RewritePatternSet &results,
1113 MLIRContext *context) {
1114 results.
add<FoldConcatsOfFill, FoldFillWithCopy, FoldFillWithTensorExtract,
1115 FoldFillWithPack, FoldFillWithPad,
1116 FoldFillWithTensorReshape<tensor::CollapseShapeOp>,
1117 FoldFillWithTensorReshape<tensor::ExpandShapeOp>,
1118 FoldInsertPadIntoFill, FoldFillWithTranspose>(context);
1131 for (
ValueRange container : {inputs, outputs}) {
1132 for (
Value v : container) {
1133 Type t = v.getType();
1134 blockArgTypes.push_back(
1136 blockArgLocs.push_back(v.getLoc());
1142 builder.
createBlock(®ion, region.
end(), blockArgTypes, blockArgLocs);
1146void GenericOp::getAsmBlockArgumentNames(Region ®ion,
1148 for (Value v : getRegionInputArgs())
1150 for (Value v : getRegionOutputArgs())
1151 setNameFn(v,
"out");
1154void GenericOp::build(
1155 OpBuilder &builder, OperationState &
result,
TypeRange resultTensorTypes,
1157 ArrayAttr iteratorTypes, StringAttr doc, StringAttr libraryCall,
1159 ArrayRef<NamedAttribute> attributes) {
1160 build(builder,
result, resultTensorTypes, inputs, outputs, indexingMaps,
1161 iteratorTypes, doc, libraryCall);
1162 result.addAttributes(attributes);
1165 inputs, outputs, bodyBuild);
1168void GenericOp::build(
1169 OpBuilder &builder, OperationState &
result,
TypeRange resultTensorTypes,
1171 ArrayRef<utils::IteratorType> iteratorTypes, StringRef doc,
1172 StringRef libraryCall,
1174 ArrayRef<NamedAttribute> attributes) {
1175 build(builder,
result, resultTensorTypes, inputs, outputs,
1179 [&](utils::IteratorType iter) -> mlir::Attribute {
1180 return IteratorTypeAttr::get(builder.getContext(), iter);
1183 libraryCall.empty() ? StringAttr() : builder.
getStringAttr(libraryCall),
1184 bodyBuild, attributes);
1187void GenericOp::build(
1189 ValueRange outputs, ArrayRef<AffineMap> indexingMaps,
1190 ArrayRef<utils::IteratorType> iteratorTypes, StringRef doc,
1191 StringRef libraryCall,
1193 ArrayRef<NamedAttribute> attributes) {
1195 iteratorTypes, doc, libraryCall, bodyBuild, attributes);
1198void GenericOp::build(
1200 ValueRange outputs, ArrayRef<AffineMap> indexingMaps,
1201 ArrayRef<utils::IteratorType> iteratorTypes,
1203 ArrayRef<NamedAttribute> attributes) {
1204 build(builder,
result, inputs, outputs, indexingMaps, iteratorTypes,
1206 "", bodyBuild, attributes);
1209void GenericOp::build(
1210 OpBuilder &builder, OperationState &
result,
TypeRange resultTensorTypes,
1212 ArrayRef<utils::IteratorType> iteratorTypes,
1214 ArrayRef<NamedAttribute> attributes) {
1215 build(builder,
result, resultTensorTypes, inputs, outputs, indexingMaps,
1218 "", bodyBuild, attributes);
1221void GenericOp::print(OpAsmPrinter &p) {
1225 auto genericAttrNames = linalgTraitAttrNames();
1227 llvm::StringSet<> genericAttrNamesSet;
1228 genericAttrNamesSet.insert_range(genericAttrNames);
1229 SmallVector<NamedAttribute, 8> genericAttrs;
1230 for (StringRef attrName : genericAttrNames) {
1231 std::optional<Attribute> value = (*this)->getInherentAttr(attrName);
1232 if (!value || !*value)
1234 NamedAttribute attr{StringAttr::get(
getContext(), attrName), *value};
1235 if (attr.
getName() == getIteratorTypesAttrName()) {
1236 auto iteratorTypes =
1237 llvm::cast<ArrayAttr>(attr.
getValue())
1238 .getAsValueRange<IteratorTypeAttr, utils::IteratorType>();
1243 SmallVector<Attribute> iteratorTypeNames = llvm::map_to_vector(
1244 iteratorTypes, [&](utils::IteratorType t) -> Attribute {
1245 return StringAttr::get(
getContext(), stringifyIteratorType(t));
1248 genericAttrs.emplace_back(
1249 getIteratorTypesAttrName(),
1250 ArrayAttr::get(
getContext(), iteratorTypeNames));
1251 }
else if (genericAttrNamesSet.count(attr.
getName().strref()) > 0) {
1252 genericAttrs.push_back(attr);
1255 if (!genericAttrs.empty()) {
1256 auto genericDictAttr = DictionaryAttr::get(
getContext(), genericAttrs);
1257 p << genericDictAttr;
1263 genericAttrNames.push_back(
"operandSegmentSizes");
1264 genericAttrNamesSet.insert(genericAttrNames.back());
1266 bool hasExtraAttrs =
false;
1267 for (NamedAttribute n : (*this)->getDiscardableAttrDictionary()) {
1268 if ((hasExtraAttrs = !genericAttrNamesSet.contains(n.getName().strref())))
1271 if (hasExtraAttrs) {
1278 if (!getRegion().empty()) {
1287ParseResult GenericOp::parse(OpAsmParser &parser, OperationState &
result) {
1288 DictionaryAttr dictAttr;
1296 result.attributes.assign(dictAttr.getValue().begin(),
1297 dictAttr.getValue().end());
1303 auto iteratorTypes = dyn_cast_or_null<ArrayAttr>(
1304 result.attributes.get(getIteratorTypesAttrName(
result.name)));
1305 if (!iteratorTypes) {
1306 return parser.
emitError(attributeLocation)
1307 <<
"expected " << getIteratorTypesAttrName(
result.name)
1308 <<
" array attribute";
1311 SmallVector<Attribute> iteratorTypeAttrs;
1313 for (StringRef s : iteratorTypes.getAsValueRange<StringAttr>()) {
1314 auto maybeIteratorType = utils::symbolizeIteratorType(s);
1315 if (!maybeIteratorType.has_value())
1317 <<
"unexpected iterator_type (" << s <<
")";
1319 iteratorTypeAttrs.push_back(
1320 IteratorTypeAttr::get(parser.
getContext(), maybeIteratorType.value()));
1322 result.attributes.set(getIteratorTypesAttrName(
result.name),
1326 SmallVector<Type, 1> inputTypes, outputTypes;
1336 std::unique_ptr<Region> region = std::make_unique<Region>();
1339 result.addRegion(std::move(region));
1345 SmallVector<Type, 1> outputTensorsTypes;
1348 result.addTypes(outputTensorsTypes);
1356 LinalgOp linalgOp) {
1357 for (
auto [
index, operand] : llvm::enumerate(linalgOp.getDpsInputs())) {
1358 if (!llvm::isa<MemRefType>(operand.
getType()))
1360 effects.emplace_back(
1365 for (
OpOperand &operand : linalgOp.getDpsInitsMutable()) {
1366 if (!llvm::isa<MemRefType>(operand.get().
getType()))
1368 if (linalgOp.payloadUsesValueFromOperand(&operand)) {
1379void GenericOp::getEffects(
1380 SmallVectorImpl<SideEffects::EffectInstance<MemoryEffects::Effect>>
1389 if (!linalgOp.hasPureTensorSemantics())
1407template <
typename OpTy>
1408struct EraseIdentityLinalgOp :
public OpRewritePattern<OpTy> {
1409 using OpRewritePattern<OpTy>::OpRewritePattern;
1411 LogicalResult matchAndRewrite(OpTy linalgOp,
1412 PatternRewriter &rewriter)
const override {
1414 if (!llvm::all_equal(linalgOp.getIndexingMapsArray()))
1419 Block &body = linalgOp->getRegion(0).front();
1420 if (!llvm::hasSingleElement(body))
1422 auto yieldOp = dyn_cast<linalg::YieldOp>(body.
getTerminator());
1427 if (linalgOp.hasPureBufferSemantics()) {
1428 if (linalgOp.getNumDpsInputs() != 1 || linalgOp.getNumDpsInits() != 1 ||
1429 linalgOp.getDpsInputOperand(0)->get() !=
1430 linalgOp.getDpsInitOperand(0)->get()) {
1432 linalgOp,
"expected single input and output to be the same value");
1435 auto yieldArg = dyn_cast<BlockArgument>(yieldOp.getOperand(0));
1436 if (!yieldArg || yieldArg.getOwner() != &body) {
1438 "cannot fold fill-like op");
1445 if (!linalgOp.hasPureTensorSemantics()) {
1447 linalgOp,
"mixed semantics is not supported yet");
1452 SmallVector<Value> returnedArgs;
1453 for (
const auto &yieldVal : llvm::enumerate(yieldOp.getValues())) {
1454 auto yieldArg = llvm::dyn_cast<BlockArgument>(yieldVal.value());
1455 if (!yieldArg || yieldArg.getOwner() != &body)
1457 unsigned argumentNumber = yieldArg.getArgNumber();
1458 Value returnedArg = linalgOp->getOperand(argumentNumber);
1459 Type resultType = linalgOp->getResult(yieldVal.index()).getType();
1462 Type returnType = returnedArg.
getType();
1463 if (returnType != resultType) {
1468 returnedArg = sparse_tensor::ConvertOp::create(
1469 rewriter, linalgOp.getLoc(), resultType, returnedArg);
1471 if (!tensor::CastOp::areCastCompatible(returnedArg.
getType(),
1474 returnedArg = tensor::CastOp::create(rewriter, linalgOp.getLoc(),
1475 resultType, returnedArg);
1478 returnedArgs.push_back(returnedArg);
1481 if (returnedArgs.size() != linalgOp->getNumResults())
1483 rewriter.
replaceOp(linalgOp, returnedArgs);
1490void GenericOp::getCanonicalizationPatterns(RewritePatternSet &results,
1491 MLIRContext *context) {
1492 results.
add<EraseIdentityLinalgOp<GenericOp>>(context);
1495LogicalResult GenericOp::fold(FoldAdaptor, SmallVectorImpl<OpFoldResult> &) {
1514 for (
Type outputType : outputTypes) {
1515 if (llvm::isa<RankedTensorType>(outputType))
1516 result.addTypes(outputType);
1520 if (parseAttrsFn && failed(parseAttrsFn(parser,
result.attributes)))
1529void MapOp::getAsmBlockArgumentNames(Region ®ion,
1531 for (Value v : getRegionInputArgs())
1533 for (Value v : getRegionOutputArgs())
1534 setNameFn(v,
"init");
1537void MapOp::getAsmResultNames(
function_ref<
void(Value, StringRef)> setNameFn) {
1538 if (!getResults().empty())
1539 setNameFn(getResults().front(),
"mapped");
1545 ArrayRef<NamedAttribute> attributes) {
1547 result.addAttributes(attributes);
1550 Type initType = init.
getType();
1551 if (llvm::isa<RankedTensorType>(initType))
1552 result.addTypes(initType);
1556 inputs, {init}, bodyBuild);
1563 bool initFirst =
false,
bool mapInit =
true) {
1567 b.setInsertionPointToStart(&block);
1568 for (
auto &operand : operands) {
1570 llvm::cast<ShapedType>(operand.
getType()).getElementType(),
1578 payloadOpOperands.push_back(block.
getArguments().back());
1579 for (
const auto &arg : block.
getArguments().drop_back())
1580 payloadOpOperands.push_back(arg);
1589 TypeRange{llvm::cast<ShapedType>(result.operands.back().getType())
1595ParseResult MapOp::parse(OpAsmParser &parser, OperationState &
result) {
1596 std::optional<OperationName> payloadOpName;
1597 NamedAttrList payloadOpAttrs;
1600 if (
failed(operationName))
1604 payloadOpName = operationName.value();
1612 if (payloadOpName.has_value()) {
1613 if (!
result.operands.empty())
1615 payloadOpAttrs, ArrayRef(
result.operands),
false,
1620 SmallVector<OpAsmParser::Argument> regionArgs;
1625 Region *body =
result.addRegion();
1633 bool mapInit =
true) {
1635 if (initFirst && !mapInit)
1659 for (
const auto &[operand, bbArg] :
1661 if (bbArg != operand)
1665 for (
const auto &[operand, bbArg] :
1668 if (bbArg != operand)
1675 return yieldOp.getNumOperands() == 1 &&
1676 yieldOp.getOperand(0).getDefiningOp() &&
1677 yieldOp.getOperand(0).getDefiningOp() == &payload;
1682 std::string attrToElide;
1688 for (
const auto &attr : attrs) {
1690 llvm::dyn_cast<mlir::arith::FastMathFlagsAttr>(attr.getValue());
1691 if (fastAttr && fastAttr.getValue() == mlir::arith::FastMathFlags::none) {
1692 attrToElide = attr.getName().str();
1693 elidedAttrs.push_back(attrToElide);
1701void MapOp::print(OpAsmPrinter &p) {
1702 Block *mapper = getBody();
1712 if (!useShortForm) {
1718 [&](
auto arg) { p.printRegionArgument(arg); });
1726LogicalResult MapOp::verify() {
1727 auto *bodyBlock = getBody();
1728 auto blockArgs = bodyBlock->getArguments();
1732 if (getInputs().size() + 1 != blockArgs.size())
1733 return emitOpError() <<
"expects number of operands to match the arity of "
1735 << getInputs().size() + 1 <<
" and "
1736 << blockArgs.size();
1739 for (
const auto &[bbArgType, inputArg] :
1740 llvm::zip(bodyBlock->getArgumentTypes(), getInputs())) {
1741 auto inputElemType =
1742 llvm::cast<ShapedType>(inputArg.getType()).getElementType();
1743 if (bbArgType != inputElemType) {
1744 return emitOpError() <<
"expected element type of input " << inputElemType
1745 <<
" to match bbArg type " << bbArgType;
1750 auto outputShape = getInit().getType().getShape();
1751 for (Type inputArgType :
TypeRange{getInputs()}) {
1752 auto inputElemShape = llvm::cast<ShapedType>(inputArgType).getShape();
1753 if (inputElemShape != outputShape) {
1754 return emitOpError() <<
"expected shape of input (" << inputElemShape
1755 <<
") to match shape of output (" << outputShape
1763SmallVector<utils::IteratorType> MapOp::getIteratorTypesArray() {
1764 int64_t rank = getInit().getType().getRank();
1765 return SmallVector<utils::IteratorType>(rank, utils::IteratorType::parallel);
1770 int64_t rank = getInit().getType().getRank();
1771 int64_t numIndexingMaps = getOperands().size();
1776void MapOp::getEffects(
1777 SmallVectorImpl<SideEffects::EffectInstance<MemoryEffects::Effect>>
1790void ReduceOp::getAsmBlockArgumentNames(Region ®ion,
1792 for (Value v : getRegionInputArgs())
1794 for (Value v : getRegionOutputArgs())
1795 setNameFn(v,
"init");
1798void ReduceOp::getAsmResultNames(
1800 if (!getResults().empty())
1801 setNameFn(getResults().front(),
"reduced");
1804void ReduceOp::build(
1806 ValueRange inits, ArrayRef<int64_t> dimensions,
1808 ArrayRef<NamedAttribute> attributes) {
1810 result.addAttributes(attributes);
1813 for (Value init : inits) {
1814 Type initType = init.
getType();
1815 if (llvm::isa<RankedTensorType>(initType))
1816 result.addTypes(initType);
1821 inputs, inits, bodyBuild);
1824SmallVector<utils::IteratorType> ReduceOp::getIteratorTypesArray() {
1826 llvm::cast<ShapedType>(getInputs()[0].
getType()).getRank();
1827 SmallVector<utils::IteratorType> iteratorTypes(inputRank,
1828 utils::IteratorType::parallel);
1829 for (int64_t reductionDim : getDimensions())
1830 iteratorTypes[reductionDim] = utils::IteratorType::reduction;
1831 return iteratorTypes;
1836 llvm::cast<ShapedType>(getInputs()[0].
getType()).getRank();
1837 SmallVector<AffineMap> affineMaps(
1840 AffineMap resultMap =
1843 for (int64_t i = 0, e = getNumDpsInits(); i < e; ++i)
1844 affineMaps.push_back(resultMap);
1845 return Builder(
getContext()).getAffineMapArrayAttr(affineMaps);
1848void ReduceOp::getEffects(
1849 SmallVectorImpl<SideEffects::EffectInstance<MemoryEffects::Effect>>
1860 StringRef attributeName) {
1868ParseResult ReduceOp::parse(OpAsmParser &parser, OperationState &
result) {
1869 std::optional<OperationName> payloadOpName;
1870 NamedAttrList payloadOpAttrs;
1873 if (
failed(operationName))
1877 payloadOpName = operationName.value();
1883 parser,
result, [&](OpAsmParser &parser, NamedAttrList &attributes) {
1888 if (payloadOpName.has_value()) {
1890 ArrayRef(
result.operands),
true);
1892 SmallVector<OpAsmParser::Argument> regionArgs;
1898 Region *body =
result.addRegion();
1908 p <<
' ' << attributeName <<
" = [" << attributeValue <<
"] ";
1911void ReduceOp::print(OpAsmPrinter &p) {
1912 Block *mapper = getBody();
1921 {getDimensionsAttrName()});
1922 if (!useShortForm) {
1928 [&](
auto arg) { p.printRegionArgument(arg); });
1936LogicalResult ReduceOp::verify() {
1937 ArrayRef<int64_t> dimensionsRef = getDimensions();
1944 if (getInputs().size() !=
static_cast<size_t>(getNumDpsInputs()))
1945 return emitOpError()
1946 <<
"expected equal number of inputs and outputs (required by "
1947 "SameVariadicOperandSize), got "
1948 << getNumDpsInputs() <<
" input(s) and " << getNumDpsInits()
1951 if (getInputs().empty())
1952 return emitOpError() <<
"expected at least one input";
1954 for (int64_t i = 1; i < getNumDpsInputs(); ++i) {
1957 return emitOpError() <<
"expects all inputs to have the same shapes. "
1958 "Shape at input-index "
1960 <<
" is not equal to the shape at input-index 0.";
1963 for (int64_t i = 1; i < getNumDpsInits(); ++i) {
1966 return emitOpError() <<
"expects all outputs to have the same shapes. "
1967 "Shape at output-index "
1969 <<
" is not equal to the shape at output-index 0.";
1972 auto inputType = llvm::cast<ShapedType>(getInputs()[0].
getType());
1973 auto initType = llvm::cast<ShapedType>(getInits()[0].
getType());
1976 for (int64_t dimension : dimensionsRef) {
1977 if (dimension < 0 || dimension >= inputType.getRank()) {
1978 return emitOpError()
1979 <<
"dimensions for reduction should be in the range [0, "
1980 << inputType.getRank() - 1 <<
"].";
1982 dimensionsToReduce.insert(dimension);
1985 auto inputDims = inputType.getShape();
1986 auto initDims = initType.getShape();
1989 SmallVector<int64_t> reducedInputDims;
1990 for (
const auto &en : llvm::enumerate(inputDims)) {
1991 if (!dimensionsToReduce.count(en.index()))
1992 reducedInputDims.push_back(en.value());
1995 if (reducedInputDims.size() !=
static_cast<size_t>(initType.getRank())) {
1996 return emitOpError() <<
"number of dimensions after reduction "
1997 << reducedInputDims.size()
1998 <<
" doesn't match the init rank "
1999 << initType.getRank();
2002 if (reducedInputDims != initDims)
2003 return emitOpError() <<
"init dimensions [" << initDims
2004 <<
"] doesn't match input dimensions after reduction ["
2005 << reducedInputDims <<
"]";
2007 Block *block = getBody();
2009 return emitOpError()
2010 <<
"mismatching number of operands and block arguments";
2013 for (
auto [input, bbArg] : llvm::zip(getInputs(), block->
getArguments())) {
2014 Type inputElementType =
2015 llvm::cast<ShapedType>(input.getType()).getElementType();
2016 if (inputElementType != bbArg.getType())
2017 return emitOpError()
2018 <<
"input element type " << inputElementType
2019 <<
" does not match corresponding block argument type "
2024 for (
auto [output, bbArg] : llvm::zip(
2025 getDpsInits(), block->
getArguments().take_back(getNumDpsInits()))) {
2026 auto outputElementType =
2027 llvm::cast<ShapedType>(output.getType()).getElementType();
2028 if (outputElementType != bbArg.getType())
2029 return emitOpError()
2030 <<
"output element type " << outputElementType
2031 <<
" does not match corresponding block argument type "
2042enum class BroadcastReduceKind {
2054static std::optional<BroadcastReduceKind>
2055matchBroadcastReduceBody(ReduceOp reduceOp) {
2056 if (reduceOp.getNumDpsInputs() != 1 || reduceOp.getNumDpsInits() != 1 ||
2057 !reduceOp.getBody())
2058 return std::nullopt;
2065 Block &block = *reduceOp.getBody();
2068 return std::nullopt;
2072 Operation *combineOp = yieldOp.getOperand(0).getDefiningOp();
2074 return std::nullopt;
2082 return std::nullopt;
2086 .Case<arith::MaxSIOp>(
2087 [](arith::MaxSIOp) {
return BroadcastReduceKind::MaxSI; })
2088 .Case<arith::MaxUIOp>(
2089 [](arith::MaxUIOp) {
return BroadcastReduceKind::MaxUI; })
2090 .Case<arith::MinSIOp>(
2091 [](arith::MinSIOp) {
return BroadcastReduceKind::MinSI; })
2092 .Case<arith::MinUIOp>(
2093 [](arith::MinUIOp) {
return BroadcastReduceKind::MinUI; })
2094 .Case<arith::AndIOp>(
2095 [](arith::AndIOp) {
return BroadcastReduceKind::AndI; })
2096 .Case<arith::OrIOp>([](arith::OrIOp) {
return BroadcastReduceKind::OrI; })
2097 .Case<arith::MaximumFOp>(
2098 [](arith::MaximumFOp) {
return BroadcastReduceKind::MaximumF; })
2099 .Case<arith::MinimumFOp>(
2100 [](arith::MinimumFOp) {
return BroadcastReduceKind::MinimumF; })
2101 .Default([](Operation *) -> std::optional<BroadcastReduceKind> {
2102 return std::nullopt;
2107static bool hasBroadcastReduceIdentity(Value init, BroadcastReduceKind kind) {
2113 if (kind == BroadcastReduceKind::MaximumF ||
2114 kind == BroadcastReduceKind::MinimumF) {
2115 auto floatAttr = dyn_cast<FloatAttr>(*initAttr);
2118 const APFloat &value = floatAttr.getValue();
2119 return kind == BroadcastReduceKind::MaximumF ? value.isNegInfinity()
2120 : value.isPosInfinity();
2123 auto integerAttr = dyn_cast<IntegerAttr>(*initAttr);
2127 const APInt &value = integerAttr.getValue();
2129 case BroadcastReduceKind::MaxSI:
2130 return value.isMinSignedValue();
2131 case BroadcastReduceKind::MaxUI:
2132 return value.isZero();
2133 case BroadcastReduceKind::MinSI:
2134 return value.isMaxSignedValue();
2135 case BroadcastReduceKind::MinUI:
2136 case BroadcastReduceKind::AndI:
2137 return value.isAllOnes();
2138 case BroadcastReduceKind::OrI:
2139 return value.isZero();
2140 case BroadcastReduceKind::MaximumF:
2141 case BroadcastReduceKind::MinimumF:
2142 llvm_unreachable(
"float kinds handled by the branch above");
2144 llvm_unreachable(
"unknown broadcast reduction kind");
2157struct FoldReduceBroadcast :
public OpRewritePattern<linalg::ReduceOp> {
2158 using OpRewritePattern<linalg::ReduceOp>::OpRewritePattern;
2160 LogicalResult matchAndRewrite(linalg::ReduceOp reduceOp,
2161 PatternRewriter &rewriter)
const override {
2162 if (reduceOp.getNumResults() != 1 || !reduceOp.hasPureTensorSemantics())
2165 assert(reduceOp.getInputs().size() == 1 &&
2166 "expected one input for a single-result tensor reduce");
2169 reduceOp.getInputs().front().getDefiningOp<linalg::BroadcastOp>();
2170 if (!broadcastOp || !broadcastOp.hasPureTensorSemantics())
2173 auto sourceType = cast<RankedTensorType>(broadcastOp.getInput().getType());
2174 auto broadcastType =
2175 cast<RankedTensorType>(broadcastOp.getResult().front().getType());
2176 auto resultType = cast<RankedTensorType>(reduceOp.getResult(0).getType());
2177 if (!sourceType.hasStaticShape() || !broadcastType.hasStaticShape() ||
2178 !resultType.hasStaticShape() || sourceType != resultType)
2181 ArrayRef<int64_t> broadcastDims = broadcastOp.getDimensions();
2182 ArrayRef<int64_t> reduceDims = reduceOp.getDimensions();
2183 if (broadcastDims != reduceDims)
2188 for (int64_t dimension : broadcastDims)
2189 if (broadcastType.getDimSize(dimension) == 0)
2192 std::optional<BroadcastReduceKind> kind =
2193 matchBroadcastReduceBody(reduceOp);
2195 !hasBroadcastReduceIdentity(reduceOp.getInits().front(), *kind))
2198 rewriter.
replaceOp(reduceOp, broadcastOp.getInput());
2205void ReduceOp::getCanonicalizationPatterns(RewritePatternSet &results,
2206 MLIRContext *context) {
2207 results.
add<FoldReduceBroadcast>(context);
2220 linalg::YieldOp::create(
b, loc, args[0]);
2224void TransposeOp::build(::mlir::OpBuilder &builder,
2225 ::mlir::OperationState &
result, Value input, Value init,
2227 ArrayRef<NamedAttribute> attributes) {
2228 result.addOperands(input);
2229 result.addOperands(init);
2230 result.addAttribute(getPermutationAttrName(
result.name), permutation);
2231 result.addAttributes(attributes);
2234 Type initType = init.
getType();
2235 if (llvm::isa<RankedTensorType>(initType))
2236 result.addTypes(initType);
2242void TransposeOp::build(::mlir::OpBuilder &builder,
2243 ::mlir::OperationState &
result, Value input, Value init,
2244 ArrayRef<int64_t> permutation,
2245 ArrayRef<NamedAttribute> attributes) {
2250ParseResult TransposeOp::parse(OpAsmParser &parser, OperationState &
result) {
2252 parser,
result, [&](OpAsmParser &parser, NamedAttrList &attributes) {
2264void TransposeOp::getAsmResultNames(
2266 if (!getResults().empty())
2267 setNameFn(getResults().front(),
"transposed");
2270void TransposeOp::print(OpAsmPrinter &p) {
2274 {getPermutationAttrName()});
2277LogicalResult TransposeOp::verify() {
2278 ArrayRef<int64_t> permutationRef = getPermutation();
2281 return emitOpError(
"permutation is not valid");
2283 auto inputType = getInput().getType();
2284 auto initType = getInit().getType();
2286 int64_t rank = inputType.getRank();
2292 if (rank !=
static_cast<int64_t
>(permutationRef.size()))
2293 return emitOpError() <<
"size of permutation " << permutationRef.size()
2294 <<
" does not match the argument rank " << rank;
2296 auto inputDims = inputType.getShape();
2297 auto initDims = initType.getShape();
2299 for (int64_t i = 0; i < rank; ++i) {
2300 int64_t inputDim = inputDims[permutationRef[i]];
2301 int64_t initDim = initDims[i];
2303 if (inputDim != initDim) {
2304 return emitOpError() <<
"dim(result, " << i <<
") = " << initDim
2305 <<
" doesn't match dim(input, permutation[" << i
2306 <<
"]) = " << inputDim;
2313SmallVector<utils::IteratorType> TransposeOp::getIteratorTypesArray() {
2314 int64_t rank = getInit().getType().getRank();
2315 return SmallVector<utils::IteratorType>(rank, utils::IteratorType::parallel);
2318ArrayAttr TransposeOp::getIndexingMaps() {
2320 int64_t rank = getInit().getType().getRank();
2323 llvm::to_vector_of<unsigned>(getPermutation()),
getContext())),
2327void TransposeOp::getEffects(
2328 SmallVectorImpl<SideEffects::EffectInstance<MemoryEffects::Effect>>
2337LogicalResult TransposeOp::fold(FoldAdaptor adaptor,
2338 SmallVectorImpl<OpFoldResult> &
result) {
2340 if (!isa<TensorType>(getInput().
getType()))
2344 if (getPermutation().empty()) {
2345 result.push_back(getInput());
2350 result.push_back(getInput());
2363 auto defTransposeOp = transposeOp.getInput().getDefiningOp<TransposeOp>();
2364 if (!defTransposeOp)
2369 foldedPerms.reserve(perms.size());
2371 foldedPerms.push_back(defPerms[perm]);
2374 transposeOp, defTransposeOp.getInput(), transposeOp.getInit(),
2387 if (!transposeOp.hasPureTensorSemantics())
2392 if (!splatValue.has_value())
2396 cast<RankedTensorType>(transposeOp.getResult()[0].getType());
2413 Value input = transposeOp.getInput();
2414 BroadcastOp broadcastOp = input.
getDefiningOp<BroadcastOp>();
2425 unsigned dimensionSize = dimensions.size();
2426 for (
unsigned i = 0; i < dimensionSize; ++i)
2427 resultDimensions.push_back(invertPerm[dimensions[i]]);
2430 Value broadcastInput = broadcastOp.getInput();
2431 Location loc = transposeOp.getLoc();
2434 auto broadcastInputTy =
2435 mlir::cast<RankedTensorType>(broadcastInput.
getType());
2436 unsigned inputRank = broadcastInputTy.getRank();
2437 for (
unsigned i = 0; i < inputRank; ++i) {
2438 if (broadcastInputTy.isDynamicDim(i)) {
2439 dims.push_back(tensor::DimOp::create(rewriter, loc, broadcastInput, i)
2442 dims.push_back(IntegerAttr::get(IndexType::get(ctx),
2443 broadcastInputTy.getDimSize(i)));
2448 Value transposeInit = tensor::EmptyOp::create(
2449 rewriter, transposeOp.getLoc(), transposeResultShapes,
2450 broadcastInputTy.getElementType());
2453 Value transposeResult =
2454 TransposeOp::create(rewriter, loc, broadcastOp.getInput(),
2455 transposeInit, resultPerms)
2458 transposeOp, transposeResult, transposeOp.getInit(), resultDimensions);
2463void TransposeOp::getCanonicalizationPatterns(RewritePatternSet &results,
2464 MLIRContext *context) {
2465 results.
add<FoldTransposeWithTranspose, FoldTransposeSplatConstant,
2466 SwapTransposeWithBroadcast>(context);
2473void BroadcastOp::build(::mlir::OpBuilder &builder,
2474 ::mlir::OperationState &
result, Value input, Value init,
2476 ArrayRef<NamedAttribute> attributes) {
2477 result.addOperands(input);
2478 result.addOperands(init);
2479 result.addAttribute(getDimensionsAttrName(
result.name), dimensions);
2480 result.addAttributes(attributes);
2483 Type initType = init.
getType();
2484 if (llvm::isa<RankedTensorType>(initType))
2485 result.addTypes(initType);
2491void BroadcastOp::build(::mlir::OpBuilder &builder,
2492 ::mlir::OperationState &
result, Value input, Value init,
2493 ArrayRef<int64_t> dimensions,
2494 ArrayRef<NamedAttribute> attributes) {
2499ParseResult BroadcastOp::parse(OpAsmParser &parser, OperationState &
result) {
2501 parser,
result, [&](OpAsmParser &parser, NamedAttrList &attributes) {
2513void BroadcastOp::getAsmResultNames(
2515 if (!getResults().empty())
2516 setNameFn(getResults().front(),
"broadcasted");
2519void BroadcastOp::print(OpAsmPrinter &p) {
2523 {getDimensionsAttrName()});
2526LogicalResult BroadcastOp::verify() {
2527 ArrayRef<int64_t> dimensionsRef = getDimensions();
2529 auto inputType = getInput().getType();
2530 auto initType = getInit().getType();
2532 int64_t inputRank = inputType.getRank();
2533 int64_t initRank = initType.getRank();
2535 auto inputShape = inputType.getShape();
2536 auto initShape = initType.getShape();
2538 if ((
size_t)inputRank + dimensionsRef.size() != (
size_t)initRank)
2539 return emitOpError() <<
"input rank plus added dimensions does not "
2540 "match init rank. input rank: "
2542 <<
", dimensions size: " << dimensionsRef.size()
2543 <<
", init rank: " << initRank;
2545 for (
const auto &[idx, dim] : llvm::enumerate(dimensionsRef)) {
2546 if (dim < 0 || dim >= initRank)
2547 return emitOpError() <<
"dimension " << idx
2548 <<
" is out of range. expected range: [0, "
2549 << initRank - 1 <<
"], got: " << dim;
2553 if (uniquedDims.size() != dimensionsRef.size())
2554 return emitOpError() <<
"dimensions should not contain duplicates";
2557 SmallVector<int64_t> dimMap;
2558 for (
auto dim : llvm::seq<int64_t>(0, initRank)) {
2559 if (!llvm::is_contained(dimensionsRef, dim))
2560 dimMap.push_back(dim);
2563 for (
const auto &[inputDimIdx, initDimIdx] : llvm::enumerate(dimMap)) {
2566 if (inputShape[inputDimIdx] != initShape[initDimIdx])
2567 return emitOpError() <<
"input dim " << inputDimIdx
2568 <<
" should match init dim " << initDimIdx
2569 <<
". input: " << inputShape[inputDimIdx]
2570 <<
", init: " << initShape[initDimIdx];
2576SmallVector<utils::IteratorType> BroadcastOp::getIteratorTypesArray() {
2577 int64_t rank = getInit().getType().getRank();
2578 return SmallVector<utils::IteratorType>(rank, utils::IteratorType::parallel);
2581ArrayAttr BroadcastOp::getIndexingMaps() {
2583 int64_t rank = getInit().getType().getRank();
2589void BroadcastOp::getEffects(
2590 SmallVectorImpl<SideEffects::EffectInstance<MemoryEffects::Effect>>
2605 auto defBroadcastOp = broadcastOp.getInput().getDefiningOp<BroadcastOp>();
2606 if (!defBroadcastOp)
2611 Value init = broadcastOp.getInit();
2615 for (
auto dim : llvm::seq<int64_t>(0, initRank)) {
2616 if (!llvm::is_contained(dimensions, dim))
2617 dimMap.push_back(dim);
2619 for (
auto dim : defDimensions)
2620 foldedDims.push_back(dimMap[dim]);
2622 llvm::sort(foldedDims);
2624 broadcastOp, defBroadcastOp.getInput(), init, foldedDims);
2636 if (!broadcastOp.hasPureTensorSemantics())
2642 if (!splatValue.has_value())
2646 cast<RankedTensorType>(broadcastOp.getResult()[0].getType());
2647 if (!resultType.hasStaticShape())
2649 "result type has dynamic shape");
2658void BroadcastOp::getCanonicalizationPatterns(RewritePatternSet &results,
2659 MLIRContext *context) {
2660 results.
add<EraseIdentityLinalgOp<BroadcastOp>, FoldBroadcasts,
2661 FoldBroadcastSplatConstant>(context);
2668void linalg::YieldOp::print(OpAsmPrinter &p) {
2670 p <<
' ' << getOperands();
2673 p <<
" : " << getOperandTypes();
2676ParseResult YieldOp::parse(OpAsmParser &parser, OperationState &
result) {
2677 SmallVector<OpAsmParser::UnresolvedOperand, 2> opInfo;
2678 SmallVector<Type, 2> types;
2688static LogicalResult
verifyYield(linalg::YieldOp op, LinalgOp linalgOp) {
2689 if (op.getNumOperands() != linalgOp.getNumDpsInits())
2690 return op.emitOpError(
"expected number of yield values (")
2691 << op.getNumOperands()
2692 <<
") to match the number of inits / outs operands of the enclosing "
2693 <<
"LinalgOp (" << linalgOp.getNumDpsInits() <<
")";
2695 for (
OpOperand &opOperand : op->getOpOperands()) {
2697 linalgOp.getDpsInitOperand(opOperand.getOperandNumber());
2699 if (isa<MemRefType, RankedTensorType>(elementType))
2701 if (opOperand.get().getType() != elementType)
2702 return op.emitOpError(
"type of yield operand ")
2703 << (opOperand.getOperandNumber() + 1) <<
" ("
2704 << opOperand.get().getType() <<
") doesn't match "
2705 <<
"the element type of the enclosing linalg.generic op ("
2706 << elementType <<
")";
2711LogicalResult linalg::YieldOp::verify() {
2712 auto *parentOp = (*this)->getParentOp();
2713 if (parentOp->getNumRegions() != 1 || parentOp->getRegion(0).empty())
2714 return emitOpError(
"expected single non-empty parent region");
2716 if (
auto linalgOp = dyn_cast<LinalgOp>(parentOp))
2719 return emitOpError(
"expected parent op with LinalgOp interface");
2726LogicalResult IndexOp::verify() {
2727 auto linalgOp = dyn_cast<LinalgOp>((*this)->getParentOp());
2729 return emitOpError(
"expected parent op with LinalgOp interface");
2730 if (linalgOp.getNumLoops() <= getDim())
2731 return emitOpError(
"expected dim (")
2732 << getDim() <<
") to be lower than the number of loops ("
2733 << linalgOp.getNumLoops() <<
") of the enclosing LinalgOp";
2737OpFoldResult IndexOp::fold(FoldAdaptor adaptor) {
2738 auto linalgOp = dyn_cast_or_null<LinalgOp>((*this)->getParentOp());
2743 return OpFoldResult{};
2746 SmallVector<int64_t, 4> loopBounds = linalgOp.getStaticLoopRanges();
2747 uint64_t dim = getDim();
2748 assert(dim < loopBounds.size() &&
"Dim is out of bounds");
2749 if (loopBounds[dim] == 1)
2750 return IntegerAttr::get(IndexType::get(
getContext()), 0);
2752 return OpFoldResult{};
2757#include "mlir/Dialect/Linalg/IR/LinalgNamedStructuredOps.yamlgen.cpp.inc"
2759#define GET_OP_CLASSES
2760#include "mlir/Dialect/Linalg/IR/LinalgOps.cpp.inc"
2762#define GET_OP_CLASSES
2763#include "mlir/Dialect/Linalg/IR/LinalgStructuredOps.cpp.inc"
2764#define GET_OP_CLASSES
2765#include "mlir/Dialect/Linalg/IR/LinalgRelayoutOps.cpp.inc"
2782 for (
unsigned i = 0; i < num; ++i)
2789 auto rangeA = llvm::make_range(a.begin(), a.end());
2790 auto rangeB = llvm::make_range(
b.begin(),
b.end());
2791 auto concatRanges = llvm::concat<const AffineExpr>(rangeA, rangeB);
2792 return llvm::to_vector<4>(concatRanges);
2796 if (
auto memref = llvm::dyn_cast<MemRefType>(t)) {
2798 for (
auto size :
memref.getShape())
2805 if (
auto as =
memref.getMemorySpace()) {
2806 if (
auto attr = llvm::dyn_cast<IntegerAttr>(as))
2807 ss <<
"as" << attr.getInt();
2813 if (
auto vec = llvm::dyn_cast<VectorType>(t)) {
2816 vec.getShape(), [&](
int64_t i) { ss << i; }, [&]() { ss <<
"x"; });
2829 assert(isa<LinalgOp>(op));
2831 std::string fun =
"";
2833 if (UnaryFnAttr ufa = llvm::dyn_cast<UnaryFnAttr>(attr)) {
2834 fun = stringifyEnum(ufa.getValue()).str() +
"_";
2835 }
else if (BinaryFnAttr bfa = llvm::dyn_cast<BinaryFnAttr>(attr)) {
2836 fun = stringifyEnum(bfa.getValue()).str() +
"_";
2840 llvm::replace(name,
'.',
'_');
2841 llvm::raw_string_ostream ss(name);
2845 return std::string();
2860 LogicalResult matchAndRewrite(LinalgOp op,
2862 for (
OpOperand &opOperand : op->getOpOperands()) {
2866 auto mt = llvm::dyn_cast<MemRefType>(opOperand.get().getType());
2869 if (llvm::is_contained(op.getShape(&opOperand), 0)) {
2880struct FoldTensorCastConsumerOp :
public OpRewritePattern<tensor::CastOp> {
2881 using OpRewritePattern<tensor::CastOp>::OpRewritePattern;
2883 LogicalResult matchAndRewrite(tensor::CastOp castOp,
2884 PatternRewriter &rewriter)
const override {
2888 auto linalgOp = castOp.getSource().getDefiningOp<LinalgOp>();
2895 if (castOp->getBlock() != linalgOp->getBlock())
2898 OpBuilder::InsertionGuard guard(rewriter);
2901 Location loc = linalgOp.getLoc();
2902 OpResult resultValue = llvm::cast<OpResult>(castOp.getSource());
2905 llvm::cast<RankedTensorType>(castOp->getResult(0).getType());
2911 OpOperand *outOperand = linalgOp.getDpsInitOperand(resultNumber);
2913 tensor::CastOp::create(rewriter, loc, resultType, outOperand->
get());
2914 SmallVector<Value> newOperands = linalgOp.getDpsInputs();
2915 SmallVector<Value> outputOperands(linalgOp.getDpsInits().begin(),
2916 linalgOp.getDpsInits().end());
2917 outputOperands[resultNumber] = newOperand;
2918 newOperands.append(outputOperands.begin(), outputOperands.end());
2920 SmallVector<Type> resultTypes(linalgOp->result_type_begin(),
2921 linalgOp->result_type_end());
2922 resultTypes[resultNumber] = resultType;
2923 Operation *newOp =
clone(rewriter, linalgOp, resultTypes, newOperands);
2926 Value castBack = tensor::CastOp::create(
2930 results[resultNumber] = castBack;
2939static void populateMap(LinalgOp linalgOp, MutableArrayRef<OpOperand> operands,
2940 llvm::DenseMap<AffineExpr, int64_t> &affineExprToSize) {
2941 for (OpOperand &opOperand : operands) {
2942 if (linalgOp.isScalar(&opOperand))
2944 Value src = opOperand.get();
2945 auto sourceType = llvm::cast<RankedTensorType>(src.
getType());
2946 auto sourceMap = linalgOp.getMatchingIndexingMap(&opOperand);
2952 ArrayRef<int64_t> sourceShape = sourceType.getShape();
2954 if (
auto castOp = dyn_cast<tensor::CastOp>(parentOp)) {
2955 Value castSource = castOp.getSource();
2956 auto castSourceType =
2957 llvm::dyn_cast<RankedTensorType>(castSource.
getType());
2958 if (castSourceType && castSourceType.hasStaticShape())
2959 sourceShape = castSourceType.getShape();
2965 for (
unsigned i = 0; i < sourceShape.size(); i++) {
2966 if (sourceType.isDynamicDim(i))
2968 if (
auto affineDimExpr = dyn_cast<AffineDimExpr>(sourceMap.getResult(i)))
2969 affineExprToSize.try_emplace(affineDimExpr, sourceShape[i]);
2979static void createNewOperandWithStaticSizes(
2980 Location loc, PatternRewriter &rewriter, OpOperand *opOperand,
2981 llvm::DenseMap<AffineExpr, int64_t> &affineExprToSize, LinalgOp linalgOp,
2982 SmallVector<Value> &newOperands, SmallVector<Type> &resultTypes,
2983 bool &changeNeeded) {
2984 Value src = opOperand->
get();
2985 newOperands.push_back(src);
2986 if (linalgOp.isScalar(opOperand))
2988 auto sourceType = llvm::cast<RankedTensorType>(src.
getType());
2989 Type resultType = sourceType;
2990 if (sourceType.hasStaticShape() && linalgOp.isDpsInit(opOperand)) {
2991 resultTypes.push_back(resultType);
2994 ArrayRef<int64_t> sourceShape = sourceType.getShape();
2995 AffineMap sourceMap = linalgOp.getMatchingIndexingMap(opOperand);
2996 SmallVector<int64_t> newShape;
2999 bool newOperandNeeded =
false;
3000 for (
unsigned i = 0; i < sourceShape.size(); i++) {
3001 int64_t dimShape = sourceShape[i];
3002 AffineExpr dimExpr = sourceMap.
getResult(i);
3003 if (!affineExprToSize.contains(dimExpr) || !sourceType.isDynamicDim(i)) {
3004 newShape.push_back(dimShape);
3010 newShape.push_back(affineExprToSize[dimExpr]);
3011 newOperandNeeded =
true;
3013 resultType = RankedTensorType::get(newShape, sourceType.getElementType(),
3014 sourceType.getEncoding());
3015 if (newOperandNeeded) {
3016 changeNeeded =
true;
3019 Value newOperand = tensor::CastOp::create(rewriter, loc, resultType, src);
3021 newOperands[index] = newOperand;
3023 if (linalgOp.isDpsInit(opOperand))
3024 resultTypes.push_back(resultType);
3030struct InferStaticShapeOfOperands :
public OpInterfaceRewritePattern<LinalgOp> {
3031 using OpInterfaceRewritePattern<LinalgOp>::OpInterfaceRewritePattern;
3033 LogicalResult matchAndRewrite(LinalgOp linalgOp,
3034 PatternRewriter &rewriter)
const override {
3035 if (!linalgOp.hasPureTensorSemantics())
3039 if (llvm::any_of(linalgOp.getIndexingMapsArray(), [](AffineMap map) {
3040 return !map.isProjectedPermutation();
3045 llvm::DenseMap<AffineExpr, int64_t> affineExprToSize;
3046 Location loc = linalgOp.getLoc();
3050 populateMap(linalgOp, linalgOp->getOpOperands(), affineExprToSize);
3052 SmallVector<Value> newOperands;
3053 SmallVector<Type> resultTypes;
3057 bool changeNeeded =
false;
3058 newOperands.reserve(linalgOp->getNumOperands());
3059 resultTypes.reserve(linalgOp.getNumDpsInits());
3062 for (OpOperand &opOperand : linalgOp->getOpOperands()) {
3063 createNewOperandWithStaticSizes(loc, rewriter, &opOperand,
3064 affineExprToSize, linalgOp, newOperands,
3065 resultTypes, changeNeeded);
3074 Operation *newOp =
clone(rewriter, linalgOp, resultTypes, newOperands);
3075 SmallVector<Value> replacements;
3077 for (
auto it : llvm::zip(linalgOp->getResults(), newOp->
getResults())) {
3078 Value newResult = std::get<1>(it);
3079 Value oldResult = std::get<0>(it);
3080 Type newType = newResult.
getType();
3081 Type oldType = oldResult.
getType();
3082 replacements.push_back(
3083 (newType != oldType)
3084 ? tensor::CastOp::create(rewriter, loc, oldType, newResult)
3087 rewriter.
replaceOp(linalgOp, replacements);
3101LogicalResult SoftmaxOp::verify() {
3102 ShapedType inputType = getInputOperandType();
3103 ShapedType outputType = getOutputOperandType();
3105 ArrayRef<int64_t> inputShape = inputType.getShape();
3106 ArrayRef<int64_t> outputShape = outputType.getShape();
3108 return emitOpError(
"incompatible output shape");
3110 int64_t inputRank = getInputOperandRank();
3111 int64_t dimension = getDimension();
3112 if ((dimension < 0) || (dimension >= inputRank))
3113 return emitOpError(
"incorrect dimension specified");
3118SmallVector<Range> SoftmaxOp::getIterationDomain(OpBuilder &builder) {
3119 int64_t operandRank = getInputOperandRank();
3120 SmallVector<Range> loopBounds(operandRank);
3121 Location loc = getLoc();
3124 Value source = getInput();
3125 for (
auto dim : llvm::seq<int64_t>(0, operandRank)) {
3126 loopBounds[dim].offset = zero;
3127 loopBounds[dim].size =
getDimValue(builder, loc, source, dim);
3128 loopBounds[dim].stride = one;
3133SmallVector<utils::IteratorType> SoftmaxOp::getLoopIteratorTypes() {
3134 SmallVector<utils::IteratorType> iteratorTypes(getInputOperandRank(),
3135 utils::IteratorType::parallel);
3136 iteratorTypes[getDimension()] = utils::IteratorType::reduction;
3137 return iteratorTypes;
3143FailureOr<TilingResult> SoftmaxOp::getTiledImplementation(
3144 OpBuilder &builder, ArrayRef<OpFoldResult> offsets,
3145 ArrayRef<OpFoldResult> sizes, ArrayRef<InnerTileAlignment>) {
3149FailureOr<TilingResult>
3150SoftmaxOp::getTiledImplementation(OpBuilder &builder,
3151 ArrayRef<OpFoldResult> offsets,
3152 ArrayRef<OpFoldResult> sizes) {
3153 int64_t rank = getInputOperandRank();
3155 SmallVector<OpFoldResult> strides(rank, oneAttr);
3156 SmallVector<Value> tiledOperands;
3157 Operation *inputSlice =
3158 getSlice(builder, getLoc(), getInput(), offsets, sizes, strides);
3160 return emitOpError(
"failed to compute input slice");
3162 tiledOperands.emplace_back(inputSlice->
getResult(0));
3163 Operation *outputSlice =
3164 getSlice(builder, getLoc(), getOutput(), offsets, sizes, strides);
3166 return emitOpError(
"failed to compute output slice");
3168 tiledOperands.emplace_back(outputSlice->
getResult(0));
3170 SmallVector<Type, 4> resultTypes;
3171 if (hasPureTensorSemantics())
3172 resultTypes.push_back(tiledOperands[1].
getType());
3173 Operation *tiledOp =
3174 mlir::clone(builder, getOperation(), resultTypes, tiledOperands);
3176 return TilingResult{
3179 llvm::to_vector(ArrayRef<Operation *>{inputSlice, outputSlice})};
3182LogicalResult SoftmaxOp::getResultTilePosition(
3183 OpBuilder &builder,
unsigned resultNumber, ArrayRef<OpFoldResult> offsets,
3184 ArrayRef<OpFoldResult> sizes, SmallVector<OpFoldResult> &resultOffsets,
3185 SmallVector<OpFoldResult> &resultSizes) {
3186 if (resultNumber == 0) {
3187 resultOffsets.assign(offsets.begin(), offsets.end());
3188 resultSizes.assign(sizes.begin(), sizes.end());
3195LogicalResult SoftmaxOp::fold(FoldAdaptor, SmallVectorImpl<OpFoldResult> &) {
3200SoftmaxOp::reifyResultShapes(OpBuilder &
b,
3202 SmallVector<OpFoldResult> shapes;
3203 Location loc = getOperation()->getLoc();
3204 IRRewriter rewriter(
b);
3205 auto inputShapedType = llvm::cast<ShapedType>(getInputOperandType());
3206 auto outputShapedType = llvm::cast<ShapedType>(getOutputOperandType());
3207 for (int64_t dim : llvm::seq<int64_t>(0, getOutputOperandRank())) {
3208 if (!outputShapedType.isDynamicDim(dim)) {
3210 shapes.push_back(
b.getIndexAttr(inputShapedType.getDimSize(dim)));
3217 reifiedReturnShapes.emplace_back(std::move(shapes));
3221void SoftmaxOp::getEffects(
3222 SmallVectorImpl<SideEffects::EffectInstance<MemoryEffects::Effect>>
3224 for (
auto [index, operand] : llvm::enumerate(getDpsInputs())) {
3225 if (!llvm::isa<MemRefType>(operand.
getType()))
3228 &getOperation()->getOpOperand(index), 0,
3233 for (OpOperand &operand : getDpsInitsMutable()) {
3234 if (!llvm::isa<MemRefType>(operand.get().
getType()))
3265static std::tuple<SmallVector<utils::IteratorType>, SmallVector<AffineMap>>
3267 int64_t dim,
bool allParallel =
false) {
3269 utils::IteratorType::parallel);
3271 iteratorTypes[dim] = utils::IteratorType::reduction;
3275 for (
int i = 0; i < inputRank; i++) {
3282 return std::make_tuple(iteratorTypes, indexingMaps);
3287template <
typename T>
3290 auto inputType = cast<ShapedType>(input.
getType());
3292 int64_t inputRank = inputShape.size();
3293 auto [iteratorTypes, indexingMaps] =
3295 assert(indexingMaps.size() == 2 &&
3296 "We should have two maps: 1 for the input, 1 for the output");
3297 assert(indexingMaps[0].isIdentity() &&
"input map should be identity");
3299 auto genericOp = linalg::GenericOp::create(
3300 builder, loc, output.
getType(), input, output, indexingMaps,
3302 Value result = T::create(b, loc, args[0], args[1]);
3303 linalg::YieldOp::create(b, loc, result);
3305 return genericOp.getResult(0);
3313 auto inputType = cast<ShapedType>(input.
getType());
3315 int64_t inputRank = inputShape.size();
3317 builder, inputRank, dim,
true);
3318 assert(indexingMaps.size() == 2 &&
"We should have one map for each input");
3319 assert(indexingMaps[0].isIdentity() &&
"input map should be identity");
3321 indexingMaps.push_back(indexingMaps[0]);
3322 auto genericOp = linalg::GenericOp::create(
3324 indexingMaps, iteratorTypes,
3326 Value diff = arith::SubFOp::create(b, loc, args[0], args[1]);
3327 Value result = math::ExpOp::create(b, loc, diff);
3328 linalg::YieldOp::create(b, loc, result);
3330 return genericOp.getResult(0);
3340 auto inputType = cast<ShapedType>(numerator.
getType());
3342 int64_t inputRank = inputShape.size();
3344 builder, inputRank, dim,
true);
3345 assert(indexingMaps.size() == 2 &&
3346 "We should have one map for each input (2)");
3347 assert(indexingMaps[0].isIdentity() &&
"Numerator map should be identity");
3349 indexingMaps.push_back(indexingMaps[0]);
3350 auto genericOp = linalg::GenericOp::create(
3352 output, indexingMaps, iteratorTypes,
3354 Value result = arith::DivFOp::create(b, loc, args[0], args[1]);
3355 linalg::YieldOp::create(b, loc, result);
3357 return genericOp.getResult(0);
3379FailureOr<SmallVector<Value>> SoftmaxOp::decomposeOperation(OpBuilder &
b) {
3380 OpBuilder::InsertionGuard guard(
b);
3381 b.setInsertionPoint(*
this);
3382 Location loc = getLoc();
3383 Value input = getInput();
3384 ShapedType inputType = getInputOperandType();
3385 Type elementType = inputType.getElementType();
3386 int64_t reductionDim = getDimension();
3388 Value output = getOutput();
3389 dims.erase(dims.begin() + reductionDim);
3391 Value outputReduce = tensor::EmptyOp::create(
b, loc, dims, elementType);
3393 elementType,
b, loc,
3395 Value neutralForMaxFInit =
3396 linalg::FillOp::create(
b, loc, Value{neutralForMaxF}, outputReduce)
3408 linalg::FillOp::create(
b, loc, Value{zero}, outputReduce).
result();
3414 buildDivOp(
b, loc, numerator, denominator, output, reductionDim);
3415 return SmallVector<Value>{
result};
3422LogicalResult WinogradFilterTransformOp::verify() {
3423 auto filterType = cast<ShapedType>(getFilter().
getType());
3424 ArrayRef<int64_t> filterShape = filterType.getShape();
3425 int64_t filterH = filterShape[getFilterHDim()];
3426 int64_t filterW = filterShape[getFilterWDim()];
3427 WinogradConv2DFmr fmr = getFmr();
3431 if (filterH != r && filterH != 1)
3432 return emitOpError(
"expect filter height either equals to r or 1");
3433 if (filterW != r && filterW != 1)
3434 return emitOpError(
"expect filter width either equals to r or 1");
3435 if (filterH == 1 && filterW == 1)
3436 return emitOpError(
"expect either filter height or width equals to r");
3438 SmallVector<int64_t> expectedOutputShape;
3439 expectedOutputShape.push_back(filterH == r ? m + r - 1 : 1);
3440 expectedOutputShape.push_back(filterW == r ? m + r - 1 : 1);
3441 expectedOutputShape.push_back(filterShape[getFilterCDim()]);
3442 expectedOutputShape.push_back(filterShape[getFilterFDim()]);
3444 auto outputType = cast<ShapedType>(getOutput().
getType());
3445 ArrayRef<int64_t> outputShape = outputType.getShape();
3447 return emitOpError(
"the output shape is not expected");
3453WinogradFilterTransformOp::getIterationDomain(OpBuilder &builder) {
3454 Location loc = getLoc();
3457 Value filter = getFilter();
3458 int64_t filterRank = getFilterOperandRank();
3459 SmallVector<Range> loopBounds(filterRank);
3460 for (
unsigned dim = 0; dim < filterRank; ++dim) {
3461 loopBounds[dim].offset = zeroAttr;
3462 loopBounds[dim].size =
getDimValue(builder, loc, filter, dim);
3463 loopBounds[dim].stride = oneAttr;
3468SmallVector<utils::IteratorType>
3469WinogradFilterTransformOp::getLoopIteratorTypes() {
3470 int64_t filterRank = getFilterOperandRank();
3471 SmallVector<utils::IteratorType> iteratorTypes(filterRank,
3472 utils::IteratorType::parallel);
3473 return iteratorTypes;
3476LogicalResult WinogradFilterTransformOp::getResultTilePosition(
3477 OpBuilder &builder,
unsigned resultNumber, ArrayRef<OpFoldResult> offsets,
3478 ArrayRef<OpFoldResult> sizes, SmallVector<OpFoldResult> &resultOffsets,
3479 SmallVector<OpFoldResult> &resultSizes) {
3481 ShapedType filterType = getFilterOperandType();
3482 ArrayRef<int64_t> filterShape = filterType.getShape();
3483 int64_t filterH = filterShape[getFilterHDim()];
3484 int64_t filterW = filterShape[getFilterWDim()];
3485 WinogradConv2DFmr fmr = getFmr();
3488 int64_t alpha = m + r - 1;
3489 int64_t alphaH = filterH != 1 ? alpha : 1;
3490 int64_t alphaW = filterW != 1 ? alpha : 1;
3494 resultOffsets.append(
3495 {zeroAttr, zeroAttr, offsets[getFilterCDim()], offsets[getFilterFDim()]});
3497 {alphaHAttr, alphaWAttr, sizes[getFilterCDim()], sizes[getFilterFDim()]});
3505FailureOr<TilingResult> WinogradFilterTransformOp::getTiledImplementation(
3506 OpBuilder &builder, ArrayRef<OpFoldResult> offsets,
3507 ArrayRef<OpFoldResult> sizes, ArrayRef<InnerTileAlignment>) {
3517FailureOr<TilingResult> WinogradFilterTransformOp::getTiledImplementation(
3518 OpBuilder &builder, ArrayRef<OpFoldResult> offsets,
3519 ArrayRef<OpFoldResult> sizes) {
3522 ShapedType filterType = getFilterOperandType();
3523 ArrayRef<int64_t> filterShape = filterType.getShape();
3524 int64_t filterH = filterShape[getFilterHDim()];
3525 int64_t filterW = filterShape[getFilterWDim()];
3528 SmallVector<Value> tiledOperands;
3529 SmallVector<OpFoldResult> sliceOffsets, sliceSizes;
3531 sliceOffsets.append(
3532 {offsets[getFilterFDim()], zeroAttr, zeroAttr, offsets[getFilterCDim()]});
3533 sliceSizes.append({sizes[getFilterFDim()], filterHAttr, filterWAttr,
3534 sizes[getFilterCDim()]});
3535 int64_t filterRank = getFilterOperandRank();
3536 SmallVector<OpFoldResult> filterStrides(filterRank, oneAttr);
3537 Location loc = getLoc();
3538 auto filterSlice = tensor::ExtractSliceOp::create(
3539 builder, loc, getFilter(), sliceOffsets, sliceSizes, filterStrides);
3540 tiledOperands.emplace_back(filterSlice);
3542 SmallVector<OpFoldResult> resultOffsets, resultSizes;
3547 int64_t outputRank = getOutputOperandRank();
3548 SmallVector<OpFoldResult> outputStrides(outputRank, oneAttr);
3549 auto outputSlice = tensor::ExtractSliceOp::create(
3550 builder, loc, getOutput(), resultOffsets, resultSizes, outputStrides);
3551 tiledOperands.emplace_back(outputSlice);
3553 SmallVector<Type> resultTypes;
3554 resultTypes.push_back(tiledOperands[1].
getType());
3555 Operation *tiledOp =
3556 mlir::clone(builder, getOperation(), resultTypes, tiledOperands);
3558 return TilingResult{
3561 llvm::to_vector(ArrayRef<Operation *>{filterSlice, outputSlice})};
3568LogicalResult WinogradInputTransformOp::verify() {
3569 auto inputType = cast<ShapedType>(getInput().
getType());
3570 ArrayRef<int64_t> inputShape = inputType.getShape();
3571 int64_t inputH = inputShape[getInputHDim()];
3572 int64_t inputW = inputShape[getInputWDim()];
3573 WinogradConv2DFmr fmr = getFmr();
3576 int64_t tileSize = m + r - 1;
3578 auto outputType = cast<ShapedType>(getOutput().
getType());
3579 ArrayRef<int64_t> outputShape = outputType.getShape();
3580 bool leftTransform = outputShape[getOutputAlphaHDim()] != 1;
3581 bool rightTransform = outputShape[getOutputAlphaWDim()] != 1;
3583 SmallVector<int64_t> expectedOutputShape(6, inputH);
3584 if (ShapedType::isDynamic(inputH)) {
3585 expectedOutputShape[getOutputAlphaHDim()] = tileSize;
3586 expectedOutputShape[getOutputTileHDim()] = ShapedType::kDynamic;
3588 expectedOutputShape[getOutputAlphaHDim()] = leftTransform ? tileSize : 1;
3589 expectedOutputShape[getOutputTileHDim()] =
3590 leftTransform ? (inputH - (r - 1)) / m : inputH;
3592 if (ShapedType::isDynamic(inputW)) {
3593 expectedOutputShape[getOutputAlphaWDim()] = tileSize;
3594 expectedOutputShape[getOutputTileWDim()] = ShapedType::kDynamic;
3596 expectedOutputShape[getOutputAlphaWDim()] = rightTransform ? tileSize : 1;
3597 expectedOutputShape[getOutputTileWDim()] =
3598 rightTransform ? (inputW - (r - 1)) / m : inputW;
3600 expectedOutputShape[getOutputNDim()] = inputShape[getInputNDim()];
3601 expectedOutputShape[getOutputCDim()] = inputShape[getInputCDim()];
3604 return emitOpError(
"the output shape is not expected");
3610WinogradInputTransformOp::getIterationDomain(OpBuilder &builder) {
3611 Location loc = getLoc();
3614 Value output = getOutput();
3615 int64_t outputRank = getOutputOperandRank();
3616 SmallVector<Range> loopBounds(outputRank);
3617 for (
unsigned dim = 0; dim < outputRank; ++dim) {
3618 loopBounds[dim].offset = zeroAttr;
3620 loopBounds[dim].size =
getDimValue(builder, loc, output, dim);
3621 loopBounds[dim].stride = oneAttr;
3626SmallVector<utils::IteratorType>
3627WinogradInputTransformOp::getLoopIteratorTypes() {
3628 int64_t outputRank = getOutputOperandRank();
3629 SmallVector<utils::IteratorType> iteratorTypes(outputRank,
3630 utils::IteratorType::parallel);
3631 return iteratorTypes;
3634LogicalResult WinogradInputTransformOp::getResultTilePosition(
3635 OpBuilder &builder,
unsigned resultNumber, ArrayRef<OpFoldResult> offsets,
3636 ArrayRef<OpFoldResult> sizes, SmallVector<OpFoldResult> &resultOffsets,
3637 SmallVector<OpFoldResult> &resultSizes) {
3639 ShapedType outputType = getOutputOperandType();
3640 ArrayRef<int64_t> outputShape = outputType.getShape();
3641 int64_t outputAlphaH = outputShape[getOutputAlphaHDim()];
3642 int64_t outputAlphaW = outputShape[getOutputAlphaWDim()];
3644 WinogradConv2DFmr fmr = getFmr();
3647 int64_t alpha = m + r - 1;
3648 int64_t alphaH = outputAlphaH != 1 ? alpha : 1;
3649 int64_t alphaW = outputAlphaW != 1 ? alpha : 1;
3654 resultOffsets.append({zeroAttr, zeroAttr, offsets[getOutputTileHDim()],
3655 offsets[getOutputTileWDim()], offsets[getOutputNDim()],
3656 offsets[getOutputCDim()]});
3657 resultSizes.append({alphaHAttr, alphaWAttr, sizes[getOutputTileHDim()],
3658 sizes[getOutputTileWDim()], sizes[getOutputNDim()],
3659 sizes[getOutputCDim()]});
3667FailureOr<TilingResult> WinogradInputTransformOp::getTiledImplementation(
3668 OpBuilder &builder, ArrayRef<OpFoldResult> offsets,
3669 ArrayRef<OpFoldResult> sizes, ArrayRef<InnerTileAlignment>) {
3679FailureOr<TilingResult>
3680WinogradInputTransformOp::getTiledImplementation(OpBuilder &builder,
3681 ArrayRef<OpFoldResult> offsets,
3682 ArrayRef<OpFoldResult> sizes) {
3684 WinogradConv2DFmr fmr = getFmr();
3688 ShapedType outputType = getOutputOperandType();
3689 ArrayRef<int64_t> outputShape = outputType.getShape();
3690 int64_t alphaH = outputShape[getOutputAlphaHDim()];
3691 int64_t alphaW = outputShape[getOutputAlphaWDim()];
3693 Location loc = getLoc();
3695 auto identityAffineMap =
3697 auto offsetAffineMap =
3700 builder, loc, (alphaH != 1 ? offsetAffineMap : identityAffineMap),
3701 offsets[getOutputTileHDim()]);
3703 builder, loc, (alphaW != 1 ? offsetAffineMap : identityAffineMap),
3704 offsets[getOutputTileWDim()]);
3708 builder, loc, sizeAffineMap, sizes[getOutputTileHDim()]);
3710 builder, loc, sizeAffineMap, sizes[getOutputTileWDim()]);
3712 SmallVector<Value> tiledOperands;
3713 SmallVector<OpFoldResult> sliceOffsets, sliceSizes;
3715 OpFoldResult offsetH = OpFoldResult(mappedOffsetH);
3716 OpFoldResult offsetW = OpFoldResult(mappedOffsetW);
3717 sliceOffsets.append(
3718 {offsets[getOutputNDim()], offsetH, offsetW, offsets[getOutputCDim()]});
3719 OpFoldResult sizeH =
3720 alphaH != 1 ? OpFoldResult(mappedSizeH) : OpFoldResult(oneAttr);
3721 OpFoldResult sizeW =
3722 alphaW != 1 ? OpFoldResult(mappedSizeW) : OpFoldResult(oneAttr);
3724 {sizes[getOutputNDim()], sizeH, sizeW, sizes[getOutputCDim()]});
3725 int64_t inputRank = getInputOperandRank();
3726 SmallVector<OpFoldResult> inputStrides(inputRank, oneAttr);
3727 auto inputSlice = tensor::ExtractSliceOp::create(
3728 builder, loc, getInput(), sliceOffsets, sliceSizes, inputStrides);
3729 tiledOperands.emplace_back(inputSlice);
3731 SmallVector<OpFoldResult> resultOffsets, resultSizes;
3736 int64_t outputRank = getOutputOperandRank();
3737 SmallVector<OpFoldResult> outputStrides(outputRank, oneAttr);
3738 auto outputSlice = tensor::ExtractSliceOp::create(
3739 builder, loc, getOutput(), resultOffsets, resultSizes, outputStrides);
3740 tiledOperands.emplace_back(outputSlice);
3742 SmallVector<Type> resultTypes;
3743 resultTypes.push_back(tiledOperands[1].
getType());
3744 Operation *tiledOp =
3745 mlir::clone(builder, getOperation(), resultTypes, tiledOperands);
3747 return TilingResult{
3750 llvm::to_vector(ArrayRef<Operation *>{inputSlice, outputSlice})};
3757LogicalResult WinogradOutputTransformOp::verify() {
3758 auto valueType = cast<ShapedType>(getValue().
getType());
3759 ArrayRef<int64_t> valueShape = valueType.getShape();
3760 int64_t valueH = valueShape[getValueAlphaHDim()];
3761 int64_t valueW = valueShape[getValueAlphaWDim()];
3762 int64_t valueTileH = valueShape[getValueTileHDim()];
3763 int64_t valueTileW = valueShape[getValueTileWDim()];
3764 WinogradConv2DFmr fmr = getFmr();
3767 bool leftTransform = valueH != 1;
3768 bool rightTransform = valueW != 1;
3770 int64_t outputRank = getOutputOperandRank();
3771 SmallVector<int64_t> expectedOutputShape(outputRank, valueH);
3772 if (ShapedType::isDynamic(valueH) || ShapedType::isDynamic(valueTileH)) {
3773 expectedOutputShape[getOutputHDim()] = ShapedType::kDynamic;
3775 if (valueH != (leftTransform ? m + r - 1 : 1))
3776 return emitOpError(
"expect input height equals to input tile size");
3777 expectedOutputShape[getOutputHDim()] = (leftTransform ? m : 1) * valueTileH;
3779 if (ShapedType::isDynamic(valueW) || ShapedType::isDynamic(valueTileW)) {
3780 expectedOutputShape[getOutputWDim()] = ShapedType::kDynamic;
3782 if (valueW != (rightTransform ? m + r - 1 : 1))
3783 return emitOpError(
"expect input width equals to input tile size");
3784 expectedOutputShape[getOutputWDim()] =
3785 (rightTransform ? m : 1) * valueTileW;
3787 expectedOutputShape[getOutputNDim()] = valueShape[getValueNDim()];
3788 expectedOutputShape[getOutputFDim()] = valueShape[getValueFDim()];
3790 auto outputType = cast<ShapedType>(getOutput().
getType());
3791 ArrayRef<int64_t> outputShape = outputType.getShape();
3793 return emitOpError(
"the output shape is not expected");
3799WinogradOutputTransformOp::getIterationDomain(OpBuilder &builder) {
3800 Location loc = getLoc();
3803 Value value = getValue();
3804 int64_t valueRank = getValueOperandRank();
3805 SmallVector<Range> loopBounds(valueRank);
3806 for (
unsigned dim = 0; dim < valueRank; ++dim) {
3807 loopBounds[dim].offset = zeroAttr;
3809 loopBounds[dim].size =
getDimValue(builder, loc, value, dim);
3810 loopBounds[dim].stride = oneAttr;
3815SmallVector<utils::IteratorType>
3816WinogradOutputTransformOp::getLoopIteratorTypes() {
3817 int64_t valueRank = getValueOperandRank();
3818 SmallVector<utils::IteratorType> iteratorTypes(valueRank,
3819 utils::IteratorType::parallel);
3820 return iteratorTypes;
3823LogicalResult WinogradOutputTransformOp::getResultTilePosition(
3824 OpBuilder &builder,
unsigned resultNumber, ArrayRef<OpFoldResult> offsets,
3825 ArrayRef<OpFoldResult> sizes, SmallVector<OpFoldResult> &resultOffsets,
3826 SmallVector<OpFoldResult> &resultSizes) {
3827 WinogradConv2DFmr fmr = getFmr();
3831 Location loc = getLoc();
3833 auto identityAffineMap =
3838 ShapedType valueType = getValueOperandType();
3839 ArrayRef<int64_t> valueShape = valueType.getShape();
3840 int64_t valueH = valueShape[0];
3841 int64_t valueW = valueShape[1];
3843 builder, loc, (valueH != 1 ? affineMap : identityAffineMap),
3844 offsets[getValueTileHDim()]);
3846 builder, loc, (valueW != 1 ? affineMap : identityAffineMap),
3847 offsets[getValueTileWDim()]);
3849 builder, loc, affineMap, sizes[getValueTileHDim()]);
3851 builder, loc, affineMap, sizes[getValueTileWDim()]);
3854 OpFoldResult offsetH = OpFoldResult(mappedOffsetH);
3855 OpFoldResult offsetW = OpFoldResult(mappedOffsetW);
3856 OpFoldResult sizeH =
3857 valueH != 1 ? OpFoldResult(mappedSizeH) : OpFoldResult(oneAttr);
3858 OpFoldResult sizeW =
3859 valueW != 1 ? OpFoldResult(mappedSizeW) : OpFoldResult(oneAttr);
3861 resultOffsets.append(
3862 {offsets[getValueNDim()], offsetH, offsetW, offsets[getValueFDim()]});
3864 {sizes[getValueNDim()], sizeH, sizeW, sizes[getValueFDim()]});
3871FailureOr<TilingResult> WinogradOutputTransformOp::getTiledImplementation(
3872 OpBuilder &builder, ArrayRef<OpFoldResult> offsets,
3873 ArrayRef<OpFoldResult> sizes, ArrayRef<InnerTileAlignment>) {
3883FailureOr<TilingResult> WinogradOutputTransformOp::getTiledImplementation(
3884 OpBuilder &builder, ArrayRef<OpFoldResult> offsets,
3885 ArrayRef<OpFoldResult> sizes) {
3888 Location loc = getLoc();
3889 SmallVector<Value> tiledOperands;
3890 SmallVector<OpFoldResult> sliceOffsets, sliceSizes;
3892 ShapedType valueType = getValueOperandType();
3893 ArrayRef<int64_t> valueShape = valueType.getShape();
3894 int64_t alphaH = valueShape[getValueAlphaHDim()];
3895 int64_t alphaW = valueShape[getValueAlphaWDim()];
3899 sliceOffsets.append({zeroAttr, zeroAttr, offsets[getValueTileHDim()],
3900 offsets[getValueTileWDim()], offsets[getValueNDim()],
3901 offsets[getValueFDim()]});
3902 sliceSizes.append({alphaHAttr, alphaWAttr, sizes[getValueTileHDim()],
3903 sizes[getValueTileWDim()], sizes[getValueNDim()],
3904 sizes[getValueFDim()]});
3905 int64_t valueRank = getValueOperandRank();
3906 SmallVector<OpFoldResult> sliceStrides(valueRank, oneAttr);
3907 auto valueSlice = tensor::ExtractSliceOp::create(
3908 builder, loc, getValue(), sliceOffsets, sliceSizes, sliceStrides);
3909 tiledOperands.emplace_back(valueSlice);
3911 SmallVector<OpFoldResult> resultOffsets, resultSizes;
3916 int64_t outputRank = getOutputOperandRank();
3917 SmallVector<OpFoldResult> strides(outputRank, oneAttr);
3918 auto outputSlice = tensor::ExtractSliceOp::create(
3919 builder, loc, getOutput(), resultOffsets, resultSizes, strides);
3920 tiledOperands.emplace_back(outputSlice);
3922 SmallVector<Type> resultTypes;
3923 resultTypes.push_back(tiledOperands[1].
getType());
3924 Operation *tiledOp =
3925 mlir::clone(builder, getOperation(), resultTypes, tiledOperands);
3927 return TilingResult{
3930 llvm::to_vector(ArrayRef<Operation *>{valueSlice, outputSlice})};
3944 llvm::set_union(explicitSet, defaultSet);
3945 return explicitSet == defaultSet;
3965 matmulOp.getDefaultIndexingMaps(matmulOp->getContext());
3967 auto opIndexingMap = opIndexingMaps[opIndex];
3968 auto defaultIndexingMap = defaultIndexingMaps[opIndex];
3971 return matmulOp->emitOpError()
3972 <<
"Unexpected dim expression in map result.";
3975 if (!matmulOp.isValidLhsRhsBroadcastMap(opIndexingMap)) {
3976 return matmulOp->emitOpError()
3977 <<
"Invalid broadcast requested, should be (d2).";
3986template <
typename OpTy>
3989 AffineMap defaultIndexingMap,
bool isLHS) {
3990 assert((isa<BatchMatmulOp>(batchVariantMatmulOp) ||
3991 isa<BatchReduceMatmulOp>(batchVariantMatmulOp)) &&
3992 "Expected BatchMatmulOp or BatchReduceMatmulOp");
3995 return batchVariantMatmulOp->emitOpError()
3996 <<
"Unexpected result dim expression (outside the set of default "
4001 return batchVariantMatmulOp->emitOpError()
4002 <<
"no. of result dim expressions exceeds 3.";
4004 auto hasValidBatchDim = [](
AffineMap map) {
4011 if (!batchVariantMatmulOp.isValidLhsRhsBroadcastMap(opIndexingMap, isLHS))
4012 return batchVariantMatmulOp->emitOpError()
4013 <<
"Invalid broadcast requested.";
4014 }
else if (!hasValidBatchDim(opIndexingMap)) {
4015 return batchVariantMatmulOp->emitOpError()
4016 <<
"Invalid batch dimension expression.";
4024template <
typename OpTy>
4027 assert((isa<BatchMatmulOp>(batchVariantMatmulOp) ||
4028 isa<BatchReduceMatmulOp>(batchVariantMatmulOp)) &&
4029 "Expected BatchMatmulOp or BatchReduceMatmulOp");
4030 if (isa<BatchMatmulOp>(batchVariantMatmulOp) &&
4033 return batchVariantMatmulOp->emitOpError()
4034 <<
"expects 3 dims, but got (" << opIndexingMap.
getNumResults()
4037 if (isa<BatchReduceMatmulOp>(batchVariantMatmulOp) &&
4039 return batchVariantMatmulOp->emitOpError()
4040 <<
"expects 2 dims, but got (" << opIndexingMap.
getNumResults()
4044 auto areValidOutputResultDim = [&](
AffineMap outputMap) {
4045 return isa<BatchMatmulOp>(batchVariantMatmulOp)
4046 ? outputMap.getResult(0).isFunctionOfDim(0) &&
4047 outputMap.getResult(1).isFunctionOfDim(1) &&
4048 outputMap.getResult(2).isFunctionOfDim(2)
4049 : outputMap.getResult(0).isFunctionOfDim(1) &&
4050 outputMap.getResult(1).isFunctionOfDim(2);
4053 if (!areValidOutputResultDim(opIndexingMap)) {
4054 return batchVariantMatmulOp->emitOpError()
4055 <<
"Invalid output map result dimension.";
4064template <
typename OpTy>
4069 batchVariantMatmulOp.getIndexingMapsArray();
4071 batchVariantMatmulOp.getDefaultIndexingMaps(
4072 batchVariantMatmulOp->getContext());
4074 if (opIndexingMaps.size() != 3)
4075 return batchVariantMatmulOp->emitOpError()
4076 <<
"Indexing_map attribute must have 3 affine maps.";
4078 auto opIndexingMap = opIndexingMaps[opIndex];
4079 auto defaultIndexingMap = defaultIndexingMaps[opIndex];
4087 defaultIndexingMap, opIndex == 0)))
4097 if (m == 2 && r == 3)
4098 return WinogradConv2DFmr::F_2_3;
4099 if (m == 4 && r == 3)
4100 return WinogradConv2DFmr::F_4_3;
4101 if (m == 2 && r == 5)
4102 return WinogradConv2DFmr::F_2_5;
4103 return std::nullopt;
4108 case WinogradConv2DFmr::F_2_3:
4110 case WinogradConv2DFmr::F_4_3:
4112 case WinogradConv2DFmr::F_2_5:
4115 llvm_unreachable(
"Unkown WinogradConv2DFmr");
4122static FailureOr<SmallVector<SmallVector<int64_t>>>
4125 for (
auto map : maps) {
4126 AffineMapAttr attr = dyn_cast<AffineMapAttr>(map);
4130 for (
auto result : attr.getAffineMap().getResults()) {
4131 auto dim = dyn_cast<AffineDimExpr>(
result);
4134 pos.push_back(dim.getPosition());
4136 positions.push_back(pos);
4149 return indexingMaps;
4152bool MatmulOp::isDefaultIndexingMaps(Attribute attr) {
4153 ArrayAttr maps = dyn_cast<ArrayAttr>(attr);
4156 if (maps.size() != 3)
4161 return (*positions)[0] == SmallVector<int64_t>{0, 2} &&
4162 (*positions)[1] == SmallVector<int64_t>{2, 1} &&
4163 (*positions)[2] == SmallVector<int64_t>{0, 1};
4166SmallVector<utils::IteratorType> MatmulOp::getIteratorTypesArray() {
4167 return SmallVector<utils::IteratorType>{utils::IteratorType::parallel,
4168 utils::IteratorType::parallel,
4169 utils::IteratorType::reduction};
4172unsigned MatmulOp::getNumRegionArgs() {
return 3; }
4174std::string MatmulOp::getLibraryCallName() {
4178bool MatmulOp::hasDynamicIndexingMaps() {
return true; }
4182bool MatmulOp::hasUserDefinedMaps() {
4183 SmallVector<AffineMap, 3> defaultMaps =
4185 SmallVector<AffineMap, 3> explicitMaps = getIndexingMapsArray();
4186 return defaultMaps != explicitMaps;
4191void MatmulOp::regionBuilder(ImplicitLocOpBuilder &
b,
Block &block,
4192 ArrayRef<NamedAttribute> attrs,
4195 emitError() <<
"MatmulOp regionBuilder expects 3 args, got "
4200 "MatmulOp regionBuilder expects 3 args");
4201 RegionBuilderHelper helper(
b, block);
4202 SmallVector<Value> yields;
4204 TypeFn castVal = TypeFn::cast_signed;
4205 const auto *castIter = llvm::find_if(attrs, [&](
const NamedAttribute &attr) {
4206 return attr.
getName() ==
"cast";
4208 if (castIter != attrs.end()) {
4209 if (
auto attr = llvm::dyn_cast<TypeFnAttr>(castIter->getValue()))
4217 Value value3 = helper.buildBinaryFn(BinaryFn::mul, value1, value2,
emitError);
4218 if (!value1 || !value2 || !value3)
4220 Value value4 = helper.buildBinaryFn(BinaryFn::add, block.
getArgument(2),
4224 yields.push_back(value4);
4225 helper.yieldOutputs(yields);
4235bool MatmulOp::isValidLhsRhsBroadcastMap(AffineMap bcastMap) {
4236 assert(bcastMap.
getNumResults() == 1 &&
"Expected single result dim expr.");
4237 AffineExpr expr = bcastMap.
getResult(0);
4247 ArrayAttr arrayAttr;
4251 if (llvm::any_of(arrayAttr,
4252 [](
auto elt) {
return !dyn_cast<AffineMapAttr>(elt); }))
4254 <<
"element of indexing_maps array is not an affine_map";
4261 if (failed(indexingMapsAttr))
4264 if (*indexingMapsAttr ==
nullptr) {
4265 auto indexingMapAttrs = llvm::map_to_vector(
4266 MatmulOp::getDefaultIndexingMaps(parser.
getContext()),
4271 result.addAttribute(
"indexing_maps", *indexingMapsAttr);
4273 MatmulOp::getRegionBuilder());
4276void MatmulOp::print(OpAsmPrinter &p) {
4277 SmallVector<Attribute, 3> indexingMaps = llvm::map_to_vector<3>(
4278 MatmulOp::getDefaultIndexingMaps(
getContext()),
4279 [](AffineMap map) -> Attribute {
return AffineMapAttr::get(map); });
4280 if (!llvm::equal(getIndexingMaps(), indexingMaps))
4281 p <<
" indexing_maps = " << llvm::interleaved_array(getIndexingMaps());
4283 std::array<StringRef, 3> elidedAttrs = {
4284 "operandSegmentSizes",
"linalg.memoized_indexing_maps",
"indexing_maps"};
4290LogicalResult MatmulOp::verify() {
4292 if (!hasUserDefinedMaps())
4295 for (
unsigned opIndex = 0; opIndex < 2; opIndex++) {
4302LogicalResult MatmulOp::fold(FoldAdaptor, SmallVectorImpl<OpFoldResult> &) {
4306void MatmulOp::getEffects(
4307 SmallVectorImpl<SideEffects::EffectInstance<MemoryEffects::Effect>>
4309 if (hasPureTensorSemantics())
4318SmallVector<AffineMap>
4319MatmulTransposeAOp::getDefaultIndexingMaps(OpBuilder &builder) {
4320 AffineExpr d0, d1, d2;
4326 return {mapLHS, mapRHS, mapOut};
4330 ArrayAttr maps = dyn_cast<ArrayAttr>(attr);
4333 if (maps.size() != 3)
4336 if (failed(positions))
4348 MatmulOp::getRegionBuilder(), getDefaultIndexingMaps(builder));
4356 build(builder, state, inputs, outputs, attributes);
4357 auto res = dyn_cast<MatmulTransposeAOp>(builder.
create(state));
4358 assert(res &&
"builder didn't return the right type");
4368 MatmulOp::getRegionBuilder(), getDefaultIndexingMaps(builder));
4377 build(builder, state, resultTensorTypes, inputs, outputs, attributes);
4378 auto res = dyn_cast<MatmulTransposeAOp>(builder.
create(state));
4379 assert(res &&
"builder didn't return the right type");
4389 result.addAttribute(
"cast", cast);
4391 MatmulOp::getRegionBuilder(), getDefaultIndexingMaps(builder));
4400 build(builder, state, resultTensorTypes, inputs, outputs, cast, attributes);
4401 auto res = dyn_cast<MatmulTransposeAOp>(builder.
create(state));
4402 assert(res &&
"builder didn't return the right type");
4407 auto matmulOp = dyn_cast_or_null<linalg::MatmulOp>(op);
4409 matmulOp.getIndexingMapsAttr());
4413MatmulTransposeBOp::getDefaultIndexingMaps(
OpBuilder &builder) {
4420 return {mapLHS, mapRHS, mapOut};
4424 ArrayAttr maps = dyn_cast<ArrayAttr>(attr);
4427 if (maps.size() != 3)
4430 if (failed(positions))
4442 MatmulOp::getRegionBuilder(), getDefaultIndexingMaps(builder));
4450 build(builder, state, inputs, outputs, attributes);
4451 auto res = dyn_cast<MatmulTransposeBOp>(builder.
create(state));
4452 assert(res &&
"builder didn't return the right type");
4462 MatmulOp::getRegionBuilder(), getDefaultIndexingMaps(builder));
4471 build(builder, state, resultTensorTypes, inputs, outputs, attributes);
4472 auto res = dyn_cast<MatmulTransposeBOp>(builder.
create(state));
4473 assert(res &&
"builder didn't return the right type");
4483 result.addAttribute(
"cast", cast);
4485 MatmulOp::getRegionBuilder(), getDefaultIndexingMaps(builder));
4494 build(builder, state, resultTensorTypes, inputs, outputs, cast, attributes);
4495 auto res = dyn_cast<MatmulTransposeBOp>(builder.
create(state));
4496 assert(res &&
"builder didn't return the right type");
4501 auto matmulOp = dyn_cast_or_null<linalg::MatmulOp>(op);
4503 matmulOp.getIndexingMapsAttr());
4507BatchMatmulTransposeAOp::getDefaultIndexingMaps(
OpBuilder &builder) {
4514 return {mapLHS, mapRHS, mapOut};
4518 ArrayAttr maps = dyn_cast<ArrayAttr>(attr);
4521 if (maps.size() != 3)
4524 if (failed(positions))
4535 BatchMatmulOp::getRegionBuilder(),
4536 getDefaultIndexingMaps(builder));
4544 build(builder, state, inputs, outputs, attributes);
4545 auto res = dyn_cast<BatchMatmulTransposeAOp>(builder.
create(state));
4546 assert(res &&
"builder didn't return the right type");
4555 BatchMatmulOp::getRegionBuilder(),
4556 getDefaultIndexingMaps(builder));
4565 build(builder, state, resultTensorTypes, inputs, outputs, attributes);
4566 auto res = dyn_cast<BatchMatmulTransposeAOp>(builder.
create(state));
4567 assert(res &&
"builder didn't return the right type");
4575 result.addAttribute(
"cast", cast);
4577 BatchMatmulOp::getRegionBuilder(),
4578 getDefaultIndexingMaps(builder));
4587 build(builder, state, resultTensorTypes, inputs, outputs, cast, attributes);
4588 auto res = dyn_cast<BatchMatmulTransposeAOp>(builder.
create(state));
4589 assert(res &&
"builder didn't return the right type");
4594 auto matmulOp = dyn_cast_or_null<linalg::BatchMatmulOp>(op);
4596 matmulOp.getIndexingMapsAttr());
4600BatchMatmulTransposeBOp::getDefaultIndexingMaps(
OpBuilder &builder) {
4607 return {mapLHS, mapRHS, mapOut};
4611 ArrayAttr maps = dyn_cast<ArrayAttr>(attr);
4614 if (maps.size() != 3)
4617 if (failed(positions))
4628 BatchMatmulOp::getRegionBuilder(),
4629 getDefaultIndexingMaps(builder));
4637 build(builder, state, inputs, outputs, attributes);
4638 auto res = dyn_cast<BatchMatmulTransposeBOp>(builder.
create(state));
4639 assert(res &&
"builder didn't return the right type");
4648 BatchMatmulOp::getRegionBuilder(),
4649 getDefaultIndexingMaps(builder));
4658 build(builder, state, resultTensorTypes, inputs, outputs, attributes);
4659 auto res = dyn_cast<BatchMatmulTransposeBOp>(builder.
create(state));
4660 assert(res &&
"builder didn't return the right type");
4668 result.addAttribute(
"cast", cast);
4670 BatchMatmulOp::getRegionBuilder(),
4671 getDefaultIndexingMaps(builder));
4680 build(builder, state, resultTensorTypes, inputs, outputs, cast, attributes);
4681 auto res = dyn_cast<BatchMatmulTransposeBOp>(builder.
create(state));
4682 assert(res &&
"builder didn't return the right type");
4687 auto matmulOp = dyn_cast_or_null<linalg::BatchMatmulOp>(op);
4689 matmulOp.getIndexingMapsAttr());
4697 AffineMap outAffineMap = getIndexingMapsArray().pop_back_val();
4708 auto dimExpr = dyn_cast<AffineDimExpr>(
result);
4709 assert(dimExpr &&
"affine_map is a projected permutation");
4710 dimsInOutput[dimExpr.getPosition()] =
true;
4714 for (
auto dimOccursInOutput : dimsInOutput)
4715 iteratorTypes.push_back(dimOccursInOutput ? utils::IteratorType::parallel
4716 : utils::IteratorType::reduction);
4718 return iteratorTypes;
4721unsigned ContractOp::getNumRegionArgs() {
return 3; }
4724void ContractOp::regionBuilder(ImplicitLocOpBuilder &
b,
Block &block,
4725 ArrayRef<NamedAttribute> attrs,
4728 emitError() <<
"ContractOp regionBuilder expects 3 args, got "
4733 "ContractOp regionBuilder expects 3 args");
4734 RegionBuilderHelper helper(
b, block);
4736 TypeFn castSignedness = TypeFn::cast_signed;
4737 auto castIter = llvm::find_if(attrs, [&](
const NamedAttribute &attr) {
4738 return attr.
getName() ==
"cast";
4740 if (castIter != attrs.end()) {
4741 if (
auto attr = llvm::dyn_cast<TypeFnAttr>(castIter->getValue()))
4747 Value lhsAtOutType =
4748 helper.buildTypeFn(castSignedness, outType, block.
getArgument(0));
4749 Value rhsAtOutType =
4750 helper.buildTypeFn(castSignedness, outType, block.
getArgument(1));
4751 Value productAtOutType = helper.buildBinaryFn(BinaryFn::mul, lhsAtOutType,
4753 if (!productAtOutType)
4759 helper.yieldOutputs({
result});
4762ParseResult ContractOp::parse(OpAsmParser &parser, OperationState &
result) {
4764 if (
failed(indexingMapsAttr) || *indexingMapsAttr ==
nullptr)
4766 "expected 'indexing_maps' attribute");
4767 result.addAttribute(
"indexing_maps", *indexingMapsAttr);
4773void ContractOp::print(OpAsmPrinter &p) {
4774 p <<
" indexing_maps = " << llvm::interleaved_array(getIndexingMaps());
4776 p, getOperation(), getInputs(), getOutputs(),
4777 {
"indexing_maps",
"operandSegmentSizes"});
4788 bool isInput,
int &iterationSpaceDims,
4793 return emitError() <<
"provided affine_map is not a projected permutation";
4795 if (
auto shapedType = dyn_cast<ShapedType>(operandType)) {
4798 <<
"ranks of shaped operand and results of corresponding "
4799 "affine_map differ";
4802 <<
"affine_map specifies shaped access while operand has "
4806 if (iterationSpaceDims == -1) {
4810 }
else if (iterationSpaceDims != (
int)affineMap.
getNumDims()) {
4811 return emitError() <<
"iteration spaces of provided affine_maps differ";
4816 auto affineDimExpr = dyn_cast<AffineDimExpr>(affineExpr);
4818 llvm_unreachable(
"affine_map is a projected permutation");
4821 inOccurrences[affineDimExpr.getPosition()] += 1;
4823 outOccurrences[affineDimExpr.getPosition()] += 1;
4839 bool hasContractingDim =
false;
4840 for (
size_t dimIndex = 0; dimIndex < iterationSpaceDims; dimIndex++) {
4841 size_t inOccCount = inOccurrences[dimIndex];
4842 size_t outOccCount = outOccurrences[dimIndex];
4845 hasContractingDim |= inOccCount == 2 && outOccCount == 0;
4847 if (inOccCount == 0 && outOccCount == 0)
4848 return emitError() <<
"iteration space dim at index " << dimIndex
4849 <<
" not used to access any operand";
4858 if (inOccCount == 1 && outOccCount != 1)
4859 return emitError() <<
"reduction dim at index " << dimIndex
4860 <<
" must appear in both LHS and RHS";
4863 if (!hasContractingDim)
4865 <<
"'indexing_maps' do not specify a contracting dimension";
4870LogicalResult ContractOp::verify() {
4871 int iterationSpaceDims = -1;
4879 for (
auto &&[affineMap, operandType, isInput] :
4880 llvm::zip(getIndexingMapsArray(), getOperandTypes(),
4883 affineMap, operandType, isInput, iterationSpaceDims, inOccurrences,
4884 outOccurrences, [&]() {
return emitError(); })))
4889 inOccurrences, outOccurrences,
4893LogicalResult ContractOp::fold(FoldAdaptor, SmallVectorImpl<OpFoldResult> &) {
4897void ContractOp::getEffects(
4898 SmallVectorImpl<SideEffects::EffectInstance<MemoryEffects::Effect>>
4900 if (hasPureTensorSemantics())
4912SmallVector<AffineMap>
4913BatchMatmulOp::getDefaultIndexingMaps(MLIRContext *context) {
4914 AffineExpr d0, d1, d2, d3;
4915 SmallVector<AffineMap> indexingMaps;
4917 indexingMaps.push_back(
AffineMap::get(4, 0, {d0, d1, d3}, context));
4918 indexingMaps.push_back(
AffineMap::get(4, 0, {d0, d3, d2}, context));
4919 indexingMaps.push_back(
AffineMap::get(4, 0, {d0, d1, d2}, context));
4920 return indexingMaps;
4923bool BatchMatmulOp::isDefaultIndexingMaps(Attribute attr) {
4924 ArrayAttr maps = dyn_cast<ArrayAttr>(attr);
4927 if (maps.size() != 3)
4932 return (*positions)[0] == SmallVector<int64_t>{0, 1, 3} &&
4933 (*positions)[1] == SmallVector<int64_t>{0, 3, 2} &&
4934 (*positions)[2] == SmallVector<int64_t>{0, 1, 2};
4937SmallVector<utils::IteratorType> BatchMatmulOp::getIteratorTypesArray() {
4938 return SmallVector<utils::IteratorType>{
4939 utils::IteratorType::parallel, utils::IteratorType::parallel,
4940 utils::IteratorType::parallel, utils::IteratorType::reduction};
4943unsigned BatchMatmulOp::getNumRegionArgs() {
return 3; }
4945std::string BatchMatmulOp::getLibraryCallName() {
4951bool BatchMatmulOp::hasUserDefinedMaps() {
4952 SmallVector<AffineMap, 3> defaultMaps =
4954 SmallVector<AffineMap, 3> explicitMaps = getIndexingMapsArray();
4955 return defaultMaps != explicitMaps;
4965bool BatchMatmulOp::isValidLhsRhsBroadcastMap(AffineMap bcastMap,
bool isLHS) {
4967 "Expected less than 3 result dim expr.");
4968 bool isValid =
false;
4969 enum Indices { batchPos, mPos, nPos, kPos };
4971 AffineExpr expr = bcastMap.
getResult(0);
4974 AffineExpr expr0 = bcastMap.
getResult(0);
4975 AffineExpr expr1 = bcastMap.
getResult(1);
4980 : ((expr0.isFunctionOfDim(batchPos) &&
4981 expr1.isFunctionOfDim(kPos)) ||
4982 (expr0.isFunctionOfDim(kPos) && expr1.isFunctionOfDim(nPos)));
4987void BatchMatmulOp::regionBuilder(
4988 ImplicitLocOpBuilder &
b,
Block &block, ArrayRef<NamedAttribute> attrs,
4991 emitError() <<
"BatchMatmulOp regionBuilder expects 3 args, got "
4996 "BatchMatmulOp regionBuilder expects 3 args");
4997 RegionBuilderHelper helper(
b, block);
4998 SmallVector<Value> yields;
5000 TypeFn castVal = TypeFn::cast_signed;
5001 auto castIter = llvm::find_if(attrs, [&](
const NamedAttribute &attr) {
5002 return attr.
getName() ==
"cast";
5004 if (castIter != attrs.end()) {
5005 if (
auto attr = llvm::dyn_cast<TypeFnAttr>(castIter->getValue()))
5010 Value castValA = helper.buildTypeFn(castVal, toType, block.
getArgument(0));
5011 Value castValB = helper.buildTypeFn(castVal, toType, block.
getArgument(1));
5013 helper.buildBinaryFn(BinaryFn::mul, castValA, castValB,
emitError);
5014 if (!castValA || !castValB || !mulVal)
5016 Value addVal = helper.buildBinaryFn(BinaryFn::add, block.
getArgument(2),
5020 yields.push_back(addVal);
5021 helper.yieldOutputs(yields);
5024ParseResult BatchMatmulOp::parse(OpAsmParser &parser, OperationState &
result) {
5025 SmallVector<Attribute, 3> indexingMapsAttr;
5037 if (!isa<AffineMapAttr>(mapAttr)) {
5039 "expected affine map attribute");
5041 indexingMapsAttr.push_back(mapAttr);
5051 if (indexingMapsAttr.empty()) {
5052 indexingMapsAttr = llvm::map_to_vector(
5053 BatchMatmulOp::getDefaultIndexingMaps(parser.
getContext()),
5054 [](AffineMap map) -> Attribute { return AffineMapAttr::get(map); });
5056 result.addAttribute(
"indexing_maps",
5059 return ::parseNamedStructuredOp(parser,
result,
5060 BatchMatmulOp::getNumRegionArgs(),
5061 BatchMatmulOp::getRegionBuilder());
5064void BatchMatmulOp::print(OpAsmPrinter &p) {
5065 SmallVector<Attribute, 3> indexingMaps = llvm::map_to_vector<3>(
5066 BatchMatmulOp::getDefaultIndexingMaps(
getContext()),
5067 [](AffineMap map) -> Attribute {
return AffineMapAttr::get(map); });
5068 if (!llvm::equal(getIndexingMaps(), indexingMaps))
5069 p <<
" indexing_maps = " << llvm::interleaved_array(getIndexingMaps());
5071 std::array<StringRef, 3> elidedAttrs = {
5072 "operandSegmentSizes",
"linalg.memoized_indexing_maps",
"indexing_maps"};
5078LogicalResult BatchMatmulOp::verify() {
5081 if (!hasUserDefinedMaps())
5084 for (
unsigned opIndex = 0; opIndex < 3; opIndex++) {
5091LogicalResult BatchMatmulOp::fold(FoldAdaptor,
5092 SmallVectorImpl<OpFoldResult> &) {
5096void BatchMatmulOp::getEffects(
5097 SmallVectorImpl<SideEffects::EffectInstance<MemoryEffects::Effect>>
5099 if (hasPureTensorSemantics())
5113unsigned getArityGroupAsUInt(ElementwiseArityGroup arityGroup) {
5114 return static_cast<unsigned>(arityGroup);
5119 constexpr int lastUnary =
static_cast<int>(ElementwiseCaseLimits::LastUnary);
5120 constexpr int lastBinary =
5121 static_cast<int>(ElementwiseCaseLimits::LastBinary);
5122 constexpr int lastTernary =
5123 static_cast<int>(ElementwiseCaseLimits::LastTernary);
5125 int val =
static_cast<int>(kind);
5128 if (val < lastUnary) {
5129 result.arityGroup = ElementwiseArityGroup::Unary;
5130 result.kind.unaryFn =
static_cast<UnaryFn
>(val);
5133 if (val < lastBinary) {
5134 result.arityGroup = ElementwiseArityGroup::Binary;
5135 result.kind.binaryFn =
static_cast<BinaryFn
>(val - lastUnary);
5138 if (val >= lastTernary) {
5139 llvm_unreachable(
"unhandled ElementwiseFn");
5141 result.arityGroup = ElementwiseArityGroup::Ternary;
5142 result.kind.ternaryFn =
static_cast<TernaryFn
>(val - lastBinary);
5147 auto rank = getResultRank();
5152ElementwiseOp::getDefaultIndexingMaps(
unsigned numMaps,
unsigned numDims,
5158ParseResult ElementwiseOp::parse(OpAsmParser &parser, OperationState &
result) {
5161 ElementwiseKindAttr kindAttr;
5162 mlir::linalg::ElementwiseKind elemwiseKindVal;
5165 elemwiseKindVal = kindAttr.getValue();
5166 result.addAttribute(
"kind", kindAttr);
5169 SmallVector<Attribute, 3> indexingMapsAttr;
5179 if (!isa<AffineMapAttr>(mapAttr))
5181 "expected affine map attribute");
5182 indexingMapsAttr.push_back(mapAttr);
5193 getArityGroupAsUInt(arityGroupAndKind.arityGroup) + 1 ;
5195 ElementwiseOp::getRegionBuilder())) {
5197 "unable to parse elemwise op");
5201 if (indexingMapsAttr.empty()) {
5204 auto resultType =
result.operands[
result.operands.size() - 1].getType();
5205 auto shapedType = llvm::dyn_cast<ShapedType>(resultType);
5208 "return type needs to be shaped type");
5209 auto numDims = shapedType.getRank();
5210 indexingMapsAttr = llvm::map_to_vector(
5211 ElementwiseOp::getDefaultIndexingMaps(numRegionArgs, numDims,
5213 [](AffineMap map) -> Attribute { return AffineMapAttr::get(map); });
5216 result.addAttribute(
"indexing_maps",
5221void ElementwiseOp::print(OpAsmPrinter &p) {
5224 SmallVector<StringRef, 3> elidedAttrs = {
"operandSegmentSizes",
"kind",
5228 unsigned numDims = getResultRank();
5230 SmallVector<Attribute, 3> indexingMaps = llvm::map_to_vector<3>(
5231 ElementwiseOp::getDefaultIndexingMaps(arity + 1 , numDims,
5233 [](AffineMap map) -> Attribute {
return AffineMapAttr::get(map); });
5235 if (!llvm::equal(getIndexingMaps(), indexingMaps))
5236 p <<
" indexing_maps = " << llvm::interleaved_array(getIndexingMaps());
5244void ElementwiseOp::regionBuilder(
5245 ImplicitLocOpBuilder &
b,
Block &block, ArrayRef<NamedAttribute> attrs,
5247 std::optional<ElementwiseKind> elemwiseKind;
5248 for (
auto attr : attrs) {
5249 if (attr.
getName() ==
"kind") {
5250 auto kindAttr = dyn_cast<ElementwiseKindAttr>(attr.
getValue());
5253 emitError() <<
"'kind' must be an ElementwiseKindAttr";
5256 elemwiseKind = kindAttr.getValue();
5261 if (!elemwiseKind) {
5263 emitError() <<
"missing required 'kind' attribute";
5269 auto kind = groupAndKind.
kind;
5271 getArityGroupAsUInt(arityGroup) + 1 ) {
5272 emitError() <<
"Elementwise regionBuilder expects "
5273 << (getArityGroupAsUInt(arityGroup) + 1) <<
" args, got "
5278 getArityGroupAsUInt(arityGroup) + 1
5279 &&
"Elementwise regionBuilder number of block args mismatch");
5281 RegionBuilderHelper helper(
b, block);
5282 SmallVector<Value> yields;
5285 if (arityGroup == ElementwiseArityGroup::Unary) {
5288 }
else if (arityGroup == ElementwiseArityGroup::Binary) {
5292 }
else if (arityGroup == ElementwiseArityGroup::Ternary) {
5297 assert(
false &&
"found unhandled category in elemwise");
5300 yields.push_back(
result);
5301 helper.yieldOutputs(yields);
5304LogicalResult ElementwiseOp::fold(FoldAdaptor,
5305 SmallVectorImpl<OpFoldResult> &) {
5309void ElementwiseOp::getEffects(
5310 SmallVectorImpl<SideEffects::EffectInstance<MemoryEffects::Effect>>
5312 if (hasPureTensorSemantics())
5325template <
typename OpTy,
typename>
5328 ShapedType packedType = (std::is_same<OpTy, PackOp>::value)
5329 ? packOrUnPack.getDestType()
5330 : packOrUnPack.getSourceType();
5331 ShapedType unpackedType = (std::is_same<OpTy, PackOp>::value)
5332 ? packOrUnPack.getSourceType()
5333 : packOrUnPack.getDestType();
5335 packedType.getShape().take_front(unpackedType.getRank()));
5336 if (!packOrUnPack.getOuterDimsPerm().empty()) {
5357 for (
auto it : llvm::zip(cast<ShapedType>(newPackedTy)
5359 .take_back(mixedTiles.size()),
5361 int64_t dimSize = std::get<0>(it);
5362 if (dimSize == ShapedType::kDynamic) {
5363 newMixedTileSizes.push_back(std::get<1>(it));
5366 newMixedTileSizes.push_back(rewriter.
getIndexAttr(dimSize));
5369 return newMixedTileSizes;
5372template <
typename OpTy>
5376 static_assert(llvm::is_one_of<OpTy, PackOp, UnPackOp>::value,
5377 "applies to only pack or unpack operations");
5378 int64_t destRank = op.getDestRank();
5380 for (
auto dim : llvm::seq<int64_t>(0, destRank))
5381 reifiedReturnShapes[0][dim] =
5386template <
typename OpTy>
5388 static_assert(llvm::is_one_of<OpTy, PackOp, UnPackOp>::value,
5389 "applies to only pack or unpack operations");
5393 assert(tiles.size() == dimsToTile.size() &&
5394 "tiles must match indices of dimension to block");
5396 for (
auto i : llvm::seq<int64_t>(0, dimsToTile.size()))
5397 dimAndTileMapping[dimsToTile[i]] = tiles[i];
5398 return dimAndTileMapping;
5401template <
typename OpTy>
5403 static_assert(llvm::is_one_of<OpTy, PackOp, UnPackOp>::value,
5404 "applies to only pack or unpack operations");
5407 unsigned dynamicValIndex = 0;
5408 for (
int64_t staticTile : op.getStaticInnerTiles()) {
5409 if (ShapedType::isStatic(staticTile))
5412 mixedInnerTiles.push_back(op.getInnerTiles()[dynamicValIndex++]);
5414 return mixedInnerTiles;
5417template <
typename OpTy>
5419 static_assert(llvm::is_one_of<OpTy, PackOp, UnPackOp>::value,
5420 "applies to only pack or unpack operations");
5433 size_t dimsPosSize = dimsPos.size();
5434 if (dimsPosSize > rank)
5437 if (dimsPosSize != uniqued.size())
5439 return llvm::any_of(dimsPos, [rank](
int64_t dimPos) {
5440 return dimPos < 0 || dimPos >=
static_cast<int64_t>(rank);
5444template <
typename OpTy>
5446 static_assert(llvm::is_one_of<OpTy, PackOp, UnPackOp>::value,
5447 "applies to only pack or unpack operations");
5448 Operation *op = packOrUnPack.getOperation();
5458 if (!packOrUnPack.getSourceType().hasRank() ||
5459 !packOrUnPack.getDestType().hasRank())
5460 return op->
emitError(
"expected both source and destination to have rank");
5463 if (!packOrUnPack.hasPureBufferSemantics() &&
5464 !packOrUnPack.hasPureTensorSemantics())
5465 return op->
emitError(
"mixing tensor and buffer semantics is not allowed");
5466 const unsigned numResults = packOrUnPack.getNumResults();
5467 if (packOrUnPack.hasPureTensorSemantics() && numResults != 1)
5468 return op->
emitError(
"expected 1 result, got ") << numResults;
5469 if (packOrUnPack.hasPureBufferSemantics() && numResults != 0)
5470 return op->
emitError(
"expected 0 results, got ") << numResults;
5474 if (hasZeros(mixedTiles))
5475 return op->
emitError(
"invalid zero tile factor");
5478 ShapedType unpackedType = (std::is_same<OpTy, PackOp>::value)
5479 ? packOrUnPack.getSourceType()
5480 : packOrUnPack.getDestType();
5481 size_t unpackedRank = unpackedType.getRank();
5485 return op->
emitError(
"invalid inner_dims_pos vector");
5487 return op->
emitError(
"invalid outer_dims_perm vector");
5488 if (!outerDimPerm.empty() && outerDimPerm.size() != unpackedRank)
5489 return op->
emitError(
"outer_dims_perm must be a permutation or empty");
5493 if (mixedTiles.size() > unpackedRank) {
5494 return op->
emitError(
"tiling factors must be less than or equal to the "
5495 "input rank for pack or output rank for unpack");
5497 if (mixedTiles.size() != innerDimsPos.size()) {
5499 "tiling factors must equal the number of dimensions to tile");
5502 ShapedType packedType = (std::is_same<OpTy, PackOp>::value)
5503 ? packOrUnPack.getDestType()
5504 : packOrUnPack.getSourceType();
5505 size_t packedRank = packedType.getRank();
5507 size_t expectedPackedRank = unpackedRank + mixedTiles.size();
5508 if (expectedPackedRank != packedRank) {
5510 "packed rank != (unpacked rank + num tiling factors), got ")
5511 << packedRank <<
" != " << expectedPackedRank;
5518 unpackedType.getShape(), packOrUnPack.getStaticTiles(),
5519 packOrUnPack.getInnerDimsPos(), packOrUnPack.getOuterDimsPerm());
5520 for (
auto it : llvm::enumerate(llvm::zip(
5521 packedType.getShape().take_back(mixedTiles.size()), mixedTiles))) {
5522 int64_t dimSize = std::get<0>(it.value());
5524 llvm::dyn_cast_if_present<Attribute>(std::get<1>(it.value()))) {
5525 IntegerAttr intAttr = dyn_cast_or_null<IntegerAttr>(attr);
5526 int64_t staticTileSize = intAttr.getValue().getSExtValue();
5527 if (dimSize != staticTileSize)
5529 "mismatch in inner tile sizes specified and shaped of "
5530 "tiled dimension in the packed type at index ")
5531 << it.index() <<
": got " << dimSize <<
" != " << staticTileSize;
5532 }
else if (!ShapedType::isDynamic(dimSize)) {
5533 return op->
emitError(
"mismatch in inner tile sizes specified at index ")
5534 << it.index() <<
": got static shape " << dimSize
5535 <<
" but dynamic tile size";
5540 auto elementType = unpackedType.getElementType();
5541 Type expectedType, actualType;
5542 if (packOrUnPack.hasPureTensorSemantics()) {
5543 expectedType = RankedTensorType::get(expectedPackedShape, elementType);
5544 actualType = RankedTensorType::get(packedType.getShape(), elementType);
5546 expectedType = MemRefType::get(expectedPackedShape, elementType);
5547 actualType = MemRefType::get(packedType.getShape(), elementType);
5550 << expectedType <<
" for the packed domain value, got "
5563struct PackOrUnPackTransposeResult {
5570template <
typename OpTy>
5571static PackOrUnPackTransposeResult
5575 static_assert(llvm::is_one_of<OpTy, PackOp, UnPackOp>::value,
5576 "applies to only pack or unpack operations");
5577 assert((!innerPermutation.empty() || !outerPermutation.empty()) &&
5578 "some permutation must be non-empty");
5579 PackOrUnPackTransposeResult metadata;
5580 metadata.innerDimsPos =
5582 metadata.innerTiles =
5584 int64_t numOuterDims = std::is_same<OpTy, PackOp>::value
5585 ? packOrUnPackOp.getSourceRank()
5586 : packOrUnPackOp.getDestRank();
5587 metadata.outerDimsPerm =
5588 packOrUnPackOp.getOuterDimsPerm().empty()
5589 ? llvm::to_vector(llvm::seq<int64_t>(0, numOuterDims))
5591 if (!innerPermutation.empty()) {
5592 assert(innerPermutation.size() == metadata.innerDimsPos.size() &&
5594 "invalid inner permutation");
5598 if (!outerPermutation.empty()) {
5599 assert(outerPermutation.size() == metadata.outerDimsPerm.size() &&
5601 "invalid outer permutation");
5612 if (!getResults().empty())
5613 setNameFn(getResult(),
"pack");
5623 Type sourceType, destType, resultType;
5640 SmallVector<int64_t> outerDimsPermVec;
5643 if (parser.parseInteger(value))
5645 outerDimsPermVec.push_back(value);
5655 SmallVector<int64_t> innerDimsPosVec;
5658 if (parser.parseInteger(value))
5660 innerDimsPosVec.push_back(value);
5672 for (
auto val : staticTilesAttr.
asArrayRef())
5673 staticTiles.push_back(val);
5690 bool isMemRef = llvm::isa<MemRefType>(sourceType);
5693 "pack/unpack requires '->' and destination type");
5697 resultType = destType;
5703 if (!paddingValue.empty() &&
5708 if (!dynamicTiles.empty() &&
5713 result.addAttribute(
"static_inner_tiles",
5715 result.addAttribute(
"inner_dims_pos", innerDimsPos);
5717 result.addAttribute(
"outer_dims_perm", outerDimsPerm);
5719 SmallVector<int32_t> segmentSizes = {
5720 1, 1,
static_cast<int32_t
>(paddingValue.size()),
5721 static_cast<int32_t
>(dynamicTiles.size())};
5722 result.addAttribute(
"operandSegmentSizes",
5726 result.addTypes(resultType);
5731void PackOp::print(OpAsmPrinter &p) {
5732 p <<
" " << getSource();
5734 if (getPaddingValue()) {
5735 p <<
" padding_value(" << getPaddingValue() <<
" : "
5736 << getPaddingValue().getType() <<
")";
5739 if (!getOuterDimsPerm().empty()) {
5740 p <<
" outer_dims_perm = [";
5741 llvm::interleaveComma(getOuterDimsPerm(), p);
5745 p <<
" inner_dims_pos = [";
5746 llvm::interleaveComma(getInnerDimsPos(), p);
5749 p <<
" inner_tiles = ";
5752 p <<
" into " << getDest();
5755 {
"static_inner_tiles",
"inner_dims_pos",
5756 "outer_dims_perm",
"operandSegmentSizes"});
5758 p <<
" : " << getSource().getType();
5759 p <<
" -> " << getDest().getType();
5762void PackOp::build(OpBuilder &builder, OperationState &state, Value source,
5763 Value dest, ArrayRef<int64_t> innerDimsPos,
5764 ArrayRef<OpFoldResult> innerTiles,
5765 std::optional<Value> paddingValue,
5766 ArrayRef<int64_t> outerDimsPerm) {
5767 assert(innerDimsPos.size() == innerTiles.size() &&
5768 "number of tile sizes specified must match the specified number of "
5769 "original dimensions to be tiled");
5770 SmallVector<int64_t> staticTileSizes;
5771 SmallVector<Value> dynamicTileSizes;
5773 build(builder, state, dest.
getType(), source, dest,
5774 paddingValue ? *paddingValue :
nullptr,
5775 outerDimsPerm.empty() ?
nullptr
5782PackOp::reifyResultShapes(OpBuilder &builder,
5791SmallVector<OpFoldResult> PackOp::getMixedTiles() {
5795SmallVector<int64_t> PackOp::getStaticTiles() {
5799ArrayRef<int64_t> PackOp::getAllOuterDims() {
5800 ShapedType inputType = getSourceType();
5801 int64_t inputRank = inputType.getRank();
5802 return getDestType().getShape().take_front(inputRank);
5805SmallVector<int64_t> PackOp::getTiledOuterDims() {
5806 auto innerDimsPos = getInnerDimsPos();
5807 SmallVector<int64_t> outerDims(getAllOuterDims());
5808 SmallVector<int64_t> res;
5811 SmallVector<int64_t> outerDimPermInv(getOuterDimsPerm());
5813 if (!outerDimPermInv.empty())
5817 for (
auto index : innerDimsPos)
5818 res.push_back(outerDims[index]);
5823bool PackOp::requirePaddingValue(ArrayRef<int64_t> inputShape,
5824 ArrayRef<int64_t> innerDimsPos,
5825 ArrayRef<int64_t> outputShape,
5826 ArrayRef<int64_t> outerDimsPerm,
5827 ArrayRef<OpFoldResult> innerTiles) {
5828 SmallVector<int64_t> outputTileSizes(
5829 outputShape.take_front(inputShape.size()));
5830 if (!outerDimsPerm.empty()) {
5831 assert(outerDimsPerm.size() == outputTileSizes.size() &&
5832 "expected output and outer_dims_perm to have same size");
5836 for (
auto [pos, tileSize] : llvm::zip_equal(innerDimsPos, innerTiles)) {
5837 if (ShapedType::isDynamic(inputShape[pos]))
5840 if (!constantTile) {
5841 if (ShapedType::isStatic(outputTileSizes[pos]) &&
5842 (inputShape[pos] % outputTileSizes[pos] != 0))
5845 assert(*constantTile != 0 &&
"static tile size can't be zero");
5846 if (inputShape[pos] % (*constantTile) != 0) {
5854bool PackOp::requirePaddingValueStrict(ArrayRef<int64_t> inputShape,
5855 ArrayRef<int64_t> innerDimsPos,
5856 ArrayRef<int64_t> outputShape,
5857 ArrayRef<int64_t> outerDimsPerm,
5858 ArrayRef<OpFoldResult> innerTiles) {
5859 SmallVector<int64_t> outputTileSizes(
5860 outputShape.take_front(inputShape.size()));
5861 if (!outerDimsPerm.empty()) {
5862 assert(outerDimsPerm.size() == outputTileSizes.size() &&
5863 "expected output and outer_dims_perm to have same size");
5867 for (
auto [pos, tileSize] : llvm::zip_equal(innerDimsPos, innerTiles)) {
5868 if (ShapedType::isDynamic(inputShape[pos]) ||
5869 ShapedType::isDynamic(outputTileSizes[pos]))
5874 assert(*constantTile != 0 &&
"static tile size can't be zero");
5875 if (inputShape[pos] % (*constantTile) != 0)
5881LogicalResult PackOp::verify() {
5888 auto paddingValue = getPaddingValue();
5891 return emitOpError(
"expected padding_value has ")
5892 << getSourceType().getElementType()
5893 <<
" but got: " << paddingValue.getType();
5896 if (!paddingValue &&
5897 requirePaddingValue(getSourceType().
getShape(), getInnerDimsPos(),
5898 getDestType().
getShape(), getOuterDimsPerm(),
5901 "invalid tile factor or output size provided. Only full tiles are "
5902 "supported when padding_value is not set");
5909static SmallVector<int64_t>
5912 for (
auto o : ofrs) {
5914 if (llvm::dyn_cast_if_present<Value>(o))
5915 result.push_back(ShapedType::kDynamic);
5927 for (
auto tiledDim : llvm::enumerate(llvm::to_vector(innerDimsPos))) {
5928 if (ShapedType::isDynamic(resultShape[tiledDim.value()]))
5930 if (ShapedType::isDynamic(innerTileSizes[tiledDim.index()])) {
5931 resultShape[tiledDim.value()] = ShapedType::kDynamic;
5934 resultShape[tiledDim.value()] = llvm::divideCeilSigned(
5935 resultShape[tiledDim.value()], innerTileSizes[tiledDim.index()]);
5939 if (!outerDimsPerm.empty())
5943 resultShape.append(innerTileSizes.begin(), innerTileSizes.end());
5947SmallVector<OpFoldResult> PackOp::getResultShape(
5948 OpBuilder &builder, Location loc, ArrayRef<OpFoldResult> sourceDims,
5949 ArrayRef<OpFoldResult> innerTileSizes, ArrayRef<int64_t> innerDimsPos,
5950 ArrayRef<int64_t> outerDimsPerm) {
5951 SmallVector<OpFoldResult> resultDims = llvm::to_vector(sourceDims);
5955 AffineExpr ceilDivExpr = s0.
ceilDiv(s1);
5956 for (
auto tiledDim : llvm::enumerate(llvm::to_vector(innerDimsPos))) {
5958 builder, loc, ceilDivExpr,
5959 {resultDims[tiledDim.value()], innerTileSizes[tiledDim.index()]});
5961 if (!outerDimsPerm.empty())
5963 resultDims.append(innerTileSizes.begin(), innerTileSizes.end());
5965 SmallVector<int64_t> resultTypeShape =
5968 innerDimsPos, outerDimsPerm);
5974 for (
unsigned i = 0; i < resultDims.size(); ++i) {
5975 if (ShapedType::isStatic(resultTypeShape[i]))
5984RankedTensorType PackOp::inferPackedTensorType(
5985 RankedTensorType sourceType, ArrayRef<int64_t> innerTileSizes,
5986 ArrayRef<int64_t> innerDimsPos, ArrayRef<int64_t> outerDimsPerm) {
5987 SmallVector<int64_t> resultShape = inferPackedShape(
5988 sourceType.getShape(), innerTileSizes, innerDimsPos, outerDimsPerm);
5989 return RankedTensorType::get(resultShape, sourceType.getElementType());
5992MemRefType PackOp::inferPackedMemRefType(MemRefType sourceType,
5993 ArrayRef<int64_t> innerTileSizes,
5994 ArrayRef<int64_t> innerDimsPos,
5995 ArrayRef<int64_t> outerDimsPerm) {
5996 SmallVector<int64_t> resultShape = inferPackedShape(
5997 sourceType.getShape(), innerTileSizes, innerDimsPos, outerDimsPerm);
5998 return MemRefType::get(resultShape, sourceType.getElementType());
6001Value PackOp::createDestinationTensor(OpBuilder &
b, Location loc, Value source,
6002 ArrayRef<OpFoldResult> innerTileSizes,
6003 ArrayRef<int64_t> innerDimsPos,
6004 ArrayRef<int64_t> outerDimsPerm) {
6005 AffineExpr dim0, dim1;
6007 auto ceilDiv = [&](OpFoldResult v1, OpFoldResult v2) -> OpFoldResult {
6012 SmallVector<OpFoldResult> mixedSizes;
6013 for (
auto [index, value] : llvm::enumerate(
6014 llvm::cast<RankedTensorType>(source.
getType()).getShape())) {
6015 if (ShapedType::isDynamic(value))
6016 mixedSizes.push_back(
6017 tensor::DimOp::create(
b, loc, source, index).getResult());
6019 mixedSizes.push_back(
b.getIndexAttr(value));
6021 for (
auto it : llvm::zip(innerDimsPos, innerTileSizes)) {
6022 int64_t dimPos = std::get<0>(it);
6023 OpFoldResult tileSize = std::get<1>(it);
6024 mixedSizes[dimPos] = ceilDiv(mixedSizes[dimPos], tileSize);
6026 if (!outerDimsPerm.empty())
6029 mixedSizes.append(innerTileSizes.begin(), innerTileSizes.end());
6030 auto elemType = llvm::cast<ShapedType>(source.
getType()).getElementType();
6031 return tensor::EmptyOp::create(
b, loc, mixedSizes, elemType);
6034PackOp PackOp::createTransposedClone(OpBuilder &
b, Location loc,
6035 ArrayRef<int64_t> innerPermutation,
6036 ArrayRef<int64_t> outerPermutation) {
6038 *
this, innerPermutation, outerPermutation);
6039 Value transposedDest =
6040 createDestinationTensor(
b, loc, getSource(), metadata.innerTiles,
6041 metadata.innerDimsPos, metadata.outerDimsPerm);
6042 return PackOp::create(
b, loc, getSource(), transposedDest,
6043 metadata.innerDimsPos, metadata.innerTiles,
6044 getPaddingValue(), metadata.outerDimsPerm);
6047template <
typename OpTy>
6052 if (op.hasPureTensorSemantics())
6055 for (
OpOperand &opOperand : op.getOperation()->getOpOperands()) {
6056 if (!llvm::isa<MemRefType>(opOperand.
get().
getType()))
6059 if (&opOperand == &op.getSourceMutable()) {
6063 }
else if (&opOperand == &op.getDestMutable()) {
6074void PackOp::getEffects(
6080void UnPackOp::getEffects(
6087template <
typename OpTy>
6089 static_assert(llvm::is_one_of<OpTy, PackOp, UnPackOp>::value,
6090 "applies to only pack or unpack operations");
6091 ShapedType packedType = (std::is_same<OpTy, PackOp>::value)
6093 : op.getSourceType();
6095 for (
auto [dimDest,
tile] : llvm::zip(
6096 packedType.getShape().take_back(mixedTiles.size()), mixedTiles)) {
6098 if (!constTileSize || ShapedType::isDynamic(dimDest))
6105 if (!hasPureTensorSemantics())
6107 if (getPaddingValue())
6122 if (packOp.getInnerDimsPos() != unPackOp.getInnerDimsPos())
6124 if (packOp.getOuterDimsPerm() == unPackOp.getOuterDimsPerm())
6136 auto packTiles = packOp.getMixedTiles();
6137 auto unPackTiles = unPackOp.getMixedTiles();
6138 if (packTiles.size() != unPackTiles.size())
6140 for (
size_t i = 0, e = packTiles.size(); i < e; i++) {
6149 auto srcType = op.getSourceType();
6150 auto innerDimsPos = op.getInnerDimsPos();
6151 auto innerTiles = op.getStaticInnerTiles();
6152 if (ShapedType::isDynamicShape(innerTiles))
6154 for (
auto [pos, tileSize] : llvm::zip_equal(innerDimsPos, innerTiles)) {
6155 if (srcType.isDynamicDim(pos) && tileSize != 1)
6158 return !PackOp::requirePaddingValue(
6159 srcType.getShape(), op.getInnerDimsPos(), op.getDestType().getShape(),
6160 op.getOuterDimsPerm(), op.getMixedTiles());
6167 bool changeNeeded =
false;
6168 srcShape.assign(packOp.getSourceType().getShape().begin(),
6169 packOp.getSourceType().getShape().end());
6170 destShape.assign(packOp.getDestType().getShape().begin(),
6171 packOp.getDestType().getShape().end());
6172 llvm::SmallSetVector<int64_t, 4> innerDims;
6173 innerDims.insert_range(packOp.getInnerDimsPos());
6175 if (!packOp.getOuterDimsPerm().empty())
6177 int srcRank = packOp.getSourceRank();
6178 for (
auto i : llvm::seq<int64_t>(0, srcRank)) {
6179 if (innerDims.contains(i))
6183 if (!inverseOuterDimsPerm.empty())
6184 destPos = inverseOuterDimsPerm[srcPos];
6185 if (ShapedType::isDynamic(srcShape[srcPos]) ==
6186 ShapedType::isDynamic(destShape[destPos])) {
6189 int64_t size = srcShape[srcPos];
6190 if (ShapedType::isDynamic(size))
6191 size = destShape[destPos];
6192 srcShape[srcPos] = size;
6193 destShape[destPos] = size;
6194 changeNeeded =
true;
6196 return changeNeeded;
6199LogicalResult PackOp::canonicalize(PackOp packOp,
PatternRewriter &rewriter) {
6203 if (!packOp.hasPureTensorSemantics())
6207 if (
auto unPackOp = packOp.getSource().getDefiningOp<UnPackOp>()) {
6208 if (unPackOp.getSourceType() == packOp.getDestType() &&
6209 !packOp.getPaddingValue() &&
6212 rewriter.
replaceOp(packOp, unPackOp.getSource());
6220 packOp.getPaddingValueMutable().clear();
6226 SmallVector<int64_t> srcShape, destShape;
6228 Location loc = packOp.getLoc();
6229 Value source = packOp.getSource();
6230 if (srcShape != packOp.getSourceType().getShape()) {
6231 auto newSrcType = packOp.getSourceType().clone(srcShape);
6233 tensor::CastOp::create(rewriter, loc, newSrcType, packOp.getSource());
6235 Value dest = packOp.getDest();
6236 ShapedType originalResultType = packOp.getDestType();
6237 bool needUpdateDestType = (destShape != originalResultType.getShape());
6238 if (needUpdateDestType) {
6239 auto newDestType = packOp.getDestType().clone(destShape);
6241 tensor::CastOp::create(rewriter, loc, newDestType, packOp.getDest());
6244 packOp.getSourceMutable().assign(source);
6245 packOp.getDestMutable().assign(dest);
6246 packOp.getResult().setType(cast<RankedTensorType>(dest.
getType()));
6249 if (needUpdateDestType) {
6251 auto castOp = tensor::CastOp::create(rewriter, loc, originalResultType,
6252 packOp.getResult());
6261template <
typename PackOrUnpackOp>
6263 static_assert(std::is_same<PackOrUnpackOp, PackOp>::value ||
6264 std::is_same<PackOrUnpackOp, UnPackOp>::value,
6265 "Function meant for pack/unpack");
6270 int64_t numPackedDims = innerDimsPos.size();
6271 auto orderedDims = llvm::to_vector<4>(llvm::seq<int64_t>(0, numPackedDims));
6272 if (orderedDims != innerDimsPos) {
6278 int64_t packedRank = packedTensorType.getRank();
6288 return llvm::all_of(
6289 llvm::seq<int64_t>(0, packedRank - numPackedDims),
6290 [&packedShape](
int64_t i) {
return packedShape[i] == 1; });
6293bool PackOp::isLikePad() {
6294 auto packedTensorType =
6295 llvm::cast<ShapedType>((*this)->getResultTypes().front());
6299::mlir::LogicalResult
6300PackOp::fold(FoldAdaptor adaptor,
6305 if (!hasPureTensorSemantics())
6307 std::optional<Attribute> paddingValue;
6308 if (
auto pad = adaptor.getPaddingValue())
6310 if (
OpFoldResult reshapedSource = reshapeConstantSource(
6311 llvm::dyn_cast_if_present<DenseElementsAttr>(adaptor.getSource()),
6312 cast<TensorType>(getDestType()), paddingValue)) {
6313 results.push_back(reshapedSource);
6341 if (!op.hasPureTensorSemantics())
6362 PackOp::create(rewriter, op.getLoc(), newOperands[0], newOperands[1],
6363 op.getInnerDimsPos(), newMixedTileSizes,
6364 op.getPaddingValue(), op.getOuterDimsPerm());
6365 newOp->setDiscardableAttrs(op->getDiscardableAttrDictionary());
6368 Value oldResult = op.getResult();
6369 Value newResult = newOp.getResult();
6372 ? tensor::CastOp::create(rewriter, op->getLoc(),
6373 oldResult.
getType(), newResult)
6386void UnPackOp::getAsmResultNames(
6388 if (!getResults().empty())
6389 setNameFn(getResult(),
"unpack");
6398 Type sourceType, destType, resultType;
6410 if (parser.parseInteger(value))
6412 outerDimsPermVec.push_back(value);
6422 SmallVector<int64_t> innerDimsPosVec;
6425 if (parser.parseInteger(value))
6427 innerDimsPosVec.push_back(value);
6439 for (
auto val : staticTilesAttr.
asArrayRef())
6440 staticTiles.push_back(val);
6457 bool isMemRef = llvm::isa<MemRefType>(sourceType);
6460 "pack/unpack requires '->' and destination type");
6464 resultType = destType;
6470 if (!dynamicTiles.empty() &&
6475 result.addAttribute(
"static_inner_tiles",
6477 result.addAttribute(
"inner_dims_pos", innerDimsPos);
6479 result.addAttribute(
"outer_dims_perm", outerDimsPerm);
6481 SmallVector<int32_t> segmentSizes = {
6482 1, 1, 0,
static_cast<int32_t
>(dynamicTiles.size())};
6483 result.addAttribute(
"operandSegmentSizes",
6487 result.addTypes(resultType);
6492void UnPackOp::print(OpAsmPrinter &p) {
6493 p <<
" " << getSource();
6495 if (!getOuterDimsPerm().empty()) {
6496 p <<
" outer_dims_perm = [";
6497 llvm::interleaveComma(getOuterDimsPerm(), p);
6501 p <<
" inner_dims_pos = [";
6502 llvm::interleaveComma(getInnerDimsPos(), p);
6505 p <<
" inner_tiles = ";
6508 p <<
" into " << getDest();
6511 {
"static_inner_tiles",
"inner_dims_pos",
6512 "outer_dims_perm",
"operandSegmentSizes"});
6514 p <<
" : " << getSource().getType();
6515 p <<
" -> " << getDest().getType();
6519UnPackOp::reifyResultShapes(OpBuilder &builder,
6528SmallVector<OpFoldResult> UnPackOp::getMixedTiles() {
6532SmallVector<int64_t> UnPackOp::getStaticTiles() {
6536ArrayRef<int64_t> UnPackOp::getAllOuterDims() {
6537 ShapedType destType = getDestType();
6538 int64_t destRank = destType.getRank();
6539 return getSourceType().getShape().take_front(destRank);
6542SmallVector<int64_t> UnPackOp::getTiledOuterDims() {
6543 auto innerDimsPos = getInnerDimsPos();
6544 SmallVector<int64_t> outerDims(getAllOuterDims());
6545 SmallVector<int64_t> res;
6548 SmallVector<int64_t> outerDimPermInv(getOuterDimsPerm());
6550 if (!outerDimPermInv.empty())
6554 for (
auto index : innerDimsPos)
6555 res.push_back(outerDims[index]);
6560LogicalResult UnPackOp::verify() {
6565 if (!hasPureTensorSemantics())
6574void UnPackOp::build(OpBuilder &builder, OperationState &state, Value source,
6575 Value dest, ArrayRef<int64_t> innerDimsPos,
6576 ArrayRef<OpFoldResult> innerTiles,
6577 ArrayRef<int64_t> outerDimsPerm) {
6578 assert(innerDimsPos.size() == innerTiles.size() &&
6579 "number of tile sizes specified must match the specified number of "
6580 "original dimensions to be tiled");
6581 SmallVector<int64_t> staticTileSizes;
6582 SmallVector<Value> dynamicTileSizes;
6584 build(builder, state, dest.
getType(), source, dest,
6585 outerDimsPerm.empty() ?
nullptr
6591Value UnPackOp::createDestinationTensor(OpBuilder &
b, Location loc,
6593 ArrayRef<OpFoldResult> innerTileSizes,
6594 ArrayRef<int64_t> innerDimsPos,
6595 ArrayRef<int64_t> outerDimsPerm) {
6596 AffineExpr sym0, sym1;
6598 auto dimMul = [&](OpFoldResult v1, OpFoldResult v2) -> OpFoldResult {
6602 SmallVector<OpFoldResult> mixedSizes;
6603 auto srcType = llvm::cast<RankedTensorType>(source.
getType());
6605 llvm::seq<unsigned>(0, srcType.getRank() - innerTileSizes.size())) {
6606 if (srcType.isDynamicDim(i))
6607 mixedSizes.push_back(
6608 tensor::DimOp::create(
b, loc, source, i).getResult());
6610 mixedSizes.push_back(
b.getIndexAttr(srcType.getDimSize(i)));
6612 if (!outerDimsPerm.empty()) {
6617 for (
auto [dimPos, tileSize] : llvm::zip_equal(innerDimsPos, innerTileSizes))
6618 mixedSizes[dimPos] = dimMul(mixedSizes[dimPos], tileSize);
6620 auto elemType = srcType.getElementType();
6621 return tensor::EmptyOp::create(
b, loc, mixedSizes, elemType);
6624UnPackOp UnPackOp::createTransposedClone(OpBuilder &
b, Location loc,
6625 Value transposedSource,
6626 ArrayRef<int64_t> innerPermutation,
6627 ArrayRef<int64_t> outerPermutation) {
6629 *
this, innerPermutation, outerPermutation);
6630 return UnPackOp::create(
b, loc, transposedSource, getDest(),
6631 metadata.innerDimsPos, metadata.innerTiles,
6632 metadata.outerDimsPerm);
6639 bool changeNeeded =
false;
6640 srcShape.assign(op.getSourceType().getShape().begin(),
6641 op.getSourceType().getShape().end());
6642 destShape.assign(op.getDestType().getShape().begin(),
6643 op.getDestType().getShape().end());
6644 llvm::SmallSetVector<int64_t, 4> innerDims;
6645 innerDims.insert_range(op.getInnerDimsPos());
6647 if (!op.getOuterDimsPerm().empty())
6649 int destRank = op.getDestRank();
6650 for (
auto i : llvm::seq<int64_t>(0, destRank)) {
6651 if (innerDims.contains(i))
6655 if (!inverseOuterDimsPerm.empty())
6656 srcPos = inverseOuterDimsPerm[destPos];
6657 if (ShapedType::isDynamic(srcShape[srcPos]) ==
6658 ShapedType::isDynamic(destShape[destPos])) {
6661 int64_t size = srcShape[srcPos];
6662 if (ShapedType::isDynamic(size))
6663 size = destShape[destPos];
6664 srcShape[srcPos] = size;
6665 destShape[destPos] = size;
6666 changeNeeded =
true;
6668 return changeNeeded;
6671LogicalResult UnPackOp::canonicalize(UnPackOp unPackOp,
6676 if (!unPackOp.hasPureTensorSemantics())
6680 if (PackOp packOp = unPackOp.getSource().getDefiningOp<PackOp>()) {
6681 if (packOp.getSourceType() != unPackOp.getDestType())
6683 if (packOp.getPaddingValue() ||
6687 rewriter.
replaceOp(unPackOp, packOp.getSource());
6691 if (
auto dstStyleOp =
6692 unPackOp.getDest().getDefiningOp<DestinationStyleOpInterface>()) {
6693 auto destValue = cast<OpResult>(unPackOp.getDest());
6694 Value newDest = dstStyleOp.getDpsInits()[destValue.getResultNumber()];
6696 [&]() { unPackOp.setDpsInitOperand(0, newDest); });
6700 if (unPackOp->hasOneUse()) {
6701 auto extractSliceUser =
6702 dyn_cast<tensor::ExtractSliceOp>(*unPackOp->getUsers().begin());
6703 if (extractSliceUser && unPackOp.canFoldSliceOp(extractSliceUser)) {
6704 OpBuilder::InsertionGuard g(rewriter);
6706 auto newDest = tensor::ExtractSliceOp::create(
6707 rewriter, unPackOp->getLoc(), unPackOp.getDest(),
6708 extractSliceUser.getMixedOffsets(), extractSliceUser.getMixedSizes(),
6709 extractSliceUser.getMixedStrides());
6711 unPackOp.setDpsInitOperand(0, newDest);
6712 unPackOp.getResult().setType(newDest.
getType());
6714 rewriter.
replaceOp(extractSliceUser, unPackOp);
6720 SmallVector<int64_t> srcShape, destShape;
6722 Location loc = unPackOp.getLoc();
6723 Value source = unPackOp.getSource();
6724 if (srcShape != unPackOp.getSourceType().getShape()) {
6725 auto newSrcType = unPackOp.getSourceType().clone(srcShape);
6726 source = tensor::CastOp::create(rewriter, loc, newSrcType,
6727 unPackOp.getSource());
6729 Value dest = unPackOp.getDest();
6730 if (destShape != unPackOp.getDestType().getShape()) {
6731 auto newDestType = unPackOp.getDestType().clone(destShape);
6732 dest = tensor::CastOp::create(rewriter, loc, newDestType,
6733 unPackOp.getDest());
6735 UnPackOp newOp = UnPackOp::create(
6736 rewriter, loc, source, dest, unPackOp.getInnerDimsPos(),
6737 unPackOp.getMixedTiles(), unPackOp.getOuterDimsPerm());
6739 unPackOp, unPackOp.getResult().
getType(), newOp.getResult());
6746bool UnPackOp::canFoldSliceOp(tensor::ExtractSliceOp sliceOp) {
6748 if (sliceOp.getResultType().getRank() != this->getDestType().getRank())
6753 RankedTensorType unpackedTypeAfterFold = sliceOp.getResultType();
6754 SmallVector<int64_t> outerShapeWithoutTranspose =
6756 SmallVector<bool> areOuterDimsTiled(outerShapeWithoutTranspose.size(),
false);
6757 for (
auto [pos, tileSize] :
6758 llvm::zip_equal(this->getInnerDimsPos(), this->getStaticInnerTiles())) {
6759 areOuterDimsTiled[pos] =
true;
6760 if (unpackedTypeAfterFold.isDynamicDim(pos))
6762 if (ShapedType::isDynamic(outerShapeWithoutTranspose[pos]))
6764 if (ShapedType::isDynamic(tileSize))
6766 int64_t paddingSize = outerShapeWithoutTranspose[pos] * tileSize -
6767 unpackedTypeAfterFold.getDimSize(pos);
6768 if (paddingSize >= tileSize)
6772 for (int64_t pos = 0, e = outerShapeWithoutTranspose.size(); pos < e; ++pos) {
6773 if (areOuterDimsTiled[pos])
6775 int64_t dim = outerShapeWithoutTranspose[pos];
6776 if (ShapedType::isDynamic(dim))
6778 if (dim != unpackedTypeAfterFold.getDimSize(pos))
6784bool UnPackOp::isLikeUnPad() {
6785 ShapedType packedTensorType = getSourceType();
6789::mlir::LogicalResult
6790UnPackOp::fold(FoldAdaptor adaptor,
6791 ::llvm::SmallVectorImpl<OpFoldResult> &results) {
6795 if (!hasPureTensorSemantics())
6798 if (OpFoldResult reshapedSource = reshapeConstantSource(
6799 llvm::dyn_cast_if_present<DenseElementsAttr>(adaptor.getSource()),
6800 cast<TensorType>(getResult().
getType()))) {
6801 results.push_back(reshapedSource);
6829 if (!op.hasPureTensorSemantics())
6838 Value sourceTensor = newOperands[0];
6842 rewriter, sourceTensor.
getType(), op.getMixedTiles());
6848 UnPackOp newOp = UnPackOp::create(rewriter, op.getLoc(), sourceTensor,
6849 newOperands[1], op.getInnerDimsPos(),
6850 newMixedTileSizes, op.getOuterDimsPerm());
6851 newOp->setDiscardableAttrs(op->getDiscardableAttrDictionary());
6854 Value oldResult = op.getResult();
6855 Value newResult = newOp.getResult();
6858 ? tensor::CastOp::create(rewriter, op->getLoc(),
6859 oldResult.
getType(), newResult)
6873 utils::IteratorType::reduction, utils::IteratorType::parallel,
6874 utils::IteratorType::parallel, utils::IteratorType::reduction};
6877SmallVector<AffineMap>
6878BatchReduceMatmulOp::getDefaultIndexingMaps(MLIRContext *context) {
6879 AffineExpr d0, d1, d2, d3;
6880 SmallVector<AffineMap> indexingMaps;
6882 indexingMaps.push_back(
AffineMap::get(4, 0, {d0, d1, d3}, context));
6883 indexingMaps.push_back(
AffineMap::get(4, 0, {d0, d3, d2}, context));
6885 return indexingMaps;
6888bool BatchReduceMatmulOp::isDefaultIndexingMaps(Attribute attr) {
6889 ArrayAttr maps = dyn_cast<ArrayAttr>(attr);
6892 if (maps.size() != 3)
6897 return (*positions)[0] == SmallVector<int64_t>{0, 1, 3} &&
6898 (*positions)[1] == SmallVector<int64_t>{0, 3, 2} &&
6899 (*positions)[2] == SmallVector<int64_t>{1, 2};
6901unsigned BatchReduceMatmulOp::getNumRegionArgs() {
return 3; }
6903std::string BatchReduceMatmulOp::getLibraryCallName() {
6909bool BatchReduceMatmulOp::hasUserDefinedMaps() {
6910 SmallVector<AffineMap, 3> defaultMaps =
6912 SmallVector<AffineMap, 3> explicitMaps = getIndexingMapsArray();
6913 return defaultMaps != explicitMaps;
6923bool BatchReduceMatmulOp::isValidLhsRhsBroadcastMap(AffineMap bcastMap,
6926 "Expected less than 3 result dim expr.");
6927 bool isValid =
false;
6928 enum Indices { batchPos, mPos, nPos, kPos };
6930 AffineExpr expr = bcastMap.
getResult(0);
6933 AffineExpr expr0 = bcastMap.
getResult(0);
6934 AffineExpr expr1 = bcastMap.
getResult(1);
6939 : ((expr0.isFunctionOfDim(batchPos) &&
6940 expr1.isFunctionOfDim(kPos)) ||
6941 (expr0.isFunctionOfDim(kPos) && expr1.isFunctionOfDim(nPos)));
6946void BatchReduceMatmulOp::regionBuilder(
6947 ImplicitLocOpBuilder &
b,
Block &block, ArrayRef<NamedAttribute> attrs,
6950 emitError() <<
"BatchReduceMatmulOp regionBuilder expects 3 args, got "
6955 "BatchReduceMatmulOp regionBuilder expects 3 args");
6956 RegionBuilderHelper helper(
b, block);
6957 SmallVector<Value> yields;
6961 helper.buildTypeFn(TypeFn::cast_signed, toType, block.
getArgument(0));
6963 helper.buildTypeFn(TypeFn::cast_signed, toType, block.
getArgument(1));
6965 helper.buildBinaryFn(BinaryFn::mul, castValA, castValB,
emitError);
6966 if (!castValA || !castValB || !mulVal)
6969 helper.buildBinaryFn(BinaryFn::add, block.
getArgument(2), mulVal);
6972 yields.push_back(addVal);
6973 helper.yieldOutputs(yields);
6976ParseResult BatchReduceMatmulOp::parse(OpAsmParser &parser,
6977 OperationState &
result) {
6978 SmallVector<Attribute, 3> indexingMapsAttr;
6989 if (!isa<AffineMapAttr>(mapAttr)) {
6991 "expected affine map attribute");
6993 indexingMapsAttr.push_back(mapAttr);
7003 if (indexingMapsAttr.empty()) {
7004 indexingMapsAttr = llvm::map_to_vector(
7005 BatchReduceMatmulOp::getDefaultIndexingMaps(parser.
getContext()),
7006 [](AffineMap map) -> Attribute { return AffineMapAttr::get(map); });
7008 result.addAttribute(
"indexing_maps",
7010 return ::parseNamedStructuredOp(parser,
result,
7011 BatchReduceMatmulOp::getNumRegionArgs(),
7012 BatchReduceMatmulOp::getRegionBuilder());
7015void BatchReduceMatmulOp::print(OpAsmPrinter &p) {
7016 SmallVector<Attribute, 3> indexingMaps = llvm::map_to_vector(
7017 BatchReduceMatmulOp::getDefaultIndexingMaps(
getContext()),
7018 [](AffineMap map) -> Attribute {
return AffineMapAttr::get(map); });
7020 if (!llvm::equal(getIndexingMaps(), indexingMaps)) {
7021 p <<
" indexing_maps = [";
7022 llvm::interleaveComma(getIndexingMaps(), p,
7027 SmallVector<StringRef, 3> elidedAttrs = {
7028 "operandSegmentSizes",
"linalg.memoized_indexing_maps",
"indexing_maps"};
7034LogicalResult BatchReduceMatmulOp::verify() {
7037 if (!hasUserDefinedMaps())
7040 for (
unsigned opIndex = 0; opIndex < 3; opIndex++) {
7046LogicalResult BatchReduceMatmulOp::fold(FoldAdaptor,
7047 SmallVectorImpl<OpFoldResult> &) {
7050void BatchReduceMatmulOp::getEffects(
7051 SmallVectorImpl<SideEffects::EffectInstance<MemoryEffects::Effect>>
7053 if (hasPureTensorSemantics())
7066SmallVector<utils::IteratorType> ScaledContractOp::getIteratorTypesArray() {
7067 AffineMap outAffineMap = getIndexingMapsArray().pop_back_val();
7069 SmallVector<bool> dimsInOutput(outAffineMap.
getNumDims(),
false);
7071 auto dimExpr = dyn_cast<AffineDimExpr>(
result);
7072 assert(dimExpr &&
"affine_map is a projected permutation");
7073 dimsInOutput[dimExpr.getPosition()] =
true;
7076 SmallVector<utils::IteratorType> iteratorTypes;
7077 for (
auto dimOccursInOutput : dimsInOutput)
7078 iteratorTypes.push_back(dimOccursInOutput ? utils::IteratorType::parallel
7079 : utils::IteratorType::reduction);
7081 return iteratorTypes;
7084unsigned ScaledContractOp::getNumRegionArgs() {
return 5; }
7087void ScaledContractOp::regionBuilder(
7088 ImplicitLocOpBuilder &
b,
Block &block, ArrayRef<NamedAttribute> attrs,
7091 emitError() <<
"ScaledContractOp regionBuilder expects 5 args, got "
7096 "ScaledContractOp regionBuilder expects 5 args");
7097 RegionBuilderHelper helper(
b, block);
7099 TypeFn castSignedness = TypeFn::cast_signed;
7100 auto castIter = llvm::find_if(attrs, [&](
const NamedAttribute &attr) {
7101 return attr.
getName() ==
"cast";
7103 if (castIter != attrs.end()) {
7104 if (
auto attr = llvm::dyn_cast<TypeFnAttr>(castIter->getValue()))
7114 auto buildScaledValue = [&](Value data, Value scale) -> Value {
7115 auto dataFloatTy = dyn_cast<FloatType>(data.
getType());
7116 auto outFloatTy = dyn_cast<FloatType>(outType);
7117 if (dataFloatTy && dyn_cast<FloatType>(scale.getType()) && outFloatTy) {
7118 unsigned dataWidth = dataFloatTy.getWidth();
7119 unsigned outWidth = outFloatTy.getWidth();
7120 if (dataWidth < outWidth)
7121 return arith::ScalingExtFOp::create(
b, outType, data, scale,
7124 Value dataAtOutType = helper.buildTypeFn(castSignedness, outType, data);
7125 Value scaleAtOutType = helper.buildTypeFn(castSignedness, outType, scale);
7126 return helper.buildBinaryFn(BinaryFn::mul, dataAtOutType, scaleAtOutType,
7138 Value productAtOutType =
7139 helper.buildBinaryFn(BinaryFn::mul, scaledLhs, scaledRhs,
emitError);
7140 if (!productAtOutType)
7146 helper.yieldOutputs({
result});
7149ParseResult ScaledContractOp::parse(OpAsmParser &parser,
7150 OperationState &
result) {
7152 if (
failed(indexingMapsAttr) || *indexingMapsAttr ==
nullptr)
7154 "expected 'indexing_maps' attribute");
7155 result.addAttribute(
"indexing_maps", *indexingMapsAttr);
7161void ScaledContractOp::print(OpAsmPrinter &p) {
7162 p <<
" indexing_maps = " << llvm::interleaved_array(getIndexingMaps());
7164 p, getOperation(), getInputs(), getOutputs(),
7165 {
"indexing_maps",
"operandSegmentSizes"});
7168LogicalResult ScaledContractOp::verify() {
7169 int iterationSpaceDims = -1;
7174 SmallVector<size_t> inOccurrences;
7175 SmallVector<size_t> outOccurrences;
7178 SmallVector<AffineMap, 5> maps = getIndexingMapsArray();
7180 return emitOpError(
"expected 5 indexing maps and operands");
7182 SmallVector<Type, 5> types = llvm::to_vector(getOperandTypes());
7184 auto outputFloatType = dyn_cast<FloatType>(outputElementType);
7185 if (!outputFloatType)
7186 return emitOpError(
"expected output element type to be floating-point");
7188 for (Type inputType : ArrayRef<Type>(types).take_front(4)) {
7192 "expected input element types to be integer or floating-point");
7194 return emitOpError(
"expected input element type bitwidth to be no "
7195 "greater than output element type bitwidth");
7198 for (
auto &&[affineMap, operandType, isInput] :
7199 llvm::zip(SmallVector<AffineMap>{maps[0], maps[2], maps[4]},
7200 SmallVector<Type>{types[0], types[2], types[4]},
7201 SmallVector<bool>{
true,
true,
false})) {
7203 affineMap, operandType, isInput, iterationSpaceDims, inOccurrences,
7204 outOccurrences, [&]() {
return emitError(); })))
7209 inOccurrences, outOccurrences,
7214 auto checkScaleAffineMapAndType = [&](AffineMap affineMap,
7215 Type operandType) -> LogicalResult {
7220 return emitError(
"scale affine_map must not contain symbols");
7223 "scale affine_map must not have more results than inputs");
7225 SmallVector<bool, 8> seen(affineMap.
getNumInputs(),
false);
7229 AffineDimExpr dim =
nullptr;
7230 if (isa<AffineDimExpr>(expr)) {
7232 dim = dyn_cast<AffineDimExpr>(expr);
7233 }
else if (
auto binExpr = dyn_cast<AffineBinaryOpExpr>(expr)) {
7240 "only block scale with floordiv is supported for now");
7241 auto scaleDim = dyn_cast<AffineDimExpr>(binExpr.getLHS());
7243 return emitError(
"block scale LHS must be dim");
7244 auto scaleFactor = dyn_cast<AffineConstantExpr>(binExpr.getRHS());
7246 return emitError(
"block scale RHS must be constant");
7247 if (scaleFactor.getValue() <= 0)
7248 return emitError(
"block scale factor must be positive");
7251 return emitError(
"unsupported scaling variant");
7255 return emitError(
"invalid scale affine_map result expression");
7258 "scale affine_map must not have duplicate result dimensions");
7263 if (
auto shapedType = dyn_cast<ShapedType>(operandType)) {
7266 "scale ranks of shaped operand and results of corresponding "
7267 "affine_map differ");
7270 "scale affine_map specifies shaped access while operand has "
7278 for (
auto &&[affineMap, operandType] :
7279 llvm::zip(SmallVector<AffineMap>{maps[1], maps[3]},
7280 SmallVector<Type>{types[1], types[3]})) {
7281 if (
failed(checkScaleAffineMapAndType(affineMap, operandType)))
7286 for (
auto &&[inputMap, inputType, scaleMap, scaleType] :
7287 llvm::zip(SmallVector<AffineMap>{maps[0], maps[2]},
7288 SmallVector<Type>{types[0], types[2]},
7289 SmallVector<AffineMap>{maps[1], maps[3]},
7290 SmallVector<Type>{types[1], types[3]})) {
7291 if (inputMap.getNumResults() < scaleMap.getNumResults())
7292 return emitError(
"scale must have at most the same rank as input");
7293 if (scaleMap.getNumResults() == 0)
7296 auto inputShape = dyn_cast<ShapedType>(inputType).getShape();
7297 auto scaleShape = dyn_cast<ShapedType>(scaleType).getShape();
7304 for (
auto [scaleIdx, scaleExpr] : llvm::enumerate(scaleMap.getResults())) {
7305 AffineDimExpr scaleDimExpr =
nullptr;
7306 std::optional<int64_t> scaleFactor;
7307 if (
auto dimExpr = dyn_cast<AffineDimExpr>(scaleExpr)) {
7309 scaleDimExpr = dimExpr;
7310 }
else if (
auto scaleBinExpr = dyn_cast<AffineBinaryOpExpr>(scaleExpr)) {
7313 "only floordiv is supported for now");
7314 auto scaleDim = dyn_cast<AffineDimExpr>(scaleBinExpr.getLHS());
7315 assert(scaleDim &&
"block scale LHS is a dim expression");
7316 scaleDimExpr = scaleDim;
7318 dyn_cast<AffineConstantExpr>(scaleBinExpr.getRHS()).getValue();
7320 llvm_unreachable(
"unknown scale expression");
7322 assert(scaleDimExpr &&
"failed to find scale dim expression");
7324 std::optional<unsigned> inputIdx =
7325 inputMap.getResultPosition(scaleDimExpr);
7328 "scale map must contain corresponding input dimensions only");
7332 if (scaleFactor && inputShape[*inputIdx] != ShapedType::kDynamic &&
7333 scaleShape[scaleIdx] != ShapedType::kDynamic &&
7334 llvm::divideCeilSigned(inputShape[*inputIdx], *scaleFactor) !=
7335 static_cast<int64_t
>(scaleShape[scaleIdx])) {
7336 return emitError() <<
"Invalid scale shape at dim " << *inputIdx
7338 << llvm::divideCeilSigned(inputShape[*inputIdx],
7340 <<
" but got " << scaleShape[scaleIdx];
7348LogicalResult ScaledContractOp::fold(FoldAdaptor,
7349 SmallVectorImpl<OpFoldResult> &) {
7353void ScaledContractOp::getEffects(
7354 SmallVectorImpl<SideEffects::EffectInstance<MemoryEffects::Effect>>
7356 if (hasPureTensorSemantics())
7372void LinalgDialect::getCanonicalizationPatterns(
7381 return arith::ConstantOp::materialize(builder, value, type, loc);
getNumOperands() - 1))) return failure()
Given a list of lists of parsed operands, populates uniqueOperands with unique operands.
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 Type getElementType(Type type, ArrayRef< int32_t > indices, function_ref< InFlightDiagnostic(StringRef)> emitErrorFn)
Walks the given type hierarchy with the given indices, potentially down to component granularity,...
static 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.
unsigned getPosition() const
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 getNumSymbols() const
unsigned getNumDims() const
ArrayRef< AffineExpr > getResults() const
unsigned getNumResults() const
unsigned getNumInputs() 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 parseCustomAttributeWithFallback(Attribute &result, Type type, function_ref< ParseResult(Attribute &result, Type type)> parseAttribute)=0
Parse a custom attribute with the provided callback, unless the next token is #, in which case the ge...
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.
void printStrippedAttrOrType(AttrOrType attrOrType)
Print the provided attribute in the context of an operation custom printer/parser: this will invoke d...
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()
iterator_range< iterator > without_terminator()
Return an iterator range over the operation within this block excluding the terminator operation at t...
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.
void walkInherentAttrs(Operation *op, InherentAttrVisitor visitor) const
Visit the inherent attributes stored in the properties of op.
Operation is the basic unit of execution within MLIR.
Value getOperand(unsigned idx)
result_iterator result_begin()
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.
DictionaryAttr getDiscardableAttrDictionary()
Return all of the discardable attributes on this operation as a DictionaryAttr.
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...
bool isIntOrFloat() const
Return true if this is an integer (of any signedness) or a float type.
unsigned getIntOrFloatBitWidth() const
Return the bit width of an integer or a float type, assert failure on other types.
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 LogicalResult verifyContractionDims(size_t iterationSpaceDims, ArrayRef< size_t > inOccurrences, ArrayRef< size_t > outOccurrences, function_ref< InFlightDiagnostic()> emitError)
Validates the contracting dimension constraints given the per-dim occurrence counts.
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 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...
static LogicalResult checkContractionAffineMapAndType(AffineMap affineMap, Type operandType, bool isInput, int &iterationSpaceDims, SmallVector< size_t > &inOccurrences, SmallVector< size_t > &outOccurrences, function_ref< InFlightDiagnostic()> emitError)
Validate contraction operands indexing maps and shapes.
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.
ArityGroupAndKind getArityGroupAndKind(ElementwiseKind kind)
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.
@ FloorDiv
RHS of floordiv is always a constant or a symbolic expression.
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={})
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.
Elementwise Arity and Kind groups.
ElementwiseArityGroup arityGroup
union mlir::linalg::ArityGroupAndKind::Kind kind
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