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 {
2052static std::optional<BroadcastReduceKind>
2053matchBroadcastReduceBody(ReduceOp reduceOp) {
2054 if (reduceOp.getNumDpsInputs() != 1 || reduceOp.getNumDpsInits() != 1 ||
2055 !reduceOp.getBody())
2056 return std::nullopt;
2063 Block &block = *reduceOp.getBody();
2066 return std::nullopt;
2070 Operation *combineOp = yieldOp.getOperand(0).getDefiningOp();
2072 return std::nullopt;
2080 return std::nullopt;
2084 .Case<arith::MaxSIOp>(
2085 [](arith::MaxSIOp) {
return BroadcastReduceKind::MaxSI; })
2086 .Case<arith::MaxUIOp>(
2087 [](arith::MaxUIOp) {
return BroadcastReduceKind::MaxUI; })
2088 .Case<arith::MinSIOp>(
2089 [](arith::MinSIOp) {
return BroadcastReduceKind::MinSI; })
2090 .Case<arith::MinUIOp>(
2091 [](arith::MinUIOp) {
return BroadcastReduceKind::MinUI; })
2092 .Case<arith::AndIOp>(
2093 [](arith::AndIOp) {
return BroadcastReduceKind::AndI; })
2094 .Case<arith::OrIOp>([](arith::OrIOp) {
return BroadcastReduceKind::OrI; })
2095 .Default([](Operation *) -> std::optional<BroadcastReduceKind> {
2096 return std::nullopt;
2101static bool hasBroadcastReduceIdentity(Value init, BroadcastReduceKind kind) {
2106 auto integerAttr = dyn_cast<IntegerAttr>(*initAttr);
2110 const APInt &value = integerAttr.getValue();
2112 case BroadcastReduceKind::MaxSI:
2113 return value.isMinSignedValue();
2114 case BroadcastReduceKind::MaxUI:
2115 return value.isZero();
2116 case BroadcastReduceKind::MinSI:
2117 return value.isMaxSignedValue();
2118 case BroadcastReduceKind::MinUI:
2119 case BroadcastReduceKind::AndI:
2120 return value.isAllOnes();
2121 case BroadcastReduceKind::OrI:
2122 return value.isZero();
2124 llvm_unreachable(
"unknown broadcast reduction kind");
2135struct FoldReduceBroadcast :
public OpRewritePattern<linalg::ReduceOp> {
2136 using OpRewritePattern<linalg::ReduceOp>::OpRewritePattern;
2138 LogicalResult matchAndRewrite(linalg::ReduceOp reduceOp,
2139 PatternRewriter &rewriter)
const override {
2140 if (reduceOp.getNumResults() != 1 || !reduceOp.hasPureTensorSemantics())
2143 assert(reduceOp.getInputs().size() == 1 &&
2144 "expected one input for a single-result tensor reduce");
2147 reduceOp.getInputs().front().getDefiningOp<linalg::BroadcastOp>();
2148 if (!broadcastOp || !broadcastOp.hasPureTensorSemantics())
2151 auto sourceType = cast<RankedTensorType>(broadcastOp.getInput().getType());
2152 auto broadcastType =
2153 cast<RankedTensorType>(broadcastOp.getResult().front().getType());
2154 auto resultType = cast<RankedTensorType>(reduceOp.getResult(0).getType());
2155 if (!sourceType.hasStaticShape() || !broadcastType.hasStaticShape() ||
2156 !resultType.hasStaticShape() || sourceType != resultType)
2159 ArrayRef<int64_t> broadcastDims = broadcastOp.getDimensions();
2160 ArrayRef<int64_t> reduceDims = reduceOp.getDimensions();
2161 if (broadcastDims != reduceDims)
2166 for (int64_t dimension : broadcastDims)
2167 if (broadcastType.getDimSize(dimension) == 0)
2170 std::optional<BroadcastReduceKind> kind =
2171 matchBroadcastReduceBody(reduceOp);
2173 !hasBroadcastReduceIdentity(reduceOp.getInits().front(), *kind))
2176 rewriter.
replaceOp(reduceOp, broadcastOp.getInput());
2183void ReduceOp::getCanonicalizationPatterns(RewritePatternSet &results,
2184 MLIRContext *context) {
2185 results.
add<FoldReduceBroadcast>(context);
2198 linalg::YieldOp::create(
b, loc, args[0]);
2202void TransposeOp::build(::mlir::OpBuilder &builder,
2203 ::mlir::OperationState &
result, Value input, Value init,
2205 ArrayRef<NamedAttribute> attributes) {
2206 result.addOperands(input);
2207 result.addOperands(init);
2208 result.addAttribute(getPermutationAttrName(
result.name), permutation);
2209 result.addAttributes(attributes);
2212 Type initType = init.
getType();
2213 if (llvm::isa<RankedTensorType>(initType))
2214 result.addTypes(initType);
2220void TransposeOp::build(::mlir::OpBuilder &builder,
2221 ::mlir::OperationState &
result, Value input, Value init,
2222 ArrayRef<int64_t> permutation,
2223 ArrayRef<NamedAttribute> attributes) {
2228ParseResult TransposeOp::parse(OpAsmParser &parser, OperationState &
result) {
2230 parser,
result, [&](OpAsmParser &parser, NamedAttrList &attributes) {
2242void TransposeOp::getAsmResultNames(
2244 if (!getResults().empty())
2245 setNameFn(getResults().front(),
"transposed");
2248void TransposeOp::print(OpAsmPrinter &p) {
2252 {getPermutationAttrName()});
2255LogicalResult TransposeOp::verify() {
2256 ArrayRef<int64_t> permutationRef = getPermutation();
2259 return emitOpError(
"permutation is not valid");
2261 auto inputType = getInput().getType();
2262 auto initType = getInit().getType();
2264 int64_t rank = inputType.getRank();
2270 if (rank !=
static_cast<int64_t
>(permutationRef.size()))
2271 return emitOpError() <<
"size of permutation " << permutationRef.size()
2272 <<
" does not match the argument rank " << rank;
2274 auto inputDims = inputType.getShape();
2275 auto initDims = initType.getShape();
2277 for (int64_t i = 0; i < rank; ++i) {
2278 int64_t inputDim = inputDims[permutationRef[i]];
2279 int64_t initDim = initDims[i];
2281 if (inputDim != initDim) {
2282 return emitOpError() <<
"dim(result, " << i <<
") = " << initDim
2283 <<
" doesn't match dim(input, permutation[" << i
2284 <<
"]) = " << inputDim;
2291SmallVector<utils::IteratorType> TransposeOp::getIteratorTypesArray() {
2292 int64_t rank = getInit().getType().getRank();
2293 return SmallVector<utils::IteratorType>(rank, utils::IteratorType::parallel);
2296ArrayAttr TransposeOp::getIndexingMaps() {
2298 int64_t rank = getInit().getType().getRank();
2301 llvm::to_vector_of<unsigned>(getPermutation()),
getContext())),
2305void TransposeOp::getEffects(
2306 SmallVectorImpl<SideEffects::EffectInstance<MemoryEffects::Effect>>
2315LogicalResult TransposeOp::fold(FoldAdaptor adaptor,
2316 SmallVectorImpl<OpFoldResult> &
result) {
2318 if (!isa<TensorType>(getInput().
getType()))
2322 if (getPermutation().empty()) {
2323 result.push_back(getInput());
2328 result.push_back(getInput());
2341 auto defTransposeOp = transposeOp.getInput().getDefiningOp<TransposeOp>();
2342 if (!defTransposeOp)
2347 foldedPerms.reserve(perms.size());
2349 foldedPerms.push_back(defPerms[perm]);
2352 transposeOp, defTransposeOp.getInput(), transposeOp.getInit(),
2365 if (!transposeOp.hasPureTensorSemantics())
2370 if (!splatValue.has_value())
2374 cast<RankedTensorType>(transposeOp.getResult()[0].getType());
2391 Value input = transposeOp.getInput();
2392 BroadcastOp broadcastOp = input.
getDefiningOp<BroadcastOp>();
2403 unsigned dimensionSize = dimensions.size();
2404 for (
unsigned i = 0; i < dimensionSize; ++i)
2405 resultDimensions.push_back(invertPerm[dimensions[i]]);
2408 Value broadcastInput = broadcastOp.getInput();
2409 Location loc = transposeOp.getLoc();
2412 auto broadcastInputTy =
2413 mlir::cast<RankedTensorType>(broadcastInput.
getType());
2414 unsigned inputRank = broadcastInputTy.getRank();
2415 for (
unsigned i = 0; i < inputRank; ++i) {
2416 if (broadcastInputTy.isDynamicDim(i)) {
2417 dims.push_back(tensor::DimOp::create(rewriter, loc, broadcastInput, i)
2420 dims.push_back(IntegerAttr::get(IndexType::get(ctx),
2421 broadcastInputTy.getDimSize(i)));
2426 Value transposeInit = tensor::EmptyOp::create(
2427 rewriter, transposeOp.getLoc(), transposeResultShapes,
2428 broadcastInputTy.getElementType());
2431 Value transposeResult =
2432 TransposeOp::create(rewriter, loc, broadcastOp.getInput(),
2433 transposeInit, resultPerms)
2436 transposeOp, transposeResult, transposeOp.getInit(), resultDimensions);
2441void TransposeOp::getCanonicalizationPatterns(RewritePatternSet &results,
2442 MLIRContext *context) {
2443 results.
add<FoldTransposeWithTranspose, FoldTransposeSplatConstant,
2444 SwapTransposeWithBroadcast>(context);
2451void BroadcastOp::build(::mlir::OpBuilder &builder,
2452 ::mlir::OperationState &
result, Value input, Value init,
2454 ArrayRef<NamedAttribute> attributes) {
2455 result.addOperands(input);
2456 result.addOperands(init);
2457 result.addAttribute(getDimensionsAttrName(
result.name), dimensions);
2458 result.addAttributes(attributes);
2461 Type initType = init.
getType();
2462 if (llvm::isa<RankedTensorType>(initType))
2463 result.addTypes(initType);
2469void BroadcastOp::build(::mlir::OpBuilder &builder,
2470 ::mlir::OperationState &
result, Value input, Value init,
2471 ArrayRef<int64_t> dimensions,
2472 ArrayRef<NamedAttribute> attributes) {
2477ParseResult BroadcastOp::parse(OpAsmParser &parser, OperationState &
result) {
2479 parser,
result, [&](OpAsmParser &parser, NamedAttrList &attributes) {
2491void BroadcastOp::getAsmResultNames(
2493 if (!getResults().empty())
2494 setNameFn(getResults().front(),
"broadcasted");
2497void BroadcastOp::print(OpAsmPrinter &p) {
2501 {getDimensionsAttrName()});
2504LogicalResult BroadcastOp::verify() {
2505 ArrayRef<int64_t> dimensionsRef = getDimensions();
2507 auto inputType = getInput().getType();
2508 auto initType = getInit().getType();
2510 int64_t inputRank = inputType.getRank();
2511 int64_t initRank = initType.getRank();
2513 auto inputShape = inputType.getShape();
2514 auto initShape = initType.getShape();
2516 if ((
size_t)inputRank + dimensionsRef.size() != (
size_t)initRank)
2517 return emitOpError() <<
"input rank plus added dimensions does not "
2518 "match init rank. input rank: "
2520 <<
", dimensions size: " << dimensionsRef.size()
2521 <<
", init rank: " << initRank;
2523 for (
const auto &[idx, dim] : llvm::enumerate(dimensionsRef)) {
2524 if (dim < 0 || dim >= initRank)
2525 return emitOpError() <<
"dimension " << idx
2526 <<
" is out of range. expected range: [0, "
2527 << initRank - 1 <<
"], got: " << dim;
2531 if (uniquedDims.size() != dimensionsRef.size())
2532 return emitOpError() <<
"dimensions should not contain duplicates";
2535 SmallVector<int64_t> dimMap;
2536 for (
auto dim : llvm::seq<int64_t>(0, initRank)) {
2537 if (!llvm::is_contained(dimensionsRef, dim))
2538 dimMap.push_back(dim);
2541 for (
const auto &[inputDimIdx, initDimIdx] : llvm::enumerate(dimMap)) {
2544 if (inputShape[inputDimIdx] != initShape[initDimIdx])
2545 return emitOpError() <<
"input dim " << inputDimIdx
2546 <<
" should match init dim " << initDimIdx
2547 <<
". input: " << inputShape[inputDimIdx]
2548 <<
", init: " << initShape[initDimIdx];
2554SmallVector<utils::IteratorType> BroadcastOp::getIteratorTypesArray() {
2555 int64_t rank = getInit().getType().getRank();
2556 return SmallVector<utils::IteratorType>(rank, utils::IteratorType::parallel);
2559ArrayAttr BroadcastOp::getIndexingMaps() {
2561 int64_t rank = getInit().getType().getRank();
2567void BroadcastOp::getEffects(
2568 SmallVectorImpl<SideEffects::EffectInstance<MemoryEffects::Effect>>
2583 auto defBroadcastOp = broadcastOp.getInput().getDefiningOp<BroadcastOp>();
2584 if (!defBroadcastOp)
2589 Value init = broadcastOp.getInit();
2593 for (
auto dim : llvm::seq<int64_t>(0, initRank)) {
2594 if (!llvm::is_contained(dimensions, dim))
2595 dimMap.push_back(dim);
2597 for (
auto dim : defDimensions)
2598 foldedDims.push_back(dimMap[dim]);
2600 llvm::sort(foldedDims);
2602 broadcastOp, defBroadcastOp.getInput(), init, foldedDims);
2614 if (!broadcastOp.hasPureTensorSemantics())
2620 if (!splatValue.has_value())
2624 cast<RankedTensorType>(broadcastOp.getResult()[0].getType());
2625 if (!resultType.hasStaticShape())
2627 "result type has dynamic shape");
2636void BroadcastOp::getCanonicalizationPatterns(RewritePatternSet &results,
2637 MLIRContext *context) {
2638 results.
add<EraseIdentityLinalgOp<BroadcastOp>, FoldBroadcasts,
2639 FoldBroadcastSplatConstant>(context);
2646void linalg::YieldOp::print(OpAsmPrinter &p) {
2648 p <<
' ' << getOperands();
2651 p <<
" : " << getOperandTypes();
2654ParseResult YieldOp::parse(OpAsmParser &parser, OperationState &
result) {
2655 SmallVector<OpAsmParser::UnresolvedOperand, 2> opInfo;
2656 SmallVector<Type, 2> types;
2666static LogicalResult
verifyYield(linalg::YieldOp op, LinalgOp linalgOp) {
2667 if (op.getNumOperands() != linalgOp.getNumDpsInits())
2668 return op.emitOpError(
"expected number of yield values (")
2669 << op.getNumOperands()
2670 <<
") to match the number of inits / outs operands of the enclosing "
2671 <<
"LinalgOp (" << linalgOp.getNumDpsInits() <<
")";
2673 for (
OpOperand &opOperand : op->getOpOperands()) {
2675 linalgOp.getDpsInitOperand(opOperand.getOperandNumber());
2677 if (isa<MemRefType, RankedTensorType>(elementType))
2679 if (opOperand.get().getType() != elementType)
2680 return op.emitOpError(
"type of yield operand ")
2681 << (opOperand.getOperandNumber() + 1) <<
" ("
2682 << opOperand.get().getType() <<
") doesn't match "
2683 <<
"the element type of the enclosing linalg.generic op ("
2684 << elementType <<
")";
2689LogicalResult linalg::YieldOp::verify() {
2690 auto *parentOp = (*this)->getParentOp();
2691 if (parentOp->getNumRegions() != 1 || parentOp->getRegion(0).empty())
2692 return emitOpError(
"expected single non-empty parent region");
2694 if (
auto linalgOp = dyn_cast<LinalgOp>(parentOp))
2697 return emitOpError(
"expected parent op with LinalgOp interface");
2704LogicalResult IndexOp::verify() {
2705 auto linalgOp = dyn_cast<LinalgOp>((*this)->getParentOp());
2707 return emitOpError(
"expected parent op with LinalgOp interface");
2708 if (linalgOp.getNumLoops() <= getDim())
2709 return emitOpError(
"expected dim (")
2710 << getDim() <<
") to be lower than the number of loops ("
2711 << linalgOp.getNumLoops() <<
") of the enclosing LinalgOp";
2715OpFoldResult IndexOp::fold(FoldAdaptor adaptor) {
2716 auto linalgOp = dyn_cast_or_null<LinalgOp>((*this)->getParentOp());
2721 return OpFoldResult{};
2724 SmallVector<int64_t, 4> loopBounds = linalgOp.getStaticLoopRanges();
2725 uint64_t dim = getDim();
2726 assert(dim < loopBounds.size() &&
"Dim is out of bounds");
2727 if (loopBounds[dim] == 1)
2728 return IntegerAttr::get(IndexType::get(
getContext()), 0);
2730 return OpFoldResult{};
2735#include "mlir/Dialect/Linalg/IR/LinalgNamedStructuredOps.yamlgen.cpp.inc"
2737#define GET_OP_CLASSES
2738#include "mlir/Dialect/Linalg/IR/LinalgOps.cpp.inc"
2740#define GET_OP_CLASSES
2741#include "mlir/Dialect/Linalg/IR/LinalgStructuredOps.cpp.inc"
2742#define GET_OP_CLASSES
2743#include "mlir/Dialect/Linalg/IR/LinalgRelayoutOps.cpp.inc"
2760 for (
unsigned i = 0; i < num; ++i)
2767 auto rangeA = llvm::make_range(a.begin(), a.end());
2768 auto rangeB = llvm::make_range(
b.begin(),
b.end());
2769 auto concatRanges = llvm::concat<const AffineExpr>(rangeA, rangeB);
2770 return llvm::to_vector<4>(concatRanges);
2774 if (
auto memref = llvm::dyn_cast<MemRefType>(t)) {
2776 for (
auto size :
memref.getShape())
2783 if (
auto as =
memref.getMemorySpace()) {
2784 if (
auto attr = llvm::dyn_cast<IntegerAttr>(as))
2785 ss <<
"as" << attr.getInt();
2791 if (
auto vec = llvm::dyn_cast<VectorType>(t)) {
2794 vec.getShape(), [&](
int64_t i) { ss << i; }, [&]() { ss <<
"x"; });
2807 assert(isa<LinalgOp>(op));
2809 std::string fun =
"";
2811 if (UnaryFnAttr ufa = llvm::dyn_cast<UnaryFnAttr>(attr)) {
2812 fun = stringifyEnum(ufa.getValue()).str() +
"_";
2813 }
else if (BinaryFnAttr bfa = llvm::dyn_cast<BinaryFnAttr>(attr)) {
2814 fun = stringifyEnum(bfa.getValue()).str() +
"_";
2818 llvm::replace(name,
'.',
'_');
2819 llvm::raw_string_ostream ss(name);
2823 return std::string();
2838 LogicalResult matchAndRewrite(LinalgOp op,
2840 for (
OpOperand &opOperand : op->getOpOperands()) {
2844 auto mt = llvm::dyn_cast<MemRefType>(opOperand.get().getType());
2847 if (llvm::is_contained(op.getShape(&opOperand), 0)) {
2858struct FoldTensorCastConsumerOp :
public OpRewritePattern<tensor::CastOp> {
2859 using OpRewritePattern<tensor::CastOp>::OpRewritePattern;
2861 LogicalResult matchAndRewrite(tensor::CastOp castOp,
2862 PatternRewriter &rewriter)
const override {
2866 auto linalgOp = castOp.getSource().getDefiningOp<LinalgOp>();
2873 if (castOp->getBlock() != linalgOp->getBlock())
2876 OpBuilder::InsertionGuard guard(rewriter);
2879 Location loc = linalgOp.getLoc();
2880 OpResult resultValue = llvm::cast<OpResult>(castOp.getSource());
2883 llvm::cast<RankedTensorType>(castOp->getResult(0).getType());
2889 OpOperand *outOperand = linalgOp.getDpsInitOperand(resultNumber);
2891 tensor::CastOp::create(rewriter, loc, resultType, outOperand->
get());
2892 SmallVector<Value> newOperands = linalgOp.getDpsInputs();
2893 SmallVector<Value> outputOperands(linalgOp.getDpsInits().begin(),
2894 linalgOp.getDpsInits().end());
2895 outputOperands[resultNumber] = newOperand;
2896 newOperands.append(outputOperands.begin(), outputOperands.end());
2898 SmallVector<Type> resultTypes(linalgOp->result_type_begin(),
2899 linalgOp->result_type_end());
2900 resultTypes[resultNumber] = resultType;
2901 Operation *newOp =
clone(rewriter, linalgOp, resultTypes, newOperands);
2904 Value castBack = tensor::CastOp::create(
2908 results[resultNumber] = castBack;
2917static void populateMap(LinalgOp linalgOp, MutableArrayRef<OpOperand> operands,
2918 llvm::DenseMap<AffineExpr, int64_t> &affineExprToSize) {
2919 for (OpOperand &opOperand : operands) {
2920 if (linalgOp.isScalar(&opOperand))
2922 Value src = opOperand.get();
2923 auto sourceType = llvm::cast<RankedTensorType>(src.
getType());
2924 auto sourceMap = linalgOp.getMatchingIndexingMap(&opOperand);
2930 ArrayRef<int64_t> sourceShape = sourceType.getShape();
2932 if (
auto castOp = dyn_cast<tensor::CastOp>(parentOp)) {
2933 Value castSource = castOp.getSource();
2934 auto castSourceType =
2935 llvm::dyn_cast<RankedTensorType>(castSource.
getType());
2936 if (castSourceType && castSourceType.hasStaticShape())
2937 sourceShape = castSourceType.getShape();
2943 for (
unsigned i = 0; i < sourceShape.size(); i++) {
2944 if (sourceType.isDynamicDim(i))
2946 if (
auto affineDimExpr = dyn_cast<AffineDimExpr>(sourceMap.getResult(i)))
2947 affineExprToSize.try_emplace(affineDimExpr, sourceShape[i]);
2957static void createNewOperandWithStaticSizes(
2958 Location loc, PatternRewriter &rewriter, OpOperand *opOperand,
2959 llvm::DenseMap<AffineExpr, int64_t> &affineExprToSize, LinalgOp linalgOp,
2960 SmallVector<Value> &newOperands, SmallVector<Type> &resultTypes,
2961 bool &changeNeeded) {
2962 Value src = opOperand->
get();
2963 newOperands.push_back(src);
2964 if (linalgOp.isScalar(opOperand))
2966 auto sourceType = llvm::cast<RankedTensorType>(src.
getType());
2967 Type resultType = sourceType;
2968 if (sourceType.hasStaticShape() && linalgOp.isDpsInit(opOperand)) {
2969 resultTypes.push_back(resultType);
2972 ArrayRef<int64_t> sourceShape = sourceType.getShape();
2973 AffineMap sourceMap = linalgOp.getMatchingIndexingMap(opOperand);
2974 SmallVector<int64_t> newShape;
2977 bool newOperandNeeded =
false;
2978 for (
unsigned i = 0; i < sourceShape.size(); i++) {
2979 int64_t dimShape = sourceShape[i];
2980 AffineExpr dimExpr = sourceMap.
getResult(i);
2981 if (!affineExprToSize.contains(dimExpr) || !sourceType.isDynamicDim(i)) {
2982 newShape.push_back(dimShape);
2988 newShape.push_back(affineExprToSize[dimExpr]);
2989 newOperandNeeded =
true;
2991 resultType = RankedTensorType::get(newShape, sourceType.getElementType(),
2992 sourceType.getEncoding());
2993 if (newOperandNeeded) {
2994 changeNeeded =
true;
2997 Value newOperand = tensor::CastOp::create(rewriter, loc, resultType, src);
2999 newOperands[index] = newOperand;
3001 if (linalgOp.isDpsInit(opOperand))
3002 resultTypes.push_back(resultType);
3008struct InferStaticShapeOfOperands :
public OpInterfaceRewritePattern<LinalgOp> {
3009 using OpInterfaceRewritePattern<LinalgOp>::OpInterfaceRewritePattern;
3011 LogicalResult matchAndRewrite(LinalgOp linalgOp,
3012 PatternRewriter &rewriter)
const override {
3013 if (!linalgOp.hasPureTensorSemantics())
3017 if (llvm::any_of(linalgOp.getIndexingMapsArray(), [](AffineMap map) {
3018 return !map.isProjectedPermutation();
3023 llvm::DenseMap<AffineExpr, int64_t> affineExprToSize;
3024 Location loc = linalgOp.getLoc();
3028 populateMap(linalgOp, linalgOp->getOpOperands(), affineExprToSize);
3030 SmallVector<Value> newOperands;
3031 SmallVector<Type> resultTypes;
3035 bool changeNeeded =
false;
3036 newOperands.reserve(linalgOp->getNumOperands());
3037 resultTypes.reserve(linalgOp.getNumDpsInits());
3040 for (OpOperand &opOperand : linalgOp->getOpOperands()) {
3041 createNewOperandWithStaticSizes(loc, rewriter, &opOperand,
3042 affineExprToSize, linalgOp, newOperands,
3043 resultTypes, changeNeeded);
3052 Operation *newOp =
clone(rewriter, linalgOp, resultTypes, newOperands);
3053 SmallVector<Value> replacements;
3055 for (
auto it : llvm::zip(linalgOp->getResults(), newOp->
getResults())) {
3056 Value newResult = std::get<1>(it);
3057 Value oldResult = std::get<0>(it);
3058 Type newType = newResult.
getType();
3059 Type oldType = oldResult.
getType();
3060 replacements.push_back(
3061 (newType != oldType)
3062 ? tensor::CastOp::create(rewriter, loc, oldType, newResult)
3065 rewriter.
replaceOp(linalgOp, replacements);
3079LogicalResult SoftmaxOp::verify() {
3080 ShapedType inputType = getInputOperandType();
3081 ShapedType outputType = getOutputOperandType();
3083 ArrayRef<int64_t> inputShape = inputType.getShape();
3084 ArrayRef<int64_t> outputShape = outputType.getShape();
3086 return emitOpError(
"incompatible output shape");
3088 int64_t inputRank = getInputOperandRank();
3089 int64_t dimension = getDimension();
3090 if ((dimension < 0) || (dimension >= inputRank))
3091 return emitOpError(
"incorrect dimension specified");
3096SmallVector<Range> SoftmaxOp::getIterationDomain(OpBuilder &builder) {
3097 int64_t operandRank = getInputOperandRank();
3098 SmallVector<Range> loopBounds(operandRank);
3099 Location loc = getLoc();
3102 Value source = getInput();
3103 for (
auto dim : llvm::seq<int64_t>(0, operandRank)) {
3104 loopBounds[dim].offset = zero;
3105 loopBounds[dim].size =
getDimValue(builder, loc, source, dim);
3106 loopBounds[dim].stride = one;
3111SmallVector<utils::IteratorType> SoftmaxOp::getLoopIteratorTypes() {
3112 SmallVector<utils::IteratorType> iteratorTypes(getInputOperandRank(),
3113 utils::IteratorType::parallel);
3114 iteratorTypes[getDimension()] = utils::IteratorType::reduction;
3115 return iteratorTypes;
3121FailureOr<TilingResult> SoftmaxOp::getTiledImplementation(
3122 OpBuilder &builder, ArrayRef<OpFoldResult> offsets,
3123 ArrayRef<OpFoldResult> sizes, ArrayRef<InnerTileAlignment>) {
3127FailureOr<TilingResult>
3128SoftmaxOp::getTiledImplementation(OpBuilder &builder,
3129 ArrayRef<OpFoldResult> offsets,
3130 ArrayRef<OpFoldResult> sizes) {
3131 int64_t rank = getInputOperandRank();
3133 SmallVector<OpFoldResult> strides(rank, oneAttr);
3134 SmallVector<Value> tiledOperands;
3135 Operation *inputSlice =
3136 getSlice(builder, getLoc(), getInput(), offsets, sizes, strides);
3138 return emitOpError(
"failed to compute input slice");
3140 tiledOperands.emplace_back(inputSlice->
getResult(0));
3141 Operation *outputSlice =
3142 getSlice(builder, getLoc(), getOutput(), offsets, sizes, strides);
3144 return emitOpError(
"failed to compute output slice");
3146 tiledOperands.emplace_back(outputSlice->
getResult(0));
3148 SmallVector<Type, 4> resultTypes;
3149 if (hasPureTensorSemantics())
3150 resultTypes.push_back(tiledOperands[1].
getType());
3151 Operation *tiledOp =
3152 mlir::clone(builder, getOperation(), resultTypes, tiledOperands);
3154 return TilingResult{
3157 llvm::to_vector(ArrayRef<Operation *>{inputSlice, outputSlice})};
3160LogicalResult SoftmaxOp::getResultTilePosition(
3161 OpBuilder &builder,
unsigned resultNumber, ArrayRef<OpFoldResult> offsets,
3162 ArrayRef<OpFoldResult> sizes, SmallVector<OpFoldResult> &resultOffsets,
3163 SmallVector<OpFoldResult> &resultSizes) {
3164 if (resultNumber == 0) {
3165 resultOffsets.assign(offsets.begin(), offsets.end());
3166 resultSizes.assign(sizes.begin(), sizes.end());
3173LogicalResult SoftmaxOp::fold(FoldAdaptor, SmallVectorImpl<OpFoldResult> &) {
3178SoftmaxOp::reifyResultShapes(OpBuilder &
b,
3180 SmallVector<OpFoldResult> shapes;
3181 Location loc = getOperation()->getLoc();
3182 IRRewriter rewriter(
b);
3183 auto inputShapedType = llvm::cast<ShapedType>(getInputOperandType());
3184 auto outputShapedType = llvm::cast<ShapedType>(getOutputOperandType());
3185 for (int64_t dim : llvm::seq<int64_t>(0, getOutputOperandRank())) {
3186 if (!outputShapedType.isDynamicDim(dim)) {
3188 shapes.push_back(
b.getIndexAttr(inputShapedType.getDimSize(dim)));
3195 reifiedReturnShapes.emplace_back(std::move(shapes));
3199void SoftmaxOp::getEffects(
3200 SmallVectorImpl<SideEffects::EffectInstance<MemoryEffects::Effect>>
3202 for (
auto [index, operand] : llvm::enumerate(getDpsInputs())) {
3203 if (!llvm::isa<MemRefType>(operand.
getType()))
3206 &getOperation()->getOpOperand(index), 0,
3211 for (OpOperand &operand : getDpsInitsMutable()) {
3212 if (!llvm::isa<MemRefType>(operand.get().
getType()))
3243static std::tuple<SmallVector<utils::IteratorType>, SmallVector<AffineMap>>
3245 int64_t dim,
bool allParallel =
false) {
3247 utils::IteratorType::parallel);
3249 iteratorTypes[dim] = utils::IteratorType::reduction;
3253 for (
int i = 0; i < inputRank; i++) {
3260 return std::make_tuple(iteratorTypes, indexingMaps);
3265template <
typename T>
3268 auto inputType = cast<ShapedType>(input.
getType());
3270 int64_t inputRank = inputShape.size();
3271 auto [iteratorTypes, indexingMaps] =
3273 assert(indexingMaps.size() == 2 &&
3274 "We should have two maps: 1 for the input, 1 for the output");
3275 assert(indexingMaps[0].isIdentity() &&
"input map should be identity");
3277 auto genericOp = linalg::GenericOp::create(
3278 builder, loc, output.
getType(), input, output, indexingMaps,
3280 Value result = T::create(b, loc, args[0], args[1]);
3281 linalg::YieldOp::create(b, loc, result);
3283 return genericOp.getResult(0);
3291 auto inputType = cast<ShapedType>(input.
getType());
3293 int64_t inputRank = inputShape.size();
3295 builder, inputRank, dim,
true);
3296 assert(indexingMaps.size() == 2 &&
"We should have one map for each input");
3297 assert(indexingMaps[0].isIdentity() &&
"input map should be identity");
3299 indexingMaps.push_back(indexingMaps[0]);
3300 auto genericOp = linalg::GenericOp::create(
3302 indexingMaps, iteratorTypes,
3304 Value diff = arith::SubFOp::create(b, loc, args[0], args[1]);
3305 Value result = math::ExpOp::create(b, loc, diff);
3306 linalg::YieldOp::create(b, loc, result);
3308 return genericOp.getResult(0);
3318 auto inputType = cast<ShapedType>(numerator.
getType());
3320 int64_t inputRank = inputShape.size();
3322 builder, inputRank, dim,
true);
3323 assert(indexingMaps.size() == 2 &&
3324 "We should have one map for each input (2)");
3325 assert(indexingMaps[0].isIdentity() &&
"Numerator map should be identity");
3327 indexingMaps.push_back(indexingMaps[0]);
3328 auto genericOp = linalg::GenericOp::create(
3330 output, indexingMaps, iteratorTypes,
3332 Value result = arith::DivFOp::create(b, loc, args[0], args[1]);
3333 linalg::YieldOp::create(b, loc, result);
3335 return genericOp.getResult(0);
3357FailureOr<SmallVector<Value>> SoftmaxOp::decomposeOperation(OpBuilder &
b) {
3358 OpBuilder::InsertionGuard guard(
b);
3359 b.setInsertionPoint(*
this);
3360 Location loc = getLoc();
3361 Value input = getInput();
3362 ShapedType inputType = getInputOperandType();
3363 Type elementType = inputType.getElementType();
3364 int64_t reductionDim = getDimension();
3366 Value output = getOutput();
3367 dims.erase(dims.begin() + reductionDim);
3369 Value outputReduce = tensor::EmptyOp::create(
b, loc, dims, elementType);
3371 elementType,
b, loc,
3373 Value neutralForMaxFInit =
3374 linalg::FillOp::create(
b, loc, Value{neutralForMaxF}, outputReduce)
3386 linalg::FillOp::create(
b, loc, Value{zero}, outputReduce).
result();
3392 buildDivOp(
b, loc, numerator, denominator, output, reductionDim);
3393 return SmallVector<Value>{
result};
3400LogicalResult WinogradFilterTransformOp::verify() {
3401 auto filterType = cast<ShapedType>(getFilter().
getType());
3402 ArrayRef<int64_t> filterShape = filterType.getShape();
3403 int64_t filterH = filterShape[getFilterHDim()];
3404 int64_t filterW = filterShape[getFilterWDim()];
3405 WinogradConv2DFmr fmr = getFmr();
3409 if (filterH != r && filterH != 1)
3410 return emitOpError(
"expect filter height either equals to r or 1");
3411 if (filterW != r && filterW != 1)
3412 return emitOpError(
"expect filter width either equals to r or 1");
3413 if (filterH == 1 && filterW == 1)
3414 return emitOpError(
"expect either filter height or width equals to r");
3416 SmallVector<int64_t> expectedOutputShape;
3417 expectedOutputShape.push_back(filterH == r ? m + r - 1 : 1);
3418 expectedOutputShape.push_back(filterW == r ? m + r - 1 : 1);
3419 expectedOutputShape.push_back(filterShape[getFilterCDim()]);
3420 expectedOutputShape.push_back(filterShape[getFilterFDim()]);
3422 auto outputType = cast<ShapedType>(getOutput().
getType());
3423 ArrayRef<int64_t> outputShape = outputType.getShape();
3425 return emitOpError(
"the output shape is not expected");
3431WinogradFilterTransformOp::getIterationDomain(OpBuilder &builder) {
3432 Location loc = getLoc();
3435 Value filter = getFilter();
3436 int64_t filterRank = getFilterOperandRank();
3437 SmallVector<Range> loopBounds(filterRank);
3438 for (
unsigned dim = 0; dim < filterRank; ++dim) {
3439 loopBounds[dim].offset = zeroAttr;
3440 loopBounds[dim].size =
getDimValue(builder, loc, filter, dim);
3441 loopBounds[dim].stride = oneAttr;
3446SmallVector<utils::IteratorType>
3447WinogradFilterTransformOp::getLoopIteratorTypes() {
3448 int64_t filterRank = getFilterOperandRank();
3449 SmallVector<utils::IteratorType> iteratorTypes(filterRank,
3450 utils::IteratorType::parallel);
3451 return iteratorTypes;
3454LogicalResult WinogradFilterTransformOp::getResultTilePosition(
3455 OpBuilder &builder,
unsigned resultNumber, ArrayRef<OpFoldResult> offsets,
3456 ArrayRef<OpFoldResult> sizes, SmallVector<OpFoldResult> &resultOffsets,
3457 SmallVector<OpFoldResult> &resultSizes) {
3459 ShapedType filterType = getFilterOperandType();
3460 ArrayRef<int64_t> filterShape = filterType.getShape();
3461 int64_t filterH = filterShape[getFilterHDim()];
3462 int64_t filterW = filterShape[getFilterWDim()];
3463 WinogradConv2DFmr fmr = getFmr();
3466 int64_t alpha = m + r - 1;
3467 int64_t alphaH = filterH != 1 ? alpha : 1;
3468 int64_t alphaW = filterW != 1 ? alpha : 1;
3472 resultOffsets.append(
3473 {zeroAttr, zeroAttr, offsets[getFilterCDim()], offsets[getFilterFDim()]});
3475 {alphaHAttr, alphaWAttr, sizes[getFilterCDim()], sizes[getFilterFDim()]});
3483FailureOr<TilingResult> WinogradFilterTransformOp::getTiledImplementation(
3484 OpBuilder &builder, ArrayRef<OpFoldResult> offsets,
3485 ArrayRef<OpFoldResult> sizes, ArrayRef<InnerTileAlignment>) {
3495FailureOr<TilingResult> WinogradFilterTransformOp::getTiledImplementation(
3496 OpBuilder &builder, ArrayRef<OpFoldResult> offsets,
3497 ArrayRef<OpFoldResult> sizes) {
3500 ShapedType filterType = getFilterOperandType();
3501 ArrayRef<int64_t> filterShape = filterType.getShape();
3502 int64_t filterH = filterShape[getFilterHDim()];
3503 int64_t filterW = filterShape[getFilterWDim()];
3506 SmallVector<Value> tiledOperands;
3507 SmallVector<OpFoldResult> sliceOffsets, sliceSizes;
3509 sliceOffsets.append(
3510 {offsets[getFilterFDim()], zeroAttr, zeroAttr, offsets[getFilterCDim()]});
3511 sliceSizes.append({sizes[getFilterFDim()], filterHAttr, filterWAttr,
3512 sizes[getFilterCDim()]});
3513 int64_t filterRank = getFilterOperandRank();
3514 SmallVector<OpFoldResult> filterStrides(filterRank, oneAttr);
3515 Location loc = getLoc();
3516 auto filterSlice = tensor::ExtractSliceOp::create(
3517 builder, loc, getFilter(), sliceOffsets, sliceSizes, filterStrides);
3518 tiledOperands.emplace_back(filterSlice);
3520 SmallVector<OpFoldResult> resultOffsets, resultSizes;
3525 int64_t outputRank = getOutputOperandRank();
3526 SmallVector<OpFoldResult> outputStrides(outputRank, oneAttr);
3527 auto outputSlice = tensor::ExtractSliceOp::create(
3528 builder, loc, getOutput(), resultOffsets, resultSizes, outputStrides);
3529 tiledOperands.emplace_back(outputSlice);
3531 SmallVector<Type> resultTypes;
3532 resultTypes.push_back(tiledOperands[1].
getType());
3533 Operation *tiledOp =
3534 mlir::clone(builder, getOperation(), resultTypes, tiledOperands);
3536 return TilingResult{
3539 llvm::to_vector(ArrayRef<Operation *>{filterSlice, outputSlice})};
3546LogicalResult WinogradInputTransformOp::verify() {
3547 auto inputType = cast<ShapedType>(getInput().
getType());
3548 ArrayRef<int64_t> inputShape = inputType.getShape();
3549 int64_t inputH = inputShape[getInputHDim()];
3550 int64_t inputW = inputShape[getInputWDim()];
3551 WinogradConv2DFmr fmr = getFmr();
3554 int64_t tileSize = m + r - 1;
3556 auto outputType = cast<ShapedType>(getOutput().
getType());
3557 ArrayRef<int64_t> outputShape = outputType.getShape();
3558 bool leftTransform = outputShape[getOutputAlphaHDim()] != 1;
3559 bool rightTransform = outputShape[getOutputAlphaWDim()] != 1;
3561 SmallVector<int64_t> expectedOutputShape(6, inputH);
3562 if (ShapedType::isDynamic(inputH)) {
3563 expectedOutputShape[getOutputAlphaHDim()] = tileSize;
3564 expectedOutputShape[getOutputTileHDim()] = ShapedType::kDynamic;
3566 expectedOutputShape[getOutputAlphaHDim()] = leftTransform ? tileSize : 1;
3567 expectedOutputShape[getOutputTileHDim()] =
3568 leftTransform ? (inputH - (r - 1)) / m : inputH;
3570 if (ShapedType::isDynamic(inputW)) {
3571 expectedOutputShape[getOutputAlphaWDim()] = tileSize;
3572 expectedOutputShape[getOutputTileWDim()] = ShapedType::kDynamic;
3574 expectedOutputShape[getOutputAlphaWDim()] = rightTransform ? tileSize : 1;
3575 expectedOutputShape[getOutputTileWDim()] =
3576 rightTransform ? (inputW - (r - 1)) / m : inputW;
3578 expectedOutputShape[getOutputNDim()] = inputShape[getInputNDim()];
3579 expectedOutputShape[getOutputCDim()] = inputShape[getInputCDim()];
3582 return emitOpError(
"the output shape is not expected");
3588WinogradInputTransformOp::getIterationDomain(OpBuilder &builder) {
3589 Location loc = getLoc();
3592 Value output = getOutput();
3593 int64_t outputRank = getOutputOperandRank();
3594 SmallVector<Range> loopBounds(outputRank);
3595 for (
unsigned dim = 0; dim < outputRank; ++dim) {
3596 loopBounds[dim].offset = zeroAttr;
3598 loopBounds[dim].size =
getDimValue(builder, loc, output, dim);
3599 loopBounds[dim].stride = oneAttr;
3604SmallVector<utils::IteratorType>
3605WinogradInputTransformOp::getLoopIteratorTypes() {
3606 int64_t outputRank = getOutputOperandRank();
3607 SmallVector<utils::IteratorType> iteratorTypes(outputRank,
3608 utils::IteratorType::parallel);
3609 return iteratorTypes;
3612LogicalResult WinogradInputTransformOp::getResultTilePosition(
3613 OpBuilder &builder,
unsigned resultNumber, ArrayRef<OpFoldResult> offsets,
3614 ArrayRef<OpFoldResult> sizes, SmallVector<OpFoldResult> &resultOffsets,
3615 SmallVector<OpFoldResult> &resultSizes) {
3617 ShapedType outputType = getOutputOperandType();
3618 ArrayRef<int64_t> outputShape = outputType.getShape();
3619 int64_t outputAlphaH = outputShape[getOutputAlphaHDim()];
3620 int64_t outputAlphaW = outputShape[getOutputAlphaWDim()];
3622 WinogradConv2DFmr fmr = getFmr();
3625 int64_t alpha = m + r - 1;
3626 int64_t alphaH = outputAlphaH != 1 ? alpha : 1;
3627 int64_t alphaW = outputAlphaW != 1 ? alpha : 1;
3632 resultOffsets.append({zeroAttr, zeroAttr, offsets[getOutputTileHDim()],
3633 offsets[getOutputTileWDim()], offsets[getOutputNDim()],
3634 offsets[getOutputCDim()]});
3635 resultSizes.append({alphaHAttr, alphaWAttr, sizes[getOutputTileHDim()],
3636 sizes[getOutputTileWDim()], sizes[getOutputNDim()],
3637 sizes[getOutputCDim()]});
3645FailureOr<TilingResult> WinogradInputTransformOp::getTiledImplementation(
3646 OpBuilder &builder, ArrayRef<OpFoldResult> offsets,
3647 ArrayRef<OpFoldResult> sizes, ArrayRef<InnerTileAlignment>) {
3657FailureOr<TilingResult>
3658WinogradInputTransformOp::getTiledImplementation(OpBuilder &builder,
3659 ArrayRef<OpFoldResult> offsets,
3660 ArrayRef<OpFoldResult> sizes) {
3662 WinogradConv2DFmr fmr = getFmr();
3666 ShapedType outputType = getOutputOperandType();
3667 ArrayRef<int64_t> outputShape = outputType.getShape();
3668 int64_t alphaH = outputShape[getOutputAlphaHDim()];
3669 int64_t alphaW = outputShape[getOutputAlphaWDim()];
3671 Location loc = getLoc();
3673 auto identityAffineMap =
3675 auto offsetAffineMap =
3678 builder, loc, (alphaH != 1 ? offsetAffineMap : identityAffineMap),
3679 offsets[getOutputTileHDim()]);
3681 builder, loc, (alphaW != 1 ? offsetAffineMap : identityAffineMap),
3682 offsets[getOutputTileWDim()]);
3686 builder, loc, sizeAffineMap, sizes[getOutputTileHDim()]);
3688 builder, loc, sizeAffineMap, sizes[getOutputTileWDim()]);
3690 SmallVector<Value> tiledOperands;
3691 SmallVector<OpFoldResult> sliceOffsets, sliceSizes;
3693 OpFoldResult offsetH = OpFoldResult(mappedOffsetH);
3694 OpFoldResult offsetW = OpFoldResult(mappedOffsetW);
3695 sliceOffsets.append(
3696 {offsets[getOutputNDim()], offsetH, offsetW, offsets[getOutputCDim()]});
3697 OpFoldResult sizeH =
3698 alphaH != 1 ? OpFoldResult(mappedSizeH) : OpFoldResult(oneAttr);
3699 OpFoldResult sizeW =
3700 alphaW != 1 ? OpFoldResult(mappedSizeW) : OpFoldResult(oneAttr);
3702 {sizes[getOutputNDim()], sizeH, sizeW, sizes[getOutputCDim()]});
3703 int64_t inputRank = getInputOperandRank();
3704 SmallVector<OpFoldResult> inputStrides(inputRank, oneAttr);
3705 auto inputSlice = tensor::ExtractSliceOp::create(
3706 builder, loc, getInput(), sliceOffsets, sliceSizes, inputStrides);
3707 tiledOperands.emplace_back(inputSlice);
3709 SmallVector<OpFoldResult> resultOffsets, resultSizes;
3714 int64_t outputRank = getOutputOperandRank();
3715 SmallVector<OpFoldResult> outputStrides(outputRank, oneAttr);
3716 auto outputSlice = tensor::ExtractSliceOp::create(
3717 builder, loc, getOutput(), resultOffsets, resultSizes, outputStrides);
3718 tiledOperands.emplace_back(outputSlice);
3720 SmallVector<Type> resultTypes;
3721 resultTypes.push_back(tiledOperands[1].
getType());
3722 Operation *tiledOp =
3723 mlir::clone(builder, getOperation(), resultTypes, tiledOperands);
3725 return TilingResult{
3728 llvm::to_vector(ArrayRef<Operation *>{inputSlice, outputSlice})};
3735LogicalResult WinogradOutputTransformOp::verify() {
3736 auto valueType = cast<ShapedType>(getValue().
getType());
3737 ArrayRef<int64_t> valueShape = valueType.getShape();
3738 int64_t valueH = valueShape[getValueAlphaHDim()];
3739 int64_t valueW = valueShape[getValueAlphaWDim()];
3740 int64_t valueTileH = valueShape[getValueTileHDim()];
3741 int64_t valueTileW = valueShape[getValueTileWDim()];
3742 WinogradConv2DFmr fmr = getFmr();
3745 bool leftTransform = valueH != 1;
3746 bool rightTransform = valueW != 1;
3748 int64_t outputRank = getOutputOperandRank();
3749 SmallVector<int64_t> expectedOutputShape(outputRank, valueH);
3750 if (ShapedType::isDynamic(valueH) || ShapedType::isDynamic(valueTileH)) {
3751 expectedOutputShape[getOutputHDim()] = ShapedType::kDynamic;
3753 if (valueH != (leftTransform ? m + r - 1 : 1))
3754 return emitOpError(
"expect input height equals to input tile size");
3755 expectedOutputShape[getOutputHDim()] = (leftTransform ? m : 1) * valueTileH;
3757 if (ShapedType::isDynamic(valueW) || ShapedType::isDynamic(valueTileW)) {
3758 expectedOutputShape[getOutputWDim()] = ShapedType::kDynamic;
3760 if (valueW != (rightTransform ? m + r - 1 : 1))
3761 return emitOpError(
"expect input width equals to input tile size");
3762 expectedOutputShape[getOutputWDim()] =
3763 (rightTransform ? m : 1) * valueTileW;
3765 expectedOutputShape[getOutputNDim()] = valueShape[getValueNDim()];
3766 expectedOutputShape[getOutputFDim()] = valueShape[getValueFDim()];
3768 auto outputType = cast<ShapedType>(getOutput().
getType());
3769 ArrayRef<int64_t> outputShape = outputType.getShape();
3771 return emitOpError(
"the output shape is not expected");
3777WinogradOutputTransformOp::getIterationDomain(OpBuilder &builder) {
3778 Location loc = getLoc();
3781 Value value = getValue();
3782 int64_t valueRank = getValueOperandRank();
3783 SmallVector<Range> loopBounds(valueRank);
3784 for (
unsigned dim = 0; dim < valueRank; ++dim) {
3785 loopBounds[dim].offset = zeroAttr;
3787 loopBounds[dim].size =
getDimValue(builder, loc, value, dim);
3788 loopBounds[dim].stride = oneAttr;
3793SmallVector<utils::IteratorType>
3794WinogradOutputTransformOp::getLoopIteratorTypes() {
3795 int64_t valueRank = getValueOperandRank();
3796 SmallVector<utils::IteratorType> iteratorTypes(valueRank,
3797 utils::IteratorType::parallel);
3798 return iteratorTypes;
3801LogicalResult WinogradOutputTransformOp::getResultTilePosition(
3802 OpBuilder &builder,
unsigned resultNumber, ArrayRef<OpFoldResult> offsets,
3803 ArrayRef<OpFoldResult> sizes, SmallVector<OpFoldResult> &resultOffsets,
3804 SmallVector<OpFoldResult> &resultSizes) {
3805 WinogradConv2DFmr fmr = getFmr();
3809 Location loc = getLoc();
3811 auto identityAffineMap =
3816 ShapedType valueType = getValueOperandType();
3817 ArrayRef<int64_t> valueShape = valueType.getShape();
3818 int64_t valueH = valueShape[0];
3819 int64_t valueW = valueShape[1];
3821 builder, loc, (valueH != 1 ? affineMap : identityAffineMap),
3822 offsets[getValueTileHDim()]);
3824 builder, loc, (valueW != 1 ? affineMap : identityAffineMap),
3825 offsets[getValueTileWDim()]);
3827 builder, loc, affineMap, sizes[getValueTileHDim()]);
3829 builder, loc, affineMap, sizes[getValueTileWDim()]);
3832 OpFoldResult offsetH = OpFoldResult(mappedOffsetH);
3833 OpFoldResult offsetW = OpFoldResult(mappedOffsetW);
3834 OpFoldResult sizeH =
3835 valueH != 1 ? OpFoldResult(mappedSizeH) : OpFoldResult(oneAttr);
3836 OpFoldResult sizeW =
3837 valueW != 1 ? OpFoldResult(mappedSizeW) : OpFoldResult(oneAttr);
3839 resultOffsets.append(
3840 {offsets[getValueNDim()], offsetH, offsetW, offsets[getValueFDim()]});
3842 {sizes[getValueNDim()], sizeH, sizeW, sizes[getValueFDim()]});
3849FailureOr<TilingResult> WinogradOutputTransformOp::getTiledImplementation(
3850 OpBuilder &builder, ArrayRef<OpFoldResult> offsets,
3851 ArrayRef<OpFoldResult> sizes, ArrayRef<InnerTileAlignment>) {
3861FailureOr<TilingResult> WinogradOutputTransformOp::getTiledImplementation(
3862 OpBuilder &builder, ArrayRef<OpFoldResult> offsets,
3863 ArrayRef<OpFoldResult> sizes) {
3866 Location loc = getLoc();
3867 SmallVector<Value> tiledOperands;
3868 SmallVector<OpFoldResult> sliceOffsets, sliceSizes;
3870 ShapedType valueType = getValueOperandType();
3871 ArrayRef<int64_t> valueShape = valueType.getShape();
3872 int64_t alphaH = valueShape[getValueAlphaHDim()];
3873 int64_t alphaW = valueShape[getValueAlphaWDim()];
3877 sliceOffsets.append({zeroAttr, zeroAttr, offsets[getValueTileHDim()],
3878 offsets[getValueTileWDim()], offsets[getValueNDim()],
3879 offsets[getValueFDim()]});
3880 sliceSizes.append({alphaHAttr, alphaWAttr, sizes[getValueTileHDim()],
3881 sizes[getValueTileWDim()], sizes[getValueNDim()],
3882 sizes[getValueFDim()]});
3883 int64_t valueRank = getValueOperandRank();
3884 SmallVector<OpFoldResult> sliceStrides(valueRank, oneAttr);
3885 auto valueSlice = tensor::ExtractSliceOp::create(
3886 builder, loc, getValue(), sliceOffsets, sliceSizes, sliceStrides);
3887 tiledOperands.emplace_back(valueSlice);
3889 SmallVector<OpFoldResult> resultOffsets, resultSizes;
3894 int64_t outputRank = getOutputOperandRank();
3895 SmallVector<OpFoldResult> strides(outputRank, oneAttr);
3896 auto outputSlice = tensor::ExtractSliceOp::create(
3897 builder, loc, getOutput(), resultOffsets, resultSizes, strides);
3898 tiledOperands.emplace_back(outputSlice);
3900 SmallVector<Type> resultTypes;
3901 resultTypes.push_back(tiledOperands[1].
getType());
3902 Operation *tiledOp =
3903 mlir::clone(builder, getOperation(), resultTypes, tiledOperands);
3905 return TilingResult{
3908 llvm::to_vector(ArrayRef<Operation *>{valueSlice, outputSlice})};
3922 llvm::set_union(explicitSet, defaultSet);
3923 return explicitSet == defaultSet;
3943 matmulOp.getDefaultIndexingMaps(matmulOp->getContext());
3945 auto opIndexingMap = opIndexingMaps[opIndex];
3946 auto defaultIndexingMap = defaultIndexingMaps[opIndex];
3949 return matmulOp->emitOpError()
3950 <<
"Unexpected dim expression in map result.";
3953 if (!matmulOp.isValidLhsRhsBroadcastMap(opIndexingMap)) {
3954 return matmulOp->emitOpError()
3955 <<
"Invalid broadcast requested, should be (d2).";
3964template <
typename OpTy>
3967 AffineMap defaultIndexingMap,
bool isLHS) {
3968 assert((isa<BatchMatmulOp>(batchVariantMatmulOp) ||
3969 isa<BatchReduceMatmulOp>(batchVariantMatmulOp)) &&
3970 "Expected BatchMatmulOp or BatchReduceMatmulOp");
3973 return batchVariantMatmulOp->emitOpError()
3974 <<
"Unexpected result dim expression (outside the set of default "
3979 return batchVariantMatmulOp->emitOpError()
3980 <<
"no. of result dim expressions exceeds 3.";
3982 auto hasValidBatchDim = [](
AffineMap map) {
3989 if (!batchVariantMatmulOp.isValidLhsRhsBroadcastMap(opIndexingMap, isLHS))
3990 return batchVariantMatmulOp->emitOpError()
3991 <<
"Invalid broadcast requested.";
3992 }
else if (!hasValidBatchDim(opIndexingMap)) {
3993 return batchVariantMatmulOp->emitOpError()
3994 <<
"Invalid batch dimension expression.";
4002template <
typename OpTy>
4005 assert((isa<BatchMatmulOp>(batchVariantMatmulOp) ||
4006 isa<BatchReduceMatmulOp>(batchVariantMatmulOp)) &&
4007 "Expected BatchMatmulOp or BatchReduceMatmulOp");
4008 if (isa<BatchMatmulOp>(batchVariantMatmulOp) &&
4011 return batchVariantMatmulOp->emitOpError()
4012 <<
"expects 3 dims, but got (" << opIndexingMap.
getNumResults()
4015 if (isa<BatchReduceMatmulOp>(batchVariantMatmulOp) &&
4017 return batchVariantMatmulOp->emitOpError()
4018 <<
"expects 2 dims, but got (" << opIndexingMap.
getNumResults()
4022 auto areValidOutputResultDim = [&](
AffineMap outputMap) {
4023 return isa<BatchMatmulOp>(batchVariantMatmulOp)
4024 ? outputMap.getResult(0).isFunctionOfDim(0) &&
4025 outputMap.getResult(1).isFunctionOfDim(1) &&
4026 outputMap.getResult(2).isFunctionOfDim(2)
4027 : outputMap.getResult(0).isFunctionOfDim(1) &&
4028 outputMap.getResult(1).isFunctionOfDim(2);
4031 if (!areValidOutputResultDim(opIndexingMap)) {
4032 return batchVariantMatmulOp->emitOpError()
4033 <<
"Invalid output map result dimension.";
4042template <
typename OpTy>
4047 batchVariantMatmulOp.getIndexingMapsArray();
4049 batchVariantMatmulOp.getDefaultIndexingMaps(
4050 batchVariantMatmulOp->getContext());
4052 if (opIndexingMaps.size() != 3)
4053 return batchVariantMatmulOp->emitOpError()
4054 <<
"Indexing_map attribute must have 3 affine maps.";
4056 auto opIndexingMap = opIndexingMaps[opIndex];
4057 auto defaultIndexingMap = defaultIndexingMaps[opIndex];
4065 defaultIndexingMap, opIndex == 0)))
4075 if (m == 2 && r == 3)
4076 return WinogradConv2DFmr::F_2_3;
4077 if (m == 4 && r == 3)
4078 return WinogradConv2DFmr::F_4_3;
4079 if (m == 2 && r == 5)
4080 return WinogradConv2DFmr::F_2_5;
4081 return std::nullopt;
4086 case WinogradConv2DFmr::F_2_3:
4088 case WinogradConv2DFmr::F_4_3:
4090 case WinogradConv2DFmr::F_2_5:
4093 llvm_unreachable(
"Unkown WinogradConv2DFmr");
4100static FailureOr<SmallVector<SmallVector<int64_t>>>
4103 for (
auto map : maps) {
4104 AffineMapAttr attr = dyn_cast<AffineMapAttr>(map);
4108 for (
auto result : attr.getAffineMap().getResults()) {
4109 auto dim = dyn_cast<AffineDimExpr>(
result);
4112 pos.push_back(dim.getPosition());
4114 positions.push_back(pos);
4127 return indexingMaps;
4130bool MatmulOp::isDefaultIndexingMaps(Attribute attr) {
4131 ArrayAttr maps = dyn_cast<ArrayAttr>(attr);
4134 if (maps.size() != 3)
4139 return (*positions)[0] == SmallVector<int64_t>{0, 2} &&
4140 (*positions)[1] == SmallVector<int64_t>{2, 1} &&
4141 (*positions)[2] == SmallVector<int64_t>{0, 1};
4144SmallVector<utils::IteratorType> MatmulOp::getIteratorTypesArray() {
4145 return SmallVector<utils::IteratorType>{utils::IteratorType::parallel,
4146 utils::IteratorType::parallel,
4147 utils::IteratorType::reduction};
4150unsigned MatmulOp::getNumRegionArgs() {
return 3; }
4152std::string MatmulOp::getLibraryCallName() {
4156bool MatmulOp::hasDynamicIndexingMaps() {
return true; }
4160bool MatmulOp::hasUserDefinedMaps() {
4161 SmallVector<AffineMap, 3> defaultMaps =
4163 SmallVector<AffineMap, 3> explicitMaps = getIndexingMapsArray();
4164 return defaultMaps != explicitMaps;
4169void MatmulOp::regionBuilder(ImplicitLocOpBuilder &
b,
Block &block,
4170 ArrayRef<NamedAttribute> attrs,
4173 emitError() <<
"MatmulOp regionBuilder expects 3 args, got "
4178 "MatmulOp regionBuilder expects 3 args");
4179 RegionBuilderHelper helper(
b, block);
4180 SmallVector<Value> yields;
4182 TypeFn castVal = TypeFn::cast_signed;
4183 const auto *castIter = llvm::find_if(attrs, [&](
const NamedAttribute &attr) {
4184 return attr.
getName() ==
"cast";
4186 if (castIter != attrs.end()) {
4187 if (
auto attr = llvm::dyn_cast<TypeFnAttr>(castIter->getValue()))
4195 Value value3 = helper.buildBinaryFn(BinaryFn::mul, value1, value2,
emitError);
4196 if (!value1 || !value2 || !value3)
4198 Value value4 = helper.buildBinaryFn(BinaryFn::add, block.
getArgument(2),
4202 yields.push_back(value4);
4203 helper.yieldOutputs(yields);
4213bool MatmulOp::isValidLhsRhsBroadcastMap(AffineMap bcastMap) {
4214 assert(bcastMap.
getNumResults() == 1 &&
"Expected single result dim expr.");
4215 AffineExpr expr = bcastMap.
getResult(0);
4225 ArrayAttr arrayAttr;
4229 if (llvm::any_of(arrayAttr,
4230 [](
auto elt) {
return !dyn_cast<AffineMapAttr>(elt); }))
4232 <<
"element of indexing_maps array is not an affine_map";
4239 if (failed(indexingMapsAttr))
4242 if (*indexingMapsAttr ==
nullptr) {
4243 auto indexingMapAttrs = llvm::map_to_vector(
4244 MatmulOp::getDefaultIndexingMaps(parser.
getContext()),
4249 result.addAttribute(
"indexing_maps", *indexingMapsAttr);
4251 MatmulOp::getRegionBuilder());
4254void MatmulOp::print(OpAsmPrinter &p) {
4255 SmallVector<Attribute, 3> indexingMaps = llvm::map_to_vector<3>(
4256 MatmulOp::getDefaultIndexingMaps(
getContext()),
4257 [](AffineMap map) -> Attribute {
return AffineMapAttr::get(map); });
4258 if (!llvm::equal(getIndexingMaps(), indexingMaps))
4259 p <<
" indexing_maps = " << llvm::interleaved_array(getIndexingMaps());
4261 std::array<StringRef, 3> elidedAttrs = {
4262 "operandSegmentSizes",
"linalg.memoized_indexing_maps",
"indexing_maps"};
4268LogicalResult MatmulOp::verify() {
4270 if (!hasUserDefinedMaps())
4273 for (
unsigned opIndex = 0; opIndex < 2; opIndex++) {
4280LogicalResult MatmulOp::fold(FoldAdaptor, SmallVectorImpl<OpFoldResult> &) {
4284void MatmulOp::getEffects(
4285 SmallVectorImpl<SideEffects::EffectInstance<MemoryEffects::Effect>>
4287 if (hasPureTensorSemantics())
4296SmallVector<AffineMap>
4297MatmulTransposeAOp::getDefaultIndexingMaps(OpBuilder &builder) {
4298 AffineExpr d0, d1, d2;
4304 return {mapLHS, mapRHS, mapOut};
4308 ArrayAttr maps = dyn_cast<ArrayAttr>(attr);
4311 if (maps.size() != 3)
4314 if (failed(positions))
4326 MatmulOp::getRegionBuilder(), getDefaultIndexingMaps(builder));
4334 build(builder, state, inputs, outputs, attributes);
4335 auto res = dyn_cast<MatmulTransposeAOp>(builder.
create(state));
4336 assert(res &&
"builder didn't return the right type");
4346 MatmulOp::getRegionBuilder(), getDefaultIndexingMaps(builder));
4355 build(builder, state, resultTensorTypes, inputs, outputs, attributes);
4356 auto res = dyn_cast<MatmulTransposeAOp>(builder.
create(state));
4357 assert(res &&
"builder didn't return the right type");
4367 result.addAttribute(
"cast", cast);
4369 MatmulOp::getRegionBuilder(), getDefaultIndexingMaps(builder));
4378 build(builder, state, resultTensorTypes, inputs, outputs, cast, attributes);
4379 auto res = dyn_cast<MatmulTransposeAOp>(builder.
create(state));
4380 assert(res &&
"builder didn't return the right type");
4385 auto matmulOp = dyn_cast_or_null<linalg::MatmulOp>(op);
4387 matmulOp.getIndexingMapsAttr());
4391MatmulTransposeBOp::getDefaultIndexingMaps(
OpBuilder &builder) {
4398 return {mapLHS, mapRHS, mapOut};
4402 ArrayAttr maps = dyn_cast<ArrayAttr>(attr);
4405 if (maps.size() != 3)
4408 if (failed(positions))
4420 MatmulOp::getRegionBuilder(), getDefaultIndexingMaps(builder));
4428 build(builder, state, inputs, outputs, attributes);
4429 auto res = dyn_cast<MatmulTransposeBOp>(builder.
create(state));
4430 assert(res &&
"builder didn't return the right type");
4440 MatmulOp::getRegionBuilder(), getDefaultIndexingMaps(builder));
4449 build(builder, state, resultTensorTypes, inputs, outputs, attributes);
4450 auto res = dyn_cast<MatmulTransposeBOp>(builder.
create(state));
4451 assert(res &&
"builder didn't return the right type");
4461 result.addAttribute(
"cast", cast);
4463 MatmulOp::getRegionBuilder(), getDefaultIndexingMaps(builder));
4472 build(builder, state, resultTensorTypes, inputs, outputs, cast, attributes);
4473 auto res = dyn_cast<MatmulTransposeBOp>(builder.
create(state));
4474 assert(res &&
"builder didn't return the right type");
4479 auto matmulOp = dyn_cast_or_null<linalg::MatmulOp>(op);
4481 matmulOp.getIndexingMapsAttr());
4485BatchMatmulTransposeAOp::getDefaultIndexingMaps(
OpBuilder &builder) {
4492 return {mapLHS, mapRHS, mapOut};
4496 ArrayAttr maps = dyn_cast<ArrayAttr>(attr);
4499 if (maps.size() != 3)
4502 if (failed(positions))
4513 BatchMatmulOp::getRegionBuilder(),
4514 getDefaultIndexingMaps(builder));
4522 build(builder, state, inputs, outputs, attributes);
4523 auto res = dyn_cast<BatchMatmulTransposeAOp>(builder.
create(state));
4524 assert(res &&
"builder didn't return the right type");
4533 BatchMatmulOp::getRegionBuilder(),
4534 getDefaultIndexingMaps(builder));
4543 build(builder, state, resultTensorTypes, inputs, outputs, attributes);
4544 auto res = dyn_cast<BatchMatmulTransposeAOp>(builder.
create(state));
4545 assert(res &&
"builder didn't return the right type");
4553 result.addAttribute(
"cast", cast);
4555 BatchMatmulOp::getRegionBuilder(),
4556 getDefaultIndexingMaps(builder));
4565 build(builder, state, resultTensorTypes, inputs, outputs, cast, attributes);
4566 auto res = dyn_cast<BatchMatmulTransposeAOp>(builder.
create(state));
4567 assert(res &&
"builder didn't return the right type");
4572 auto matmulOp = dyn_cast_or_null<linalg::BatchMatmulOp>(op);
4574 matmulOp.getIndexingMapsAttr());
4578BatchMatmulTransposeBOp::getDefaultIndexingMaps(
OpBuilder &builder) {
4585 return {mapLHS, mapRHS, mapOut};
4589 ArrayAttr maps = dyn_cast<ArrayAttr>(attr);
4592 if (maps.size() != 3)
4595 if (failed(positions))
4606 BatchMatmulOp::getRegionBuilder(),
4607 getDefaultIndexingMaps(builder));
4615 build(builder, state, inputs, outputs, attributes);
4616 auto res = dyn_cast<BatchMatmulTransposeBOp>(builder.
create(state));
4617 assert(res &&
"builder didn't return the right type");
4626 BatchMatmulOp::getRegionBuilder(),
4627 getDefaultIndexingMaps(builder));
4636 build(builder, state, resultTensorTypes, inputs, outputs, attributes);
4637 auto res = dyn_cast<BatchMatmulTransposeBOp>(builder.
create(state));
4638 assert(res &&
"builder didn't return the right type");
4646 result.addAttribute(
"cast", cast);
4648 BatchMatmulOp::getRegionBuilder(),
4649 getDefaultIndexingMaps(builder));
4658 build(builder, state, resultTensorTypes, inputs, outputs, cast, attributes);
4659 auto res = dyn_cast<BatchMatmulTransposeBOp>(builder.
create(state));
4660 assert(res &&
"builder didn't return the right type");
4665 auto matmulOp = dyn_cast_or_null<linalg::BatchMatmulOp>(op);
4667 matmulOp.getIndexingMapsAttr());
4675 AffineMap outAffineMap = getIndexingMapsArray().pop_back_val();
4686 auto dimExpr = dyn_cast<AffineDimExpr>(
result);
4687 assert(dimExpr &&
"affine_map is a projected permutation");
4688 dimsInOutput[dimExpr.getPosition()] =
true;
4692 for (
auto dimOccursInOutput : dimsInOutput)
4693 iteratorTypes.push_back(dimOccursInOutput ? utils::IteratorType::parallel
4694 : utils::IteratorType::reduction);
4696 return iteratorTypes;
4699unsigned ContractOp::getNumRegionArgs() {
return 3; }
4702void ContractOp::regionBuilder(ImplicitLocOpBuilder &
b,
Block &block,
4703 ArrayRef<NamedAttribute> attrs,
4706 emitError() <<
"ContractOp regionBuilder expects 3 args, got "
4711 "ContractOp regionBuilder expects 3 args");
4712 RegionBuilderHelper helper(
b, block);
4714 TypeFn castSignedness = TypeFn::cast_signed;
4715 auto castIter = llvm::find_if(attrs, [&](
const NamedAttribute &attr) {
4716 return attr.
getName() ==
"cast";
4718 if (castIter != attrs.end()) {
4719 if (
auto attr = llvm::dyn_cast<TypeFnAttr>(castIter->getValue()))
4725 Value lhsAtOutType =
4726 helper.buildTypeFn(castSignedness, outType, block.
getArgument(0));
4727 Value rhsAtOutType =
4728 helper.buildTypeFn(castSignedness, outType, block.
getArgument(1));
4729 Value productAtOutType = helper.buildBinaryFn(BinaryFn::mul, lhsAtOutType,
4731 if (!productAtOutType)
4737 helper.yieldOutputs({
result});
4740ParseResult ContractOp::parse(OpAsmParser &parser, OperationState &
result) {
4742 if (
failed(indexingMapsAttr) || *indexingMapsAttr ==
nullptr)
4744 "expected 'indexing_maps' attribute");
4745 result.addAttribute(
"indexing_maps", *indexingMapsAttr);
4751void ContractOp::print(OpAsmPrinter &p) {
4752 p <<
" indexing_maps = " << llvm::interleaved_array(getIndexingMaps());
4754 p, getOperation(), getInputs(), getOutputs(),
4755 {
"indexing_maps",
"operandSegmentSizes"});
4766 bool isInput,
int &iterationSpaceDims,
4771 return emitError() <<
"provided affine_map is not a projected permutation";
4773 if (
auto shapedType = dyn_cast<ShapedType>(operandType)) {
4776 <<
"ranks of shaped operand and results of corresponding "
4777 "affine_map differ";
4780 <<
"affine_map specifies shaped access while operand has "
4784 if (iterationSpaceDims == -1) {
4788 }
else if (iterationSpaceDims != (
int)affineMap.
getNumDims()) {
4789 return emitError() <<
"iteration spaces of provided affine_maps differ";
4794 auto affineDimExpr = dyn_cast<AffineDimExpr>(affineExpr);
4796 llvm_unreachable(
"affine_map is a projected permutation");
4799 inOccurrences[affineDimExpr.getPosition()] += 1;
4801 outOccurrences[affineDimExpr.getPosition()] += 1;
4817 bool hasContractingDim =
false;
4818 for (
size_t dimIndex = 0; dimIndex < iterationSpaceDims; dimIndex++) {
4819 size_t inOccCount = inOccurrences[dimIndex];
4820 size_t outOccCount = outOccurrences[dimIndex];
4823 hasContractingDim |= inOccCount == 2 && outOccCount == 0;
4825 if (inOccCount == 0 && outOccCount == 0)
4826 return emitError() <<
"iteration space dim at index " << dimIndex
4827 <<
" not used to access any operand";
4838 if (inOccCount == 1 && outOccCount != 1)
4840 <<
"iteration space dim at index " << dimIndex
4841 <<
" is neither a contracting dim nor of parallel iteration type";
4844 if (!hasContractingDim)
4846 <<
"'indexing_maps' do not specify a contracting dimension";
4851LogicalResult ContractOp::verify() {
4852 int iterationSpaceDims = -1;
4860 for (
auto &&[affineMap, operandType, isInput] :
4861 llvm::zip(getIndexingMapsArray(), getOperandTypes(),
4864 affineMap, operandType, isInput, iterationSpaceDims, inOccurrences,
4865 outOccurrences, [&]() {
return emitError(); })))
4870 inOccurrences, outOccurrences,
4874LogicalResult ContractOp::fold(FoldAdaptor, SmallVectorImpl<OpFoldResult> &) {
4878void ContractOp::getEffects(
4879 SmallVectorImpl<SideEffects::EffectInstance<MemoryEffects::Effect>>
4881 if (hasPureTensorSemantics())
4893SmallVector<AffineMap>
4894BatchMatmulOp::getDefaultIndexingMaps(MLIRContext *context) {
4895 AffineExpr d0, d1, d2, d3;
4896 SmallVector<AffineMap> indexingMaps;
4898 indexingMaps.push_back(
AffineMap::get(4, 0, {d0, d1, d3}, context));
4899 indexingMaps.push_back(
AffineMap::get(4, 0, {d0, d3, d2}, context));
4900 indexingMaps.push_back(
AffineMap::get(4, 0, {d0, d1, d2}, context));
4901 return indexingMaps;
4904bool BatchMatmulOp::isDefaultIndexingMaps(Attribute attr) {
4905 ArrayAttr maps = dyn_cast<ArrayAttr>(attr);
4908 if (maps.size() != 3)
4913 return (*positions)[0] == SmallVector<int64_t>{0, 1, 3} &&
4914 (*positions)[1] == SmallVector<int64_t>{0, 3, 2} &&
4915 (*positions)[2] == SmallVector<int64_t>{0, 1, 2};
4918SmallVector<utils::IteratorType> BatchMatmulOp::getIteratorTypesArray() {
4919 return SmallVector<utils::IteratorType>{
4920 utils::IteratorType::parallel, utils::IteratorType::parallel,
4921 utils::IteratorType::parallel, utils::IteratorType::reduction};
4924unsigned BatchMatmulOp::getNumRegionArgs() {
return 3; }
4926std::string BatchMatmulOp::getLibraryCallName() {
4932bool BatchMatmulOp::hasUserDefinedMaps() {
4933 SmallVector<AffineMap, 3> defaultMaps =
4935 SmallVector<AffineMap, 3> explicitMaps = getIndexingMapsArray();
4936 return defaultMaps != explicitMaps;
4946bool BatchMatmulOp::isValidLhsRhsBroadcastMap(AffineMap bcastMap,
bool isLHS) {
4948 "Expected less than 3 result dim expr.");
4949 bool isValid =
false;
4950 enum Indices { batchPos, mPos, nPos, kPos };
4952 AffineExpr expr = bcastMap.
getResult(0);
4955 AffineExpr expr0 = bcastMap.
getResult(0);
4956 AffineExpr expr1 = bcastMap.
getResult(1);
4961 : ((expr0.isFunctionOfDim(batchPos) &&
4962 expr1.isFunctionOfDim(kPos)) ||
4963 (expr0.isFunctionOfDim(kPos) && expr1.isFunctionOfDim(nPos)));
4968void BatchMatmulOp::regionBuilder(
4969 ImplicitLocOpBuilder &
b,
Block &block, ArrayRef<NamedAttribute> attrs,
4972 emitError() <<
"BatchMatmulOp regionBuilder expects 3 args, got "
4977 "BatchMatmulOp regionBuilder expects 3 args");
4978 RegionBuilderHelper helper(
b, block);
4979 SmallVector<Value> yields;
4981 TypeFn castVal = TypeFn::cast_signed;
4982 auto castIter = llvm::find_if(attrs, [&](
const NamedAttribute &attr) {
4983 return attr.
getName() ==
"cast";
4985 if (castIter != attrs.end()) {
4986 if (
auto attr = llvm::dyn_cast<TypeFnAttr>(castIter->getValue()))
4991 Value castValA = helper.buildTypeFn(castVal, toType, block.
getArgument(0));
4992 Value castValB = helper.buildTypeFn(castVal, toType, block.
getArgument(1));
4994 helper.buildBinaryFn(BinaryFn::mul, castValA, castValB,
emitError);
4995 if (!castValA || !castValB || !mulVal)
4997 Value addVal = helper.buildBinaryFn(BinaryFn::add, block.
getArgument(2),
5001 yields.push_back(addVal);
5002 helper.yieldOutputs(yields);
5005ParseResult BatchMatmulOp::parse(OpAsmParser &parser, OperationState &
result) {
5006 SmallVector<Attribute, 3> indexingMapsAttr;
5018 if (!isa<AffineMapAttr>(mapAttr)) {
5020 "expected affine map attribute");
5022 indexingMapsAttr.push_back(mapAttr);
5032 if (indexingMapsAttr.empty()) {
5033 indexingMapsAttr = llvm::map_to_vector(
5034 BatchMatmulOp::getDefaultIndexingMaps(parser.
getContext()),
5035 [](AffineMap map) -> Attribute { return AffineMapAttr::get(map); });
5037 result.addAttribute(
"indexing_maps",
5040 return ::parseNamedStructuredOp(parser,
result,
5041 BatchMatmulOp::getNumRegionArgs(),
5042 BatchMatmulOp::getRegionBuilder());
5045void BatchMatmulOp::print(OpAsmPrinter &p) {
5046 SmallVector<Attribute, 3> indexingMaps = llvm::map_to_vector<3>(
5047 BatchMatmulOp::getDefaultIndexingMaps(
getContext()),
5048 [](AffineMap map) -> Attribute {
return AffineMapAttr::get(map); });
5049 if (!llvm::equal(getIndexingMaps(), indexingMaps))
5050 p <<
" indexing_maps = " << llvm::interleaved_array(getIndexingMaps());
5052 std::array<StringRef, 3> elidedAttrs = {
5053 "operandSegmentSizes",
"linalg.memoized_indexing_maps",
"indexing_maps"};
5059LogicalResult BatchMatmulOp::verify() {
5062 if (!hasUserDefinedMaps())
5065 for (
unsigned opIndex = 0; opIndex < 3; opIndex++) {
5072LogicalResult BatchMatmulOp::fold(FoldAdaptor,
5073 SmallVectorImpl<OpFoldResult> &) {
5077void BatchMatmulOp::getEffects(
5078 SmallVectorImpl<SideEffects::EffectInstance<MemoryEffects::Effect>>
5080 if (hasPureTensorSemantics())
5094unsigned getArityGroupAsUInt(ElementwiseArityGroup arityGroup) {
5095 return static_cast<unsigned>(arityGroup);
5100 constexpr int lastUnary =
static_cast<int>(ElementwiseCaseLimits::LastUnary);
5101 constexpr int lastBinary =
5102 static_cast<int>(ElementwiseCaseLimits::LastBinary);
5103 constexpr int lastTernary =
5104 static_cast<int>(ElementwiseCaseLimits::LastTernary);
5106 int val =
static_cast<int>(kind);
5109 if (val < lastUnary) {
5110 result.arityGroup = ElementwiseArityGroup::Unary;
5111 result.kind.unaryFn =
static_cast<UnaryFn
>(val);
5114 if (val < lastBinary) {
5115 result.arityGroup = ElementwiseArityGroup::Binary;
5116 result.kind.binaryFn =
static_cast<BinaryFn
>(val - lastUnary);
5119 if (val >= lastTernary) {
5120 llvm_unreachable(
"unhandled ElementwiseFn");
5122 result.arityGroup = ElementwiseArityGroup::Ternary;
5123 result.kind.ternaryFn =
static_cast<TernaryFn
>(val - lastBinary);
5128 auto rank = getResultRank();
5133ElementwiseOp::getDefaultIndexingMaps(
unsigned numMaps,
unsigned numDims,
5139ParseResult ElementwiseOp::parse(OpAsmParser &parser, OperationState &
result) {
5142 ElementwiseKindAttr kindAttr;
5143 mlir::linalg::ElementwiseKind elemwiseKindVal;
5146 elemwiseKindVal = kindAttr.getValue();
5147 result.addAttribute(
"kind", kindAttr);
5150 SmallVector<Attribute, 3> indexingMapsAttr;
5160 if (!isa<AffineMapAttr>(mapAttr))
5162 "expected affine map attribute");
5163 indexingMapsAttr.push_back(mapAttr);
5174 getArityGroupAsUInt(arityGroupAndKind.arityGroup) + 1 ;
5176 ElementwiseOp::getRegionBuilder())) {
5178 "unable to parse elemwise op");
5182 if (indexingMapsAttr.empty()) {
5185 auto resultType =
result.operands[
result.operands.size() - 1].getType();
5186 auto shapedType = llvm::dyn_cast<ShapedType>(resultType);
5189 "return type needs to be shaped type");
5190 auto numDims = shapedType.getRank();
5191 indexingMapsAttr = llvm::map_to_vector(
5192 ElementwiseOp::getDefaultIndexingMaps(numRegionArgs, numDims,
5194 [](AffineMap map) -> Attribute { return AffineMapAttr::get(map); });
5197 result.addAttribute(
"indexing_maps",
5202void ElementwiseOp::print(OpAsmPrinter &p) {
5205 SmallVector<StringRef, 3> elidedAttrs = {
"operandSegmentSizes",
"kind",
5209 unsigned numDims = getResultRank();
5211 SmallVector<Attribute, 3> indexingMaps = llvm::map_to_vector<3>(
5212 ElementwiseOp::getDefaultIndexingMaps(arity + 1 , numDims,
5214 [](AffineMap map) -> Attribute {
return AffineMapAttr::get(map); });
5216 if (!llvm::equal(getIndexingMaps(), indexingMaps))
5217 p <<
" indexing_maps = " << llvm::interleaved_array(getIndexingMaps());
5225void ElementwiseOp::regionBuilder(
5226 ImplicitLocOpBuilder &
b,
Block &block, ArrayRef<NamedAttribute> attrs,
5228 std::optional<ElementwiseKind> elemwiseKind;
5229 for (
auto attr : attrs) {
5230 if (attr.
getName() ==
"kind") {
5231 auto kindAttr = dyn_cast<ElementwiseKindAttr>(attr.
getValue());
5234 emitError() <<
"'kind' must be an ElementwiseKindAttr";
5237 elemwiseKind = kindAttr.getValue();
5242 if (!elemwiseKind) {
5244 emitError() <<
"missing required 'kind' attribute";
5250 auto kind = groupAndKind.
kind;
5252 getArityGroupAsUInt(arityGroup) + 1 ) {
5253 emitError() <<
"Elementwise regionBuilder expects "
5254 << (getArityGroupAsUInt(arityGroup) + 1) <<
" args, got "
5259 getArityGroupAsUInt(arityGroup) + 1
5260 &&
"Elementwise regionBuilder number of block args mismatch");
5262 RegionBuilderHelper helper(
b, block);
5263 SmallVector<Value> yields;
5266 if (arityGroup == ElementwiseArityGroup::Unary) {
5269 }
else if (arityGroup == ElementwiseArityGroup::Binary) {
5273 }
else if (arityGroup == ElementwiseArityGroup::Ternary) {
5278 assert(
false &&
"found unhandled category in elemwise");
5281 yields.push_back(
result);
5282 helper.yieldOutputs(yields);
5285LogicalResult ElementwiseOp::fold(FoldAdaptor,
5286 SmallVectorImpl<OpFoldResult> &) {
5290void ElementwiseOp::getEffects(
5291 SmallVectorImpl<SideEffects::EffectInstance<MemoryEffects::Effect>>
5293 if (hasPureTensorSemantics())
5306template <
typename OpTy,
typename>
5309 ShapedType packedType = (std::is_same<OpTy, PackOp>::value)
5310 ? packOrUnPack.getDestType()
5311 : packOrUnPack.getSourceType();
5312 ShapedType unpackedType = (std::is_same<OpTy, PackOp>::value)
5313 ? packOrUnPack.getSourceType()
5314 : packOrUnPack.getDestType();
5316 packedType.getShape().take_front(unpackedType.getRank()));
5317 if (!packOrUnPack.getOuterDimsPerm().empty()) {
5338 for (
auto it : llvm::zip(cast<ShapedType>(newPackedTy)
5340 .take_back(mixedTiles.size()),
5342 int64_t dimSize = std::get<0>(it);
5343 if (dimSize == ShapedType::kDynamic) {
5344 newMixedTileSizes.push_back(std::get<1>(it));
5347 newMixedTileSizes.push_back(rewriter.
getIndexAttr(dimSize));
5350 return newMixedTileSizes;
5353template <
typename OpTy>
5357 static_assert(llvm::is_one_of<OpTy, PackOp, UnPackOp>::value,
5358 "applies to only pack or unpack operations");
5359 int64_t destRank = op.getDestRank();
5361 for (
auto dim : llvm::seq<int64_t>(0, destRank))
5362 reifiedReturnShapes[0][dim] =
5367template <
typename OpTy>
5369 static_assert(llvm::is_one_of<OpTy, PackOp, UnPackOp>::value,
5370 "applies to only pack or unpack operations");
5374 assert(tiles.size() == dimsToTile.size() &&
5375 "tiles must match indices of dimension to block");
5377 for (
auto i : llvm::seq<int64_t>(0, dimsToTile.size()))
5378 dimAndTileMapping[dimsToTile[i]] = tiles[i];
5379 return dimAndTileMapping;
5382template <
typename OpTy>
5384 static_assert(llvm::is_one_of<OpTy, PackOp, UnPackOp>::value,
5385 "applies to only pack or unpack operations");
5388 unsigned dynamicValIndex = 0;
5389 for (
int64_t staticTile : op.getStaticInnerTiles()) {
5390 if (ShapedType::isStatic(staticTile))
5393 mixedInnerTiles.push_back(op.getInnerTiles()[dynamicValIndex++]);
5395 return mixedInnerTiles;
5398template <
typename OpTy>
5400 static_assert(llvm::is_one_of<OpTy, PackOp, UnPackOp>::value,
5401 "applies to only pack or unpack operations");
5414 size_t dimsPosSize = dimsPos.size();
5415 if (dimsPosSize > rank)
5418 if (dimsPosSize != uniqued.size())
5420 return llvm::any_of(dimsPos, [rank](
int64_t dimPos) {
5421 return dimPos < 0 || dimPos >=
static_cast<int64_t>(rank);
5425template <
typename OpTy>
5427 static_assert(llvm::is_one_of<OpTy, PackOp, UnPackOp>::value,
5428 "applies to only pack or unpack operations");
5429 Operation *op = packOrUnPack.getOperation();
5439 if (!packOrUnPack.getSourceType().hasRank() ||
5440 !packOrUnPack.getDestType().hasRank())
5441 return op->
emitError(
"expected both source and destination to have rank");
5444 if (!packOrUnPack.hasPureBufferSemantics() &&
5445 !packOrUnPack.hasPureTensorSemantics())
5446 return op->
emitError(
"mixing tensor and buffer semantics is not allowed");
5447 const unsigned numResults = packOrUnPack.getNumResults();
5448 if (packOrUnPack.hasPureTensorSemantics() && numResults != 1)
5449 return op->
emitError(
"expected 1 result, got ") << numResults;
5450 if (packOrUnPack.hasPureBufferSemantics() && numResults != 0)
5451 return op->
emitError(
"expected 0 results, got ") << numResults;
5455 if (hasZeros(mixedTiles))
5456 return op->
emitError(
"invalid zero tile factor");
5459 ShapedType unpackedType = (std::is_same<OpTy, PackOp>::value)
5460 ? packOrUnPack.getSourceType()
5461 : packOrUnPack.getDestType();
5462 size_t unpackedRank = unpackedType.getRank();
5466 return op->
emitError(
"invalid inner_dims_pos vector");
5468 return op->
emitError(
"invalid outer_dims_perm vector");
5469 if (!outerDimPerm.empty() && outerDimPerm.size() != unpackedRank)
5470 return op->
emitError(
"outer_dims_perm must be a permutation or empty");
5474 if (mixedTiles.size() > unpackedRank) {
5475 return op->
emitError(
"tiling factors must be less than or equal to the "
5476 "input rank for pack or output rank for unpack");
5478 if (mixedTiles.size() != innerDimsPos.size()) {
5480 "tiling factors must equal the number of dimensions to tile");
5483 ShapedType packedType = (std::is_same<OpTy, PackOp>::value)
5484 ? packOrUnPack.getDestType()
5485 : packOrUnPack.getSourceType();
5486 size_t packedRank = packedType.getRank();
5488 size_t expectedPackedRank = unpackedRank + mixedTiles.size();
5489 if (expectedPackedRank != packedRank) {
5491 "packed rank != (unpacked rank + num tiling factors), got ")
5492 << packedRank <<
" != " << expectedPackedRank;
5499 unpackedType.getShape(), packOrUnPack.getStaticTiles(),
5500 packOrUnPack.getInnerDimsPos(), packOrUnPack.getOuterDimsPerm());
5501 for (
auto it : llvm::enumerate(llvm::zip(
5502 packedType.getShape().take_back(mixedTiles.size()), mixedTiles))) {
5503 int64_t dimSize = std::get<0>(it.value());
5505 llvm::dyn_cast_if_present<Attribute>(std::get<1>(it.value()))) {
5506 IntegerAttr intAttr = dyn_cast_or_null<IntegerAttr>(attr);
5507 int64_t staticTileSize = intAttr.getValue().getSExtValue();
5508 if (dimSize != staticTileSize)
5510 "mismatch in inner tile sizes specified and shaped of "
5511 "tiled dimension in the packed type at index ")
5512 << it.index() <<
": got " << dimSize <<
" != " << staticTileSize;
5513 }
else if (!ShapedType::isDynamic(dimSize)) {
5514 return op->
emitError(
"mismatch in inner tile sizes specified at index ")
5515 << it.index() <<
": got static shape " << dimSize
5516 <<
" but dynamic tile size";
5521 auto elementType = unpackedType.getElementType();
5522 Type expectedType, actualType;
5523 if (packOrUnPack.hasPureTensorSemantics()) {
5524 expectedType = RankedTensorType::get(expectedPackedShape, elementType);
5525 actualType = RankedTensorType::get(packedType.getShape(), elementType);
5527 expectedType = MemRefType::get(expectedPackedShape, elementType);
5528 actualType = MemRefType::get(packedType.getShape(), elementType);
5531 << expectedType <<
" for the packed domain value, got "
5544struct PackOrUnPackTransposeResult {
5551template <
typename OpTy>
5552static PackOrUnPackTransposeResult
5556 static_assert(llvm::is_one_of<OpTy, PackOp, UnPackOp>::value,
5557 "applies to only pack or unpack operations");
5558 assert((!innerPermutation.empty() || !outerPermutation.empty()) &&
5559 "some permutation must be non-empty");
5560 PackOrUnPackTransposeResult metadata;
5561 metadata.innerDimsPos =
5563 metadata.innerTiles =
5565 int64_t numOuterDims = std::is_same<OpTy, PackOp>::value
5566 ? packOrUnPackOp.getSourceRank()
5567 : packOrUnPackOp.getDestRank();
5568 metadata.outerDimsPerm =
5569 packOrUnPackOp.getOuterDimsPerm().empty()
5570 ? llvm::to_vector(llvm::seq<int64_t>(0, numOuterDims))
5572 if (!innerPermutation.empty()) {
5573 assert(innerPermutation.size() == metadata.innerDimsPos.size() &&
5575 "invalid inner permutation");
5579 if (!outerPermutation.empty()) {
5580 assert(outerPermutation.size() == metadata.outerDimsPerm.size() &&
5582 "invalid outer permutation");
5593 if (!getResults().empty())
5594 setNameFn(getResult(),
"pack");
5604 Type sourceType, destType, resultType;
5621 SmallVector<int64_t> outerDimsPermVec;
5624 if (parser.parseInteger(value))
5626 outerDimsPermVec.push_back(value);
5636 SmallVector<int64_t> innerDimsPosVec;
5639 if (parser.parseInteger(value))
5641 innerDimsPosVec.push_back(value);
5653 for (
auto val : staticTilesAttr.
asArrayRef())
5654 staticTiles.push_back(val);
5671 bool isMemRef = llvm::isa<MemRefType>(sourceType);
5674 "pack/unpack requires '->' and destination type");
5678 resultType = destType;
5684 if (!paddingValue.empty() &&
5689 if (!dynamicTiles.empty() &&
5694 result.addAttribute(
"static_inner_tiles",
5696 result.addAttribute(
"inner_dims_pos", innerDimsPos);
5698 result.addAttribute(
"outer_dims_perm", outerDimsPerm);
5700 SmallVector<int32_t> segmentSizes = {
5701 1, 1,
static_cast<int32_t
>(paddingValue.size()),
5702 static_cast<int32_t
>(dynamicTiles.size())};
5703 result.addAttribute(
"operandSegmentSizes",
5707 result.addTypes(resultType);
5712void PackOp::print(OpAsmPrinter &p) {
5713 p <<
" " << getSource();
5715 if (getPaddingValue()) {
5716 p <<
" padding_value(" << getPaddingValue() <<
" : "
5717 << getPaddingValue().getType() <<
")";
5720 if (!getOuterDimsPerm().empty()) {
5721 p <<
" outer_dims_perm = [";
5722 llvm::interleaveComma(getOuterDimsPerm(), p);
5726 p <<
" inner_dims_pos = [";
5727 llvm::interleaveComma(getInnerDimsPos(), p);
5730 p <<
" inner_tiles = ";
5733 p <<
" into " << getDest();
5736 {
"static_inner_tiles",
"inner_dims_pos",
5737 "outer_dims_perm",
"operandSegmentSizes"});
5739 p <<
" : " << getSource().getType();
5740 p <<
" -> " << getDest().getType();
5743void PackOp::build(OpBuilder &builder, OperationState &state, Value source,
5744 Value dest, ArrayRef<int64_t> innerDimsPos,
5745 ArrayRef<OpFoldResult> innerTiles,
5746 std::optional<Value> paddingValue,
5747 ArrayRef<int64_t> outerDimsPerm) {
5748 assert(innerDimsPos.size() == innerTiles.size() &&
5749 "number of tile sizes specified must match the specified number of "
5750 "original dimensions to be tiled");
5751 SmallVector<int64_t> staticTileSizes;
5752 SmallVector<Value> dynamicTileSizes;
5754 build(builder, state, dest.
getType(), source, dest,
5755 paddingValue ? *paddingValue :
nullptr,
5756 outerDimsPerm.empty() ?
nullptr
5763PackOp::reifyResultShapes(OpBuilder &builder,
5772SmallVector<OpFoldResult> PackOp::getMixedTiles() {
5776SmallVector<int64_t> PackOp::getStaticTiles() {
5780ArrayRef<int64_t> PackOp::getAllOuterDims() {
5781 ShapedType inputType = getSourceType();
5782 int64_t inputRank = inputType.getRank();
5783 return getDestType().getShape().take_front(inputRank);
5786SmallVector<int64_t> PackOp::getTiledOuterDims() {
5787 auto innerDimsPos = getInnerDimsPos();
5788 SmallVector<int64_t> outerDims(getAllOuterDims());
5789 SmallVector<int64_t> res;
5792 SmallVector<int64_t> outerDimPermInv(getOuterDimsPerm());
5794 if (!outerDimPermInv.empty())
5798 for (
auto index : innerDimsPos)
5799 res.push_back(outerDims[index]);
5804bool PackOp::requirePaddingValue(ArrayRef<int64_t> inputShape,
5805 ArrayRef<int64_t> innerDimsPos,
5806 ArrayRef<int64_t> outputShape,
5807 ArrayRef<int64_t> outerDimsPerm,
5808 ArrayRef<OpFoldResult> innerTiles) {
5809 SmallVector<int64_t> outputTileSizes(
5810 outputShape.take_front(inputShape.size()));
5811 if (!outerDimsPerm.empty()) {
5812 assert(outerDimsPerm.size() == outputTileSizes.size() &&
5813 "expected output and outer_dims_perm to have same size");
5817 for (
auto [pos, tileSize] : llvm::zip_equal(innerDimsPos, innerTiles)) {
5818 if (ShapedType::isDynamic(inputShape[pos]))
5821 if (!constantTile) {
5822 if (ShapedType::isStatic(outputTileSizes[pos]) &&
5823 (inputShape[pos] % outputTileSizes[pos] != 0))
5826 assert(*constantTile != 0 &&
"static tile size can't be zero");
5827 if (inputShape[pos] % (*constantTile) != 0) {
5835bool PackOp::requirePaddingValueStrict(ArrayRef<int64_t> inputShape,
5836 ArrayRef<int64_t> innerDimsPos,
5837 ArrayRef<int64_t> outputShape,
5838 ArrayRef<int64_t> outerDimsPerm,
5839 ArrayRef<OpFoldResult> innerTiles) {
5840 SmallVector<int64_t> outputTileSizes(
5841 outputShape.take_front(inputShape.size()));
5842 if (!outerDimsPerm.empty()) {
5843 assert(outerDimsPerm.size() == outputTileSizes.size() &&
5844 "expected output and outer_dims_perm to have same size");
5848 for (
auto [pos, tileSize] : llvm::zip_equal(innerDimsPos, innerTiles)) {
5849 if (ShapedType::isDynamic(inputShape[pos]) ||
5850 ShapedType::isDynamic(outputTileSizes[pos]))
5855 assert(*constantTile != 0 &&
"static tile size can't be zero");
5856 if (inputShape[pos] % (*constantTile) != 0)
5862LogicalResult PackOp::verify() {
5869 auto paddingValue = getPaddingValue();
5872 return emitOpError(
"expected padding_value has ")
5873 << getSourceType().getElementType()
5874 <<
" but got: " << paddingValue.getType();
5877 if (!paddingValue &&
5878 requirePaddingValue(getSourceType().
getShape(), getInnerDimsPos(),
5879 getDestType().
getShape(), getOuterDimsPerm(),
5882 "invalid tile factor or output size provided. Only full tiles are "
5883 "supported when padding_value is not set");
5890static SmallVector<int64_t>
5893 for (
auto o : ofrs) {
5895 if (llvm::dyn_cast_if_present<Value>(o))
5896 result.push_back(ShapedType::kDynamic);
5908 for (
auto tiledDim : llvm::enumerate(llvm::to_vector(innerDimsPos))) {
5909 if (ShapedType::isDynamic(resultShape[tiledDim.value()]))
5911 if (ShapedType::isDynamic(innerTileSizes[tiledDim.index()])) {
5912 resultShape[tiledDim.value()] = ShapedType::kDynamic;
5915 resultShape[tiledDim.value()] = llvm::divideCeilSigned(
5916 resultShape[tiledDim.value()], innerTileSizes[tiledDim.index()]);
5920 if (!outerDimsPerm.empty())
5924 resultShape.append(innerTileSizes.begin(), innerTileSizes.end());
5928SmallVector<OpFoldResult> PackOp::getResultShape(
5929 OpBuilder &builder, Location loc, ArrayRef<OpFoldResult> sourceDims,
5930 ArrayRef<OpFoldResult> innerTileSizes, ArrayRef<int64_t> innerDimsPos,
5931 ArrayRef<int64_t> outerDimsPerm) {
5932 SmallVector<OpFoldResult> resultDims = llvm::to_vector(sourceDims);
5936 AffineExpr ceilDivExpr = s0.
ceilDiv(s1);
5937 for (
auto tiledDim : llvm::enumerate(llvm::to_vector(innerDimsPos))) {
5939 builder, loc, ceilDivExpr,
5940 {resultDims[tiledDim.value()], innerTileSizes[tiledDim.index()]});
5942 if (!outerDimsPerm.empty())
5944 resultDims.append(innerTileSizes.begin(), innerTileSizes.end());
5946 SmallVector<int64_t> resultTypeShape =
5949 innerDimsPos, outerDimsPerm);
5955 for (
unsigned i = 0; i < resultDims.size(); ++i) {
5956 if (ShapedType::isStatic(resultTypeShape[i]))
5965RankedTensorType PackOp::inferPackedTensorType(
5966 RankedTensorType sourceType, ArrayRef<int64_t> innerTileSizes,
5967 ArrayRef<int64_t> innerDimsPos, ArrayRef<int64_t> outerDimsPerm) {
5968 SmallVector<int64_t> resultShape = inferPackedShape(
5969 sourceType.getShape(), innerTileSizes, innerDimsPos, outerDimsPerm);
5970 return RankedTensorType::get(resultShape, sourceType.getElementType());
5973MemRefType PackOp::inferPackedMemRefType(MemRefType sourceType,
5974 ArrayRef<int64_t> innerTileSizes,
5975 ArrayRef<int64_t> innerDimsPos,
5976 ArrayRef<int64_t> outerDimsPerm) {
5977 SmallVector<int64_t> resultShape = inferPackedShape(
5978 sourceType.getShape(), innerTileSizes, innerDimsPos, outerDimsPerm);
5979 return MemRefType::get(resultShape, sourceType.getElementType());
5982Value PackOp::createDestinationTensor(OpBuilder &
b, Location loc, Value source,
5983 ArrayRef<OpFoldResult> innerTileSizes,
5984 ArrayRef<int64_t> innerDimsPos,
5985 ArrayRef<int64_t> outerDimsPerm) {
5986 AffineExpr dim0, dim1;
5988 auto ceilDiv = [&](OpFoldResult v1, OpFoldResult v2) -> OpFoldResult {
5993 SmallVector<OpFoldResult> mixedSizes;
5994 for (
auto [index, value] : llvm::enumerate(
5995 llvm::cast<RankedTensorType>(source.
getType()).getShape())) {
5996 if (ShapedType::isDynamic(value))
5997 mixedSizes.push_back(
5998 tensor::DimOp::create(
b, loc, source, index).getResult());
6000 mixedSizes.push_back(
b.getIndexAttr(value));
6002 for (
auto it : llvm::zip(innerDimsPos, innerTileSizes)) {
6003 int64_t dimPos = std::get<0>(it);
6004 OpFoldResult tileSize = std::get<1>(it);
6005 mixedSizes[dimPos] = ceilDiv(mixedSizes[dimPos], tileSize);
6007 if (!outerDimsPerm.empty())
6010 mixedSizes.append(innerTileSizes.begin(), innerTileSizes.end());
6011 auto elemType = llvm::cast<ShapedType>(source.
getType()).getElementType();
6012 return tensor::EmptyOp::create(
b, loc, mixedSizes, elemType);
6015PackOp PackOp::createTransposedClone(OpBuilder &
b, Location loc,
6016 ArrayRef<int64_t> innerPermutation,
6017 ArrayRef<int64_t> outerPermutation) {
6019 *
this, innerPermutation, outerPermutation);
6020 Value transposedDest =
6021 createDestinationTensor(
b, loc, getSource(), metadata.innerTiles,
6022 metadata.innerDimsPos, metadata.outerDimsPerm);
6023 return PackOp::create(
b, loc, getSource(), transposedDest,
6024 metadata.innerDimsPos, metadata.innerTiles,
6025 getPaddingValue(), metadata.outerDimsPerm);
6028template <
typename OpTy>
6033 if (op.hasPureTensorSemantics())
6036 for (
OpOperand &opOperand : op.getOperation()->getOpOperands()) {
6037 if (!llvm::isa<MemRefType>(opOperand.
get().
getType()))
6040 if (&opOperand == &op.getSourceMutable()) {
6044 }
else if (&opOperand == &op.getDestMutable()) {
6055void PackOp::getEffects(
6061void UnPackOp::getEffects(
6068template <
typename OpTy>
6070 static_assert(llvm::is_one_of<OpTy, PackOp, UnPackOp>::value,
6071 "applies to only pack or unpack operations");
6072 ShapedType packedType = (std::is_same<OpTy, PackOp>::value)
6074 : op.getSourceType();
6076 for (
auto [dimDest,
tile] : llvm::zip(
6077 packedType.getShape().take_back(mixedTiles.size()), mixedTiles)) {
6079 if (!constTileSize || ShapedType::isDynamic(dimDest))
6086 if (!hasPureTensorSemantics())
6088 if (getPaddingValue())
6103 if (packOp.getInnerDimsPos() != unPackOp.getInnerDimsPos())
6105 if (packOp.getOuterDimsPerm() == unPackOp.getOuterDimsPerm())
6117 auto packTiles = packOp.getMixedTiles();
6118 auto unPackTiles = unPackOp.getMixedTiles();
6119 if (packTiles.size() != unPackTiles.size())
6121 for (
size_t i = 0, e = packTiles.size(); i < e; i++) {
6130 auto srcType = op.getSourceType();
6131 auto innerDimsPos = op.getInnerDimsPos();
6132 auto innerTiles = op.getStaticInnerTiles();
6133 if (ShapedType::isDynamicShape(innerTiles))
6135 for (
auto [pos, tileSize] : llvm::zip_equal(innerDimsPos, innerTiles)) {
6136 if (srcType.isDynamicDim(pos) && tileSize != 1)
6139 return !PackOp::requirePaddingValue(
6140 srcType.getShape(), op.getInnerDimsPos(), op.getDestType().getShape(),
6141 op.getOuterDimsPerm(), op.getMixedTiles());
6148 bool changeNeeded =
false;
6149 srcShape.assign(packOp.getSourceType().getShape().begin(),
6150 packOp.getSourceType().getShape().end());
6151 destShape.assign(packOp.getDestType().getShape().begin(),
6152 packOp.getDestType().getShape().end());
6153 llvm::SmallSetVector<int64_t, 4> innerDims;
6154 innerDims.insert_range(packOp.getInnerDimsPos());
6156 if (!packOp.getOuterDimsPerm().empty())
6158 int srcRank = packOp.getSourceRank();
6159 for (
auto i : llvm::seq<int64_t>(0, srcRank)) {
6160 if (innerDims.contains(i))
6164 if (!inverseOuterDimsPerm.empty())
6165 destPos = inverseOuterDimsPerm[srcPos];
6166 if (ShapedType::isDynamic(srcShape[srcPos]) ==
6167 ShapedType::isDynamic(destShape[destPos])) {
6170 int64_t size = srcShape[srcPos];
6171 if (ShapedType::isDynamic(size))
6172 size = destShape[destPos];
6173 srcShape[srcPos] = size;
6174 destShape[destPos] = size;
6175 changeNeeded =
true;
6177 return changeNeeded;
6180LogicalResult PackOp::canonicalize(PackOp packOp,
PatternRewriter &rewriter) {
6184 if (!packOp.hasPureTensorSemantics())
6188 if (
auto unPackOp = packOp.getSource().getDefiningOp<UnPackOp>()) {
6189 if (unPackOp.getSourceType() == packOp.getDestType() &&
6190 !packOp.getPaddingValue() &&
6193 rewriter.
replaceOp(packOp, unPackOp.getSource());
6201 packOp.getPaddingValueMutable().clear();
6207 SmallVector<int64_t> srcShape, destShape;
6209 Location loc = packOp.getLoc();
6210 Value source = packOp.getSource();
6211 if (srcShape != packOp.getSourceType().getShape()) {
6212 auto newSrcType = packOp.getSourceType().clone(srcShape);
6214 tensor::CastOp::create(rewriter, loc, newSrcType, packOp.getSource());
6216 Value dest = packOp.getDest();
6217 ShapedType originalResultType = packOp.getDestType();
6218 bool needUpdateDestType = (destShape != originalResultType.getShape());
6219 if (needUpdateDestType) {
6220 auto newDestType = packOp.getDestType().clone(destShape);
6222 tensor::CastOp::create(rewriter, loc, newDestType, packOp.getDest());
6225 packOp.getSourceMutable().assign(source);
6226 packOp.getDestMutable().assign(dest);
6227 packOp.getResult().setType(cast<RankedTensorType>(dest.
getType()));
6230 if (needUpdateDestType) {
6232 auto castOp = tensor::CastOp::create(rewriter, loc, originalResultType,
6233 packOp.getResult());
6242template <
typename PackOrUnpackOp>
6244 static_assert(std::is_same<PackOrUnpackOp, PackOp>::value ||
6245 std::is_same<PackOrUnpackOp, UnPackOp>::value,
6246 "Function meant for pack/unpack");
6251 int64_t numPackedDims = innerDimsPos.size();
6252 auto orderedDims = llvm::to_vector<4>(llvm::seq<int64_t>(0, numPackedDims));
6253 if (orderedDims != innerDimsPos) {
6259 int64_t packedRank = packedTensorType.getRank();
6269 return llvm::all_of(
6270 llvm::seq<int64_t>(0, packedRank - numPackedDims),
6271 [&packedShape](
int64_t i) {
return packedShape[i] == 1; });
6274bool PackOp::isLikePad() {
6275 auto packedTensorType =
6276 llvm::cast<ShapedType>((*this)->getResultTypes().front());
6280::mlir::LogicalResult
6281PackOp::fold(FoldAdaptor adaptor,
6286 if (!hasPureTensorSemantics())
6288 std::optional<Attribute> paddingValue;
6289 if (
auto pad = adaptor.getPaddingValue())
6291 if (
OpFoldResult reshapedSource = reshapeConstantSource(
6292 llvm::dyn_cast_if_present<DenseElementsAttr>(adaptor.getSource()),
6293 cast<TensorType>(getDestType()), paddingValue)) {
6294 results.push_back(reshapedSource);
6322 if (!op.hasPureTensorSemantics())
6343 PackOp::create(rewriter, op.getLoc(), newOperands[0], newOperands[1],
6344 op.getInnerDimsPos(), newMixedTileSizes,
6345 op.getPaddingValue(), op.getOuterDimsPerm());
6346 newOp->setDiscardableAttrs(op->getDiscardableAttrDictionary());
6349 Value oldResult = op.getResult();
6350 Value newResult = newOp.getResult();
6353 ? tensor::CastOp::create(rewriter, op->getLoc(),
6354 oldResult.
getType(), newResult)
6367void UnPackOp::getAsmResultNames(
6369 if (!getResults().empty())
6370 setNameFn(getResult(),
"unpack");
6379 Type sourceType, destType, resultType;
6391 if (parser.parseInteger(value))
6393 outerDimsPermVec.push_back(value);
6403 SmallVector<int64_t> innerDimsPosVec;
6406 if (parser.parseInteger(value))
6408 innerDimsPosVec.push_back(value);
6420 for (
auto val : staticTilesAttr.
asArrayRef())
6421 staticTiles.push_back(val);
6438 bool isMemRef = llvm::isa<MemRefType>(sourceType);
6441 "pack/unpack requires '->' and destination type");
6445 resultType = destType;
6451 if (!dynamicTiles.empty() &&
6456 result.addAttribute(
"static_inner_tiles",
6458 result.addAttribute(
"inner_dims_pos", innerDimsPos);
6460 result.addAttribute(
"outer_dims_perm", outerDimsPerm);
6462 SmallVector<int32_t> segmentSizes = {
6463 1, 1, 0,
static_cast<int32_t
>(dynamicTiles.size())};
6464 result.addAttribute(
"operandSegmentSizes",
6468 result.addTypes(resultType);
6473void UnPackOp::print(OpAsmPrinter &p) {
6474 p <<
" " << getSource();
6476 if (!getOuterDimsPerm().empty()) {
6477 p <<
" outer_dims_perm = [";
6478 llvm::interleaveComma(getOuterDimsPerm(), p);
6482 p <<
" inner_dims_pos = [";
6483 llvm::interleaveComma(getInnerDimsPos(), p);
6486 p <<
" inner_tiles = ";
6489 p <<
" into " << getDest();
6492 {
"static_inner_tiles",
"inner_dims_pos",
6493 "outer_dims_perm",
"operandSegmentSizes"});
6495 p <<
" : " << getSource().getType();
6496 p <<
" -> " << getDest().getType();
6500UnPackOp::reifyResultShapes(OpBuilder &builder,
6509SmallVector<OpFoldResult> UnPackOp::getMixedTiles() {
6513SmallVector<int64_t> UnPackOp::getStaticTiles() {
6517ArrayRef<int64_t> UnPackOp::getAllOuterDims() {
6518 ShapedType destType = getDestType();
6519 int64_t destRank = destType.getRank();
6520 return getSourceType().getShape().take_front(destRank);
6523SmallVector<int64_t> UnPackOp::getTiledOuterDims() {
6524 auto innerDimsPos = getInnerDimsPos();
6525 SmallVector<int64_t> outerDims(getAllOuterDims());
6526 SmallVector<int64_t> res;
6529 SmallVector<int64_t> outerDimPermInv(getOuterDimsPerm());
6531 if (!outerDimPermInv.empty())
6535 for (
auto index : innerDimsPos)
6536 res.push_back(outerDims[index]);
6541LogicalResult UnPackOp::verify() {
6546 if (!hasPureTensorSemantics())
6555void UnPackOp::build(OpBuilder &builder, OperationState &state, Value source,
6556 Value dest, ArrayRef<int64_t> innerDimsPos,
6557 ArrayRef<OpFoldResult> innerTiles,
6558 ArrayRef<int64_t> outerDimsPerm) {
6559 assert(innerDimsPos.size() == innerTiles.size() &&
6560 "number of tile sizes specified must match the specified number of "
6561 "original dimensions to be tiled");
6562 SmallVector<int64_t> staticTileSizes;
6563 SmallVector<Value> dynamicTileSizes;
6565 build(builder, state, dest.
getType(), source, dest,
6566 outerDimsPerm.empty() ?
nullptr
6572Value UnPackOp::createDestinationTensor(OpBuilder &
b, Location loc,
6574 ArrayRef<OpFoldResult> innerTileSizes,
6575 ArrayRef<int64_t> innerDimsPos,
6576 ArrayRef<int64_t> outerDimsPerm) {
6577 AffineExpr sym0, sym1;
6579 auto dimMul = [&](OpFoldResult v1, OpFoldResult v2) -> OpFoldResult {
6583 SmallVector<OpFoldResult> mixedSizes;
6584 auto srcType = llvm::cast<RankedTensorType>(source.
getType());
6586 llvm::seq<unsigned>(0, srcType.getRank() - innerTileSizes.size())) {
6587 if (srcType.isDynamicDim(i))
6588 mixedSizes.push_back(
6589 tensor::DimOp::create(
b, loc, source, i).getResult());
6591 mixedSizes.push_back(
b.getIndexAttr(srcType.getDimSize(i)));
6593 if (!outerDimsPerm.empty()) {
6598 for (
auto [dimPos, tileSize] : llvm::zip_equal(innerDimsPos, innerTileSizes))
6599 mixedSizes[dimPos] = dimMul(mixedSizes[dimPos], tileSize);
6601 auto elemType = srcType.getElementType();
6602 return tensor::EmptyOp::create(
b, loc, mixedSizes, elemType);
6605UnPackOp UnPackOp::createTransposedClone(OpBuilder &
b, Location loc,
6606 Value transposedSource,
6607 ArrayRef<int64_t> innerPermutation,
6608 ArrayRef<int64_t> outerPermutation) {
6610 *
this, innerPermutation, outerPermutation);
6611 return UnPackOp::create(
b, loc, transposedSource, getDest(),
6612 metadata.innerDimsPos, metadata.innerTiles,
6613 metadata.outerDimsPerm);
6620 bool changeNeeded =
false;
6621 srcShape.assign(op.getSourceType().getShape().begin(),
6622 op.getSourceType().getShape().end());
6623 destShape.assign(op.getDestType().getShape().begin(),
6624 op.getDestType().getShape().end());
6625 llvm::SmallSetVector<int64_t, 4> innerDims;
6626 innerDims.insert_range(op.getInnerDimsPos());
6628 if (!op.getOuterDimsPerm().empty())
6630 int destRank = op.getDestRank();
6631 for (
auto i : llvm::seq<int64_t>(0, destRank)) {
6632 if (innerDims.contains(i))
6636 if (!inverseOuterDimsPerm.empty())
6637 srcPos = inverseOuterDimsPerm[destPos];
6638 if (ShapedType::isDynamic(srcShape[srcPos]) ==
6639 ShapedType::isDynamic(destShape[destPos])) {
6642 int64_t size = srcShape[srcPos];
6643 if (ShapedType::isDynamic(size))
6644 size = destShape[destPos];
6645 srcShape[srcPos] = size;
6646 destShape[destPos] = size;
6647 changeNeeded =
true;
6649 return changeNeeded;
6652LogicalResult UnPackOp::canonicalize(UnPackOp unPackOp,
6657 if (!unPackOp.hasPureTensorSemantics())
6661 if (PackOp packOp = unPackOp.getSource().getDefiningOp<PackOp>()) {
6662 if (packOp.getSourceType() != unPackOp.getDestType())
6664 if (packOp.getPaddingValue() ||
6668 rewriter.
replaceOp(unPackOp, packOp.getSource());
6672 if (
auto dstStyleOp =
6673 unPackOp.getDest().getDefiningOp<DestinationStyleOpInterface>()) {
6674 auto destValue = cast<OpResult>(unPackOp.getDest());
6675 Value newDest = dstStyleOp.getDpsInits()[destValue.getResultNumber()];
6677 [&]() { unPackOp.setDpsInitOperand(0, newDest); });
6681 if (unPackOp->hasOneUse()) {
6682 auto extractSliceUser =
6683 dyn_cast<tensor::ExtractSliceOp>(*unPackOp->getUsers().begin());
6684 if (extractSliceUser && unPackOp.canFoldSliceOp(extractSliceUser)) {
6685 OpBuilder::InsertionGuard g(rewriter);
6687 auto newDest = tensor::ExtractSliceOp::create(
6688 rewriter, unPackOp->getLoc(), unPackOp.getDest(),
6689 extractSliceUser.getMixedOffsets(), extractSliceUser.getMixedSizes(),
6690 extractSliceUser.getMixedStrides());
6692 unPackOp.setDpsInitOperand(0, newDest);
6693 unPackOp.getResult().setType(newDest.
getType());
6695 rewriter.
replaceOp(extractSliceUser, unPackOp);
6701 SmallVector<int64_t> srcShape, destShape;
6703 Location loc = unPackOp.getLoc();
6704 Value source = unPackOp.getSource();
6705 if (srcShape != unPackOp.getSourceType().getShape()) {
6706 auto newSrcType = unPackOp.getSourceType().clone(srcShape);
6707 source = tensor::CastOp::create(rewriter, loc, newSrcType,
6708 unPackOp.getSource());
6710 Value dest = unPackOp.getDest();
6711 if (destShape != unPackOp.getDestType().getShape()) {
6712 auto newDestType = unPackOp.getDestType().clone(destShape);
6713 dest = tensor::CastOp::create(rewriter, loc, newDestType,
6714 unPackOp.getDest());
6716 UnPackOp newOp = UnPackOp::create(
6717 rewriter, loc, source, dest, unPackOp.getInnerDimsPos(),
6718 unPackOp.getMixedTiles(), unPackOp.getOuterDimsPerm());
6720 unPackOp, unPackOp.getResult().
getType(), newOp.getResult());
6727bool UnPackOp::canFoldSliceOp(tensor::ExtractSliceOp sliceOp) {
6729 if (sliceOp.getResultType().getRank() != this->getDestType().getRank())
6734 RankedTensorType unpackedTypeAfterFold = sliceOp.getResultType();
6735 SmallVector<int64_t> outerShapeWithoutTranspose =
6737 SmallVector<bool> areOuterDimsTiled(outerShapeWithoutTranspose.size(),
false);
6738 for (
auto [pos, tileSize] :
6739 llvm::zip_equal(this->getInnerDimsPos(), this->getStaticInnerTiles())) {
6740 areOuterDimsTiled[pos] =
true;
6741 if (unpackedTypeAfterFold.isDynamicDim(pos))
6743 if (ShapedType::isDynamic(outerShapeWithoutTranspose[pos]))
6745 if (ShapedType::isDynamic(tileSize))
6747 int64_t paddingSize = outerShapeWithoutTranspose[pos] * tileSize -
6748 unpackedTypeAfterFold.getDimSize(pos);
6749 if (paddingSize >= tileSize)
6753 for (int64_t pos = 0, e = outerShapeWithoutTranspose.size(); pos < e; ++pos) {
6754 if (areOuterDimsTiled[pos])
6756 int64_t dim = outerShapeWithoutTranspose[pos];
6757 if (ShapedType::isDynamic(dim))
6759 if (dim != unpackedTypeAfterFold.getDimSize(pos))
6765bool UnPackOp::isLikeUnPad() {
6766 ShapedType packedTensorType = getSourceType();
6770::mlir::LogicalResult
6771UnPackOp::fold(FoldAdaptor adaptor,
6772 ::llvm::SmallVectorImpl<OpFoldResult> &results) {
6776 if (!hasPureTensorSemantics())
6779 if (OpFoldResult reshapedSource = reshapeConstantSource(
6780 llvm::dyn_cast_if_present<DenseElementsAttr>(adaptor.getSource()),
6781 cast<TensorType>(getResult().
getType()))) {
6782 results.push_back(reshapedSource);
6810 if (!op.hasPureTensorSemantics())
6819 Value sourceTensor = newOperands[0];
6823 rewriter, sourceTensor.
getType(), op.getMixedTiles());
6829 UnPackOp newOp = UnPackOp::create(rewriter, op.getLoc(), sourceTensor,
6830 newOperands[1], op.getInnerDimsPos(),
6831 newMixedTileSizes, op.getOuterDimsPerm());
6832 newOp->setDiscardableAttrs(op->getDiscardableAttrDictionary());
6835 Value oldResult = op.getResult();
6836 Value newResult = newOp.getResult();
6839 ? tensor::CastOp::create(rewriter, op->getLoc(),
6840 oldResult.
getType(), newResult)
6854 utils::IteratorType::reduction, utils::IteratorType::parallel,
6855 utils::IteratorType::parallel, utils::IteratorType::reduction};
6858SmallVector<AffineMap>
6859BatchReduceMatmulOp::getDefaultIndexingMaps(MLIRContext *context) {
6860 AffineExpr d0, d1, d2, d3;
6861 SmallVector<AffineMap> indexingMaps;
6863 indexingMaps.push_back(
AffineMap::get(4, 0, {d0, d1, d3}, context));
6864 indexingMaps.push_back(
AffineMap::get(4, 0, {d0, d3, d2}, context));
6866 return indexingMaps;
6869bool BatchReduceMatmulOp::isDefaultIndexingMaps(Attribute attr) {
6870 ArrayAttr maps = dyn_cast<ArrayAttr>(attr);
6873 if (maps.size() != 3)
6878 return (*positions)[0] == SmallVector<int64_t>{0, 1, 3} &&
6879 (*positions)[1] == SmallVector<int64_t>{0, 3, 2} &&
6880 (*positions)[2] == SmallVector<int64_t>{1, 2};
6882unsigned BatchReduceMatmulOp::getNumRegionArgs() {
return 3; }
6884std::string BatchReduceMatmulOp::getLibraryCallName() {
6890bool BatchReduceMatmulOp::hasUserDefinedMaps() {
6891 SmallVector<AffineMap, 3> defaultMaps =
6893 SmallVector<AffineMap, 3> explicitMaps = getIndexingMapsArray();
6894 return defaultMaps != explicitMaps;
6904bool BatchReduceMatmulOp::isValidLhsRhsBroadcastMap(AffineMap bcastMap,
6907 "Expected less than 3 result dim expr.");
6908 bool isValid =
false;
6909 enum Indices { batchPos, mPos, nPos, kPos };
6911 AffineExpr expr = bcastMap.
getResult(0);
6914 AffineExpr expr0 = bcastMap.
getResult(0);
6915 AffineExpr expr1 = bcastMap.
getResult(1);
6920 : ((expr0.isFunctionOfDim(batchPos) &&
6921 expr1.isFunctionOfDim(kPos)) ||
6922 (expr0.isFunctionOfDim(kPos) && expr1.isFunctionOfDim(nPos)));
6927void BatchReduceMatmulOp::regionBuilder(
6928 ImplicitLocOpBuilder &
b,
Block &block, ArrayRef<NamedAttribute> attrs,
6931 emitError() <<
"BatchReduceMatmulOp regionBuilder expects 3 args, got "
6936 "BatchReduceMatmulOp regionBuilder expects 3 args");
6937 RegionBuilderHelper helper(
b, block);
6938 SmallVector<Value> yields;
6942 helper.buildTypeFn(TypeFn::cast_signed, toType, block.
getArgument(0));
6944 helper.buildTypeFn(TypeFn::cast_signed, toType, block.
getArgument(1));
6946 helper.buildBinaryFn(BinaryFn::mul, castValA, castValB,
emitError);
6947 if (!castValA || !castValB || !mulVal)
6950 helper.buildBinaryFn(BinaryFn::add, block.
getArgument(2), mulVal);
6953 yields.push_back(addVal);
6954 helper.yieldOutputs(yields);
6957ParseResult BatchReduceMatmulOp::parse(OpAsmParser &parser,
6958 OperationState &
result) {
6959 SmallVector<Attribute, 3> indexingMapsAttr;
6970 if (!isa<AffineMapAttr>(mapAttr)) {
6972 "expected affine map attribute");
6974 indexingMapsAttr.push_back(mapAttr);
6984 if (indexingMapsAttr.empty()) {
6985 indexingMapsAttr = llvm::map_to_vector(
6986 BatchReduceMatmulOp::getDefaultIndexingMaps(parser.
getContext()),
6987 [](AffineMap map) -> Attribute { return AffineMapAttr::get(map); });
6989 result.addAttribute(
"indexing_maps",
6991 return ::parseNamedStructuredOp(parser,
result,
6992 BatchReduceMatmulOp::getNumRegionArgs(),
6993 BatchReduceMatmulOp::getRegionBuilder());
6996void BatchReduceMatmulOp::print(OpAsmPrinter &p) {
6997 SmallVector<Attribute, 3> indexingMaps = llvm::map_to_vector(
6998 BatchReduceMatmulOp::getDefaultIndexingMaps(
getContext()),
6999 [](AffineMap map) -> Attribute {
return AffineMapAttr::get(map); });
7001 if (!llvm::equal(getIndexingMaps(), indexingMaps)) {
7002 p <<
" indexing_maps = [";
7003 llvm::interleaveComma(getIndexingMaps(), p,
7008 SmallVector<StringRef, 3> elidedAttrs = {
7009 "operandSegmentSizes",
"linalg.memoized_indexing_maps",
"indexing_maps"};
7015LogicalResult BatchReduceMatmulOp::verify() {
7018 if (!hasUserDefinedMaps())
7021 for (
unsigned opIndex = 0; opIndex < 3; opIndex++) {
7027LogicalResult BatchReduceMatmulOp::fold(FoldAdaptor,
7028 SmallVectorImpl<OpFoldResult> &) {
7031void BatchReduceMatmulOp::getEffects(
7032 SmallVectorImpl<SideEffects::EffectInstance<MemoryEffects::Effect>>
7034 if (hasPureTensorSemantics())
7047SmallVector<utils::IteratorType> ScaledContractOp::getIteratorTypesArray() {
7048 AffineMap outAffineMap = getIndexingMapsArray().pop_back_val();
7050 SmallVector<bool> dimsInOutput(outAffineMap.
getNumDims(),
false);
7052 auto dimExpr = dyn_cast<AffineDimExpr>(
result);
7053 assert(dimExpr &&
"affine_map is a projected permutation");
7054 dimsInOutput[dimExpr.getPosition()] =
true;
7057 SmallVector<utils::IteratorType> iteratorTypes;
7058 for (
auto dimOccursInOutput : dimsInOutput)
7059 iteratorTypes.push_back(dimOccursInOutput ? utils::IteratorType::parallel
7060 : utils::IteratorType::reduction);
7062 return iteratorTypes;
7065unsigned ScaledContractOp::getNumRegionArgs() {
return 5; }
7068void ScaledContractOp::regionBuilder(
7069 ImplicitLocOpBuilder &
b,
Block &block, ArrayRef<NamedAttribute> attrs,
7072 emitError() <<
"ScaledContractOp regionBuilder expects 5 args, got "
7077 "ScaledContractOp regionBuilder expects 5 args");
7078 RegionBuilderHelper helper(
b, block);
7080 TypeFn castSignedness = TypeFn::cast_signed;
7081 auto castIter = llvm::find_if(attrs, [&](
const NamedAttribute &attr) {
7082 return attr.
getName() ==
"cast";
7084 if (castIter != attrs.end()) {
7085 if (
auto attr = llvm::dyn_cast<TypeFnAttr>(castIter->getValue()))
7095 auto buildScaledValue = [&](Value data, Value scale) -> Value {
7096 auto dataFloatTy = dyn_cast<FloatType>(data.
getType());
7097 auto outFloatTy = dyn_cast<FloatType>(outType);
7098 if (dataFloatTy && dyn_cast<FloatType>(scale.getType()) && outFloatTy) {
7099 unsigned dataWidth = dataFloatTy.getWidth();
7100 unsigned outWidth = outFloatTy.getWidth();
7101 if (dataWidth < outWidth)
7102 return arith::ScalingExtFOp::create(
b, outType, data, scale,
7105 Value dataAtOutType = helper.buildTypeFn(castSignedness, outType, data);
7106 Value scaleAtOutType = helper.buildTypeFn(castSignedness, outType, scale);
7107 return helper.buildBinaryFn(BinaryFn::mul, dataAtOutType, scaleAtOutType,
7119 Value productAtOutType =
7120 helper.buildBinaryFn(BinaryFn::mul, scaledLhs, scaledRhs,
emitError);
7121 if (!productAtOutType)
7127 helper.yieldOutputs({
result});
7130ParseResult ScaledContractOp::parse(OpAsmParser &parser,
7131 OperationState &
result) {
7133 if (
failed(indexingMapsAttr) || *indexingMapsAttr ==
nullptr)
7135 "expected 'indexing_maps' attribute");
7136 result.addAttribute(
"indexing_maps", *indexingMapsAttr);
7142void ScaledContractOp::print(OpAsmPrinter &p) {
7143 p <<
" indexing_maps = " << llvm::interleaved_array(getIndexingMaps());
7145 p, getOperation(), getInputs(), getOutputs(),
7146 {
"indexing_maps",
"operandSegmentSizes"});
7149LogicalResult ScaledContractOp::verify() {
7150 int iterationSpaceDims = -1;
7155 SmallVector<size_t> inOccurrences;
7156 SmallVector<size_t> outOccurrences;
7159 SmallVector<AffineMap, 5> maps = getIndexingMapsArray();
7161 return emitOpError(
"expected 5 indexing maps and operands");
7163 SmallVector<Type, 5> types = llvm::to_vector(getOperandTypes());
7165 auto outputFloatType = dyn_cast<FloatType>(outputElementType);
7166 if (!outputFloatType)
7167 return emitOpError(
"expected output element type to be floating-point");
7169 for (Type inputType : ArrayRef<Type>(types).take_front(4)) {
7173 "expected input element types to be integer or floating-point");
7175 return emitOpError(
"expected input element type bitwidth to be no "
7176 "greater than output element type bitwidth");
7179 for (
auto &&[affineMap, operandType, isInput] :
7180 llvm::zip(SmallVector<AffineMap>{maps[0], maps[2], maps[4]},
7181 SmallVector<Type>{types[0], types[2], types[4]},
7182 SmallVector<bool>{
true,
true,
false})) {
7184 affineMap, operandType, isInput, iterationSpaceDims, inOccurrences,
7185 outOccurrences, [&]() {
return emitError(); })))
7190 inOccurrences, outOccurrences,
7195 auto checkScaleAffineMapAndType = [&](AffineMap affineMap,
7196 Type operandType) -> LogicalResult {
7201 return emitError(
"scale affine_map must not contain symbols");
7204 "scale affine_map must not have more results than inputs");
7206 SmallVector<bool, 8> seen(affineMap.
getNumInputs(),
false);
7210 AffineDimExpr dim =
nullptr;
7211 if (isa<AffineDimExpr>(expr)) {
7213 dim = dyn_cast<AffineDimExpr>(expr);
7214 }
else if (
auto binExpr = dyn_cast<AffineBinaryOpExpr>(expr)) {
7221 "only block scale with floordiv is supported for now");
7222 auto scaleDim = dyn_cast<AffineDimExpr>(binExpr.getLHS());
7224 return emitError(
"block scale LHS must be dim");
7225 auto scaleFactor = dyn_cast<AffineConstantExpr>(binExpr.getRHS());
7227 return emitError(
"block scale RHS must be constant");
7228 if (scaleFactor.getValue() <= 0)
7229 return emitError(
"block scale factor must be positive");
7232 return emitError(
"unsupported scaling variant");
7236 return emitError(
"invalid scale affine_map result expression");
7239 "scale affine_map must not have duplicate result dimensions");
7244 if (
auto shapedType = dyn_cast<ShapedType>(operandType)) {
7247 "scale ranks of shaped operand and results of corresponding "
7248 "affine_map differ");
7251 "scale affine_map specifies shaped access while operand has "
7259 for (
auto &&[affineMap, operandType] :
7260 llvm::zip(SmallVector<AffineMap>{maps[1], maps[3]},
7261 SmallVector<Type>{types[1], types[3]})) {
7262 if (
failed(checkScaleAffineMapAndType(affineMap, operandType)))
7267 for (
auto &&[inputMap, inputType, scaleMap, scaleType] :
7268 llvm::zip(SmallVector<AffineMap>{maps[0], maps[2]},
7269 SmallVector<Type>{types[0], types[2]},
7270 SmallVector<AffineMap>{maps[1], maps[3]},
7271 SmallVector<Type>{types[1], types[3]})) {
7272 if (inputMap.getNumResults() < scaleMap.getNumResults())
7273 return emitError(
"scale must have at most the same rank as input");
7274 if (scaleMap.getNumResults() == 0)
7277 auto inputShape = dyn_cast<ShapedType>(inputType).getShape();
7278 auto scaleShape = dyn_cast<ShapedType>(scaleType).getShape();
7285 for (
auto [scaleIdx, scaleExpr] : llvm::enumerate(scaleMap.getResults())) {
7286 AffineDimExpr scaleDimExpr =
nullptr;
7287 std::optional<int64_t> scaleFactor;
7288 if (
auto dimExpr = dyn_cast<AffineDimExpr>(scaleExpr)) {
7290 scaleDimExpr = dimExpr;
7291 }
else if (
auto scaleBinExpr = dyn_cast<AffineBinaryOpExpr>(scaleExpr)) {
7294 "only floordiv is supported for now");
7295 auto scaleDim = dyn_cast<AffineDimExpr>(scaleBinExpr.getLHS());
7296 assert(scaleDim &&
"block scale LHS is a dim expression");
7297 scaleDimExpr = scaleDim;
7299 dyn_cast<AffineConstantExpr>(scaleBinExpr.getRHS()).getValue();
7301 llvm_unreachable(
"unknown scale expression");
7303 assert(scaleDimExpr &&
"failed to find scale dim expression");
7305 std::optional<unsigned> inputIdx =
7306 inputMap.getResultPosition(scaleDimExpr);
7309 "scale map must contain corresponding input dimensions only");
7313 if (scaleFactor && inputShape[*inputIdx] != ShapedType::kDynamic &&
7314 scaleShape[scaleIdx] != ShapedType::kDynamic &&
7315 llvm::divideCeilSigned(inputShape[*inputIdx], *scaleFactor) !=
7316 static_cast<int64_t
>(scaleShape[scaleIdx])) {
7317 return emitError() <<
"Invalid scale shape at dim " << *inputIdx
7319 << llvm::divideCeilSigned(inputShape[*inputIdx],
7321 <<
" but got " << scaleShape[scaleIdx];
7329LogicalResult ScaledContractOp::fold(FoldAdaptor,
7330 SmallVectorImpl<OpFoldResult> &) {
7334void ScaledContractOp::getEffects(
7335 SmallVectorImpl<SideEffects::EffectInstance<MemoryEffects::Effect>>
7337 if (hasPureTensorSemantics())
7353void LinalgDialect::getCanonicalizationPatterns(
7362 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