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)) {
940 newOffsets.push_back(affine::makeComposedFoldedAffineApply(
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 {
2050static std::optional<BroadcastReduceKind>
2051matchBroadcastReduceBody(ReduceOp reduceOp) {
2052 if (reduceOp.getNumDpsInputs() != 1 || reduceOp.getNumDpsInits() != 1 ||
2053 !reduceOp.getBody())
2054 return std::nullopt;
2061 Block &block = *reduceOp.getBody();
2064 return std::nullopt;
2068 Operation *combineOp = yieldOp.getOperand(0).getDefiningOp();
2070 return std::nullopt;
2078 return std::nullopt;
2082 .Case<arith::MaxSIOp>(
2083 [](arith::MaxSIOp) {
return BroadcastReduceKind::MaxSI; })
2084 .Case<arith::MaxUIOp>(
2085 [](arith::MaxUIOp) {
return BroadcastReduceKind::MaxUI; })
2086 .Case<arith::MinSIOp>(
2087 [](arith::MinSIOp) {
return BroadcastReduceKind::MinSI; })
2088 .Case<arith::MinUIOp>(
2089 [](arith::MinUIOp) {
return BroadcastReduceKind::MinUI; })
2090 .Default([](Operation *) -> std::optional<BroadcastReduceKind> {
2091 return std::nullopt;
2096static bool hasBroadcastReduceIdentity(Value init, BroadcastReduceKind kind) {
2101 auto integerAttr = dyn_cast<IntegerAttr>(*initAttr);
2105 const APInt &value = integerAttr.getValue();
2107 case BroadcastReduceKind::MaxSI:
2108 return value.isMinSignedValue();
2109 case BroadcastReduceKind::MaxUI:
2110 return value.isZero();
2111 case BroadcastReduceKind::MinSI:
2112 return value.isMaxSignedValue();
2113 case BroadcastReduceKind::MinUI:
2114 return value.isAllOnes();
2116 llvm_unreachable(
"unknown broadcast reduction kind");
2125struct FoldReduceBroadcast :
public OpRewritePattern<linalg::ReduceOp> {
2126 using OpRewritePattern<linalg::ReduceOp>::OpRewritePattern;
2128 LogicalResult matchAndRewrite(linalg::ReduceOp reduceOp,
2129 PatternRewriter &rewriter)
const override {
2130 if (reduceOp.getNumResults() != 1 || !reduceOp.hasPureTensorSemantics())
2133 assert(reduceOp.getInputs().size() == 1 &&
2134 "expected one input for a single-result tensor reduce");
2137 reduceOp.getInputs().front().getDefiningOp<linalg::BroadcastOp>();
2138 if (!broadcastOp || !broadcastOp.hasPureTensorSemantics())
2141 auto sourceType = cast<RankedTensorType>(broadcastOp.getInput().getType());
2142 auto broadcastType =
2143 cast<RankedTensorType>(broadcastOp.getResult().front().getType());
2144 auto resultType = cast<RankedTensorType>(reduceOp.getResult(0).getType());
2145 if (!sourceType.hasStaticShape() || !broadcastType.hasStaticShape() ||
2146 !resultType.hasStaticShape() || sourceType != resultType)
2149 ArrayRef<int64_t> broadcastDims = broadcastOp.getDimensions();
2150 ArrayRef<int64_t> reduceDims = reduceOp.getDimensions();
2151 if (broadcastDims != reduceDims)
2156 for (int64_t dimension : broadcastDims)
2157 if (broadcastType.getDimSize(dimension) == 0)
2160 std::optional<BroadcastReduceKind> kind =
2161 matchBroadcastReduceBody(reduceOp);
2163 !hasBroadcastReduceIdentity(reduceOp.getInits().front(), *kind))
2166 rewriter.
replaceOp(reduceOp, broadcastOp.getInput());
2173void ReduceOp::getCanonicalizationPatterns(RewritePatternSet &results,
2174 MLIRContext *context) {
2175 results.
add<FoldReduceBroadcast>(context);
2188 linalg::YieldOp::create(
b, loc, args[0]);
2192void TransposeOp::build(::mlir::OpBuilder &builder,
2193 ::mlir::OperationState &
result, Value input, Value init,
2195 ArrayRef<NamedAttribute> attributes) {
2196 result.addOperands(input);
2197 result.addOperands(init);
2198 result.addAttribute(getPermutationAttrName(
result.name), permutation);
2199 result.addAttributes(attributes);
2202 Type initType = init.
getType();
2203 if (llvm::isa<RankedTensorType>(initType))
2204 result.addTypes(initType);
2210void TransposeOp::build(::mlir::OpBuilder &builder,
2211 ::mlir::OperationState &
result, Value input, Value init,
2212 ArrayRef<int64_t> permutation,
2213 ArrayRef<NamedAttribute> attributes) {
2218ParseResult TransposeOp::parse(OpAsmParser &parser, OperationState &
result) {
2220 parser,
result, [&](OpAsmParser &parser, NamedAttrList &attributes) {
2232void TransposeOp::getAsmResultNames(
2234 if (!getResults().empty())
2235 setNameFn(getResults().front(),
"transposed");
2238void TransposeOp::print(OpAsmPrinter &p) {
2242 {getPermutationAttrName()});
2245LogicalResult TransposeOp::verify() {
2246 ArrayRef<int64_t> permutationRef = getPermutation();
2249 return emitOpError(
"permutation is not valid");
2251 auto inputType = getInput().getType();
2252 auto initType = getInit().getType();
2254 int64_t rank = inputType.getRank();
2260 if (rank !=
static_cast<int64_t
>(permutationRef.size()))
2261 return emitOpError() <<
"size of permutation " << permutationRef.size()
2262 <<
" does not match the argument rank " << rank;
2264 auto inputDims = inputType.getShape();
2265 auto initDims = initType.getShape();
2267 for (int64_t i = 0; i < rank; ++i) {
2268 int64_t inputDim = inputDims[permutationRef[i]];
2269 int64_t initDim = initDims[i];
2271 if (inputDim != initDim) {
2272 return emitOpError() <<
"dim(result, " << i <<
") = " << initDim
2273 <<
" doesn't match dim(input, permutation[" << i
2274 <<
"]) = " << inputDim;
2281SmallVector<utils::IteratorType> TransposeOp::getIteratorTypesArray() {
2282 int64_t rank = getInit().getType().getRank();
2283 return SmallVector<utils::IteratorType>(rank, utils::IteratorType::parallel);
2286ArrayAttr TransposeOp::getIndexingMaps() {
2288 int64_t rank = getInit().getType().getRank();
2291 llvm::to_vector_of<unsigned>(getPermutation()),
getContext())),
2295void TransposeOp::getEffects(
2296 SmallVectorImpl<SideEffects::EffectInstance<MemoryEffects::Effect>>
2305LogicalResult TransposeOp::fold(FoldAdaptor adaptor,
2306 SmallVectorImpl<OpFoldResult> &
result) {
2308 if (!isa<TensorType>(getInput().
getType()))
2312 if (getPermutation().empty()) {
2313 result.push_back(getInput());
2318 result.push_back(getInput());
2331 auto defTransposeOp = transposeOp.getInput().getDefiningOp<TransposeOp>();
2332 if (!defTransposeOp)
2337 foldedPerms.reserve(perms.size());
2339 foldedPerms.push_back(defPerms[perm]);
2342 transposeOp, defTransposeOp.getInput(), transposeOp.getInit(),
2355 if (!transposeOp.hasPureTensorSemantics())
2360 if (!splatValue.has_value())
2364 cast<RankedTensorType>(transposeOp.getResult()[0].getType());
2381 Value input = transposeOp.getInput();
2382 BroadcastOp broadcastOp = input.
getDefiningOp<BroadcastOp>();
2393 unsigned dimensionSize = dimensions.size();
2394 for (
unsigned i = 0; i < dimensionSize; ++i)
2395 resultDimensions.push_back(invertPerm[dimensions[i]]);
2398 Value broadcastInput = broadcastOp.getInput();
2399 Location loc = transposeOp.getLoc();
2402 auto broadcastInputTy =
2403 mlir::cast<RankedTensorType>(broadcastInput.
getType());
2404 unsigned inputRank = broadcastInputTy.getRank();
2405 for (
unsigned i = 0; i < inputRank; ++i) {
2406 if (broadcastInputTy.isDynamicDim(i)) {
2407 dims.push_back(tensor::DimOp::create(rewriter, loc, broadcastInput, i)
2410 dims.push_back(IntegerAttr::get(IndexType::get(ctx),
2411 broadcastInputTy.getDimSize(i)));
2416 Value transposeInit = tensor::EmptyOp::create(
2417 rewriter, transposeOp.getLoc(), transposeResultShapes,
2418 broadcastInputTy.getElementType());
2421 Value transposeResult =
2422 TransposeOp::create(rewriter, loc, broadcastOp.getInput(),
2423 transposeInit, resultPerms)
2426 transposeOp, transposeResult, transposeOp.getInit(), resultDimensions);
2431void TransposeOp::getCanonicalizationPatterns(RewritePatternSet &results,
2432 MLIRContext *context) {
2433 results.
add<FoldTransposeWithTranspose, FoldTransposeSplatConstant,
2434 SwapTransposeWithBroadcast>(context);
2441void BroadcastOp::build(::mlir::OpBuilder &builder,
2442 ::mlir::OperationState &
result, Value input, Value init,
2444 ArrayRef<NamedAttribute> attributes) {
2445 result.addOperands(input);
2446 result.addOperands(init);
2447 result.addAttribute(getDimensionsAttrName(
result.name), dimensions);
2448 result.addAttributes(attributes);
2451 Type initType = init.
getType();
2452 if (llvm::isa<RankedTensorType>(initType))
2453 result.addTypes(initType);
2459void BroadcastOp::build(::mlir::OpBuilder &builder,
2460 ::mlir::OperationState &
result, Value input, Value init,
2461 ArrayRef<int64_t> dimensions,
2462 ArrayRef<NamedAttribute> attributes) {
2467ParseResult BroadcastOp::parse(OpAsmParser &parser, OperationState &
result) {
2469 parser,
result, [&](OpAsmParser &parser, NamedAttrList &attributes) {
2481void BroadcastOp::getAsmResultNames(
2483 if (!getResults().empty())
2484 setNameFn(getResults().front(),
"broadcasted");
2487void BroadcastOp::print(OpAsmPrinter &p) {
2491 {getDimensionsAttrName()});
2494LogicalResult BroadcastOp::verify() {
2495 ArrayRef<int64_t> dimensionsRef = getDimensions();
2497 auto inputType = getInput().getType();
2498 auto initType = getInit().getType();
2500 int64_t inputRank = inputType.getRank();
2501 int64_t initRank = initType.getRank();
2503 auto inputShape = inputType.getShape();
2504 auto initShape = initType.getShape();
2506 if ((
size_t)inputRank + dimensionsRef.size() != (
size_t)initRank)
2507 return emitOpError() <<
"input rank plus added dimensions does not "
2508 "match init rank. input rank: "
2510 <<
", dimensions size: " << dimensionsRef.size()
2511 <<
", init rank: " << initRank;
2513 for (
const auto &[idx, dim] : llvm::enumerate(dimensionsRef)) {
2514 if (dim < 0 || dim >= initRank)
2515 return emitOpError() <<
"dimension " << idx
2516 <<
" is out of range. expected range: [0, "
2517 << initRank - 1 <<
"], got: " << dim;
2521 if (uniquedDims.size() != dimensionsRef.size())
2522 return emitOpError() <<
"dimensions should not contain duplicates";
2525 SmallVector<int64_t> dimMap;
2526 for (
auto dim : llvm::seq<int64_t>(0, initRank)) {
2527 if (!llvm::is_contained(dimensionsRef, dim))
2528 dimMap.push_back(dim);
2531 for (
const auto &[inputDimIdx, initDimIdx] : llvm::enumerate(dimMap)) {
2534 if (inputShape[inputDimIdx] != initShape[initDimIdx])
2535 return emitOpError() <<
"input dim " << inputDimIdx
2536 <<
" should match init dim " << initDimIdx
2537 <<
". input: " << inputShape[inputDimIdx]
2538 <<
", init: " << initShape[initDimIdx];
2544SmallVector<utils::IteratorType> BroadcastOp::getIteratorTypesArray() {
2545 int64_t rank = getInit().getType().getRank();
2546 return SmallVector<utils::IteratorType>(rank, utils::IteratorType::parallel);
2549ArrayAttr BroadcastOp::getIndexingMaps() {
2551 int64_t rank = getInit().getType().getRank();
2557void BroadcastOp::getEffects(
2558 SmallVectorImpl<SideEffects::EffectInstance<MemoryEffects::Effect>>
2573 auto defBroadcastOp = broadcastOp.getInput().getDefiningOp<BroadcastOp>();
2574 if (!defBroadcastOp)
2579 Value init = broadcastOp.getInit();
2583 for (
auto dim : llvm::seq<int64_t>(0, initRank)) {
2584 if (!llvm::is_contained(dimensions, dim))
2585 dimMap.push_back(dim);
2587 for (
auto dim : defDimensions)
2588 foldedDims.push_back(dimMap[dim]);
2590 llvm::sort(foldedDims);
2592 broadcastOp, defBroadcastOp.getInput(), init, foldedDims);
2604 if (!broadcastOp.hasPureTensorSemantics())
2610 if (!splatValue.has_value())
2614 cast<RankedTensorType>(broadcastOp.getResult()[0].getType());
2615 if (!resultType.hasStaticShape())
2617 "result type has dynamic shape");
2626void BroadcastOp::getCanonicalizationPatterns(RewritePatternSet &results,
2627 MLIRContext *context) {
2628 results.
add<EraseIdentityLinalgOp<BroadcastOp>, FoldBroadcasts,
2629 FoldBroadcastSplatConstant>(context);
2636void linalg::YieldOp::print(OpAsmPrinter &p) {
2638 p <<
' ' << getOperands();
2641 p <<
" : " << getOperandTypes();
2644ParseResult YieldOp::parse(OpAsmParser &parser, OperationState &
result) {
2645 SmallVector<OpAsmParser::UnresolvedOperand, 2> opInfo;
2646 SmallVector<Type, 2> types;
2656static LogicalResult
verifyYield(linalg::YieldOp op, LinalgOp linalgOp) {
2657 if (op.getNumOperands() != linalgOp.getNumDpsInits())
2658 return op.emitOpError(
"expected number of yield values (")
2659 << op.getNumOperands()
2660 <<
") to match the number of inits / outs operands of the enclosing "
2661 <<
"LinalgOp (" << linalgOp.getNumDpsInits() <<
")";
2663 for (
OpOperand &opOperand : op->getOpOperands()) {
2665 linalgOp.getDpsInitOperand(opOperand.getOperandNumber());
2667 if (isa<MemRefType, RankedTensorType>(elementType))
2669 if (opOperand.get().getType() != elementType)
2670 return op.emitOpError(
"type of yield operand ")
2671 << (opOperand.getOperandNumber() + 1) <<
" ("
2672 << opOperand.get().getType() <<
") doesn't match "
2673 <<
"the element type of the enclosing linalg.generic op ("
2674 << elementType <<
")";
2679LogicalResult linalg::YieldOp::verify() {
2680 auto *parentOp = (*this)->getParentOp();
2681 if (parentOp->getNumRegions() != 1 || parentOp->getRegion(0).empty())
2682 return emitOpError(
"expected single non-empty parent region");
2684 if (
auto linalgOp = dyn_cast<LinalgOp>(parentOp))
2687 return emitOpError(
"expected parent op with LinalgOp interface");
2694LogicalResult IndexOp::verify() {
2695 auto linalgOp = dyn_cast<LinalgOp>((*this)->getParentOp());
2697 return emitOpError(
"expected parent op with LinalgOp interface");
2698 if (linalgOp.getNumLoops() <= getDim())
2699 return emitOpError(
"expected dim (")
2700 << getDim() <<
") to be lower than the number of loops ("
2701 << linalgOp.getNumLoops() <<
") of the enclosing LinalgOp";
2705OpFoldResult IndexOp::fold(FoldAdaptor adaptor) {
2706 auto linalgOp = dyn_cast_or_null<LinalgOp>((*this)->getParentOp());
2711 return OpFoldResult{};
2714 SmallVector<int64_t, 4> loopBounds = linalgOp.getStaticLoopRanges();
2715 uint64_t dim = getDim();
2716 assert(dim < loopBounds.size() &&
"Dim is out of bounds");
2717 if (loopBounds[dim] == 1)
2718 return IntegerAttr::get(IndexType::get(
getContext()), 0);
2720 return OpFoldResult{};
2725#include "mlir/Dialect/Linalg/IR/LinalgNamedStructuredOps.yamlgen.cpp.inc"
2727#define GET_OP_CLASSES
2728#include "mlir/Dialect/Linalg/IR/LinalgOps.cpp.inc"
2730#define GET_OP_CLASSES
2731#include "mlir/Dialect/Linalg/IR/LinalgStructuredOps.cpp.inc"
2732#define GET_OP_CLASSES
2733#include "mlir/Dialect/Linalg/IR/LinalgRelayoutOps.cpp.inc"
2750 for (
unsigned i = 0; i < num; ++i)
2757 auto rangeA = llvm::make_range(a.begin(), a.end());
2758 auto rangeB = llvm::make_range(
b.begin(),
b.end());
2759 auto concatRanges = llvm::concat<const AffineExpr>(rangeA, rangeB);
2760 return llvm::to_vector<4>(concatRanges);
2764 if (
auto memref = llvm::dyn_cast<MemRefType>(t)) {
2766 for (
auto size :
memref.getShape())
2773 if (
auto as =
memref.getMemorySpace()) {
2774 if (
auto attr = llvm::dyn_cast<IntegerAttr>(as))
2775 ss <<
"as" << attr.getInt();
2781 if (
auto vec = llvm::dyn_cast<VectorType>(t)) {
2784 vec.getShape(), [&](
int64_t i) { ss << i; }, [&]() { ss <<
"x"; });
2797 assert(isa<LinalgOp>(op));
2799 std::string fun =
"";
2801 if (UnaryFnAttr ufa = llvm::dyn_cast<UnaryFnAttr>(attr)) {
2802 fun = stringifyEnum(ufa.getValue()).str() +
"_";
2803 }
else if (BinaryFnAttr bfa = llvm::dyn_cast<BinaryFnAttr>(attr)) {
2804 fun = stringifyEnum(bfa.getValue()).str() +
"_";
2808 llvm::replace(name,
'.',
'_');
2809 llvm::raw_string_ostream ss(name);
2813 return std::string();
2828 LogicalResult matchAndRewrite(LinalgOp op,
2830 for (
OpOperand &opOperand : op->getOpOperands()) {
2834 auto mt = llvm::dyn_cast<MemRefType>(opOperand.get().getType());
2837 if (llvm::is_contained(op.getShape(&opOperand), 0)) {
2848struct FoldTensorCastConsumerOp :
public OpRewritePattern<tensor::CastOp> {
2849 using OpRewritePattern<tensor::CastOp>::OpRewritePattern;
2851 LogicalResult matchAndRewrite(tensor::CastOp castOp,
2852 PatternRewriter &rewriter)
const override {
2856 auto linalgOp = castOp.getSource().getDefiningOp<LinalgOp>();
2863 if (castOp->getBlock() != linalgOp->getBlock())
2866 OpBuilder::InsertionGuard guard(rewriter);
2869 Location loc = linalgOp.getLoc();
2870 OpResult resultValue = llvm::cast<OpResult>(castOp.getSource());
2873 llvm::cast<RankedTensorType>(castOp->getResult(0).getType());
2879 OpOperand *outOperand = linalgOp.getDpsInitOperand(resultNumber);
2881 tensor::CastOp::create(rewriter, loc, resultType, outOperand->
get());
2882 SmallVector<Value> newOperands = linalgOp.getDpsInputs();
2883 SmallVector<Value> outputOperands(linalgOp.getDpsInits().begin(),
2884 linalgOp.getDpsInits().end());
2885 outputOperands[resultNumber] = newOperand;
2886 newOperands.append(outputOperands.begin(), outputOperands.end());
2888 SmallVector<Type> resultTypes(linalgOp->result_type_begin(),
2889 linalgOp->result_type_end());
2890 resultTypes[resultNumber] = resultType;
2891 Operation *newOp =
clone(rewriter, linalgOp, resultTypes, newOperands);
2894 Value castBack = tensor::CastOp::create(
2898 results[resultNumber] = castBack;
2907static void populateMap(LinalgOp linalgOp, MutableArrayRef<OpOperand> operands,
2908 llvm::DenseMap<AffineExpr, int64_t> &affineExprToSize) {
2909 for (OpOperand &opOperand : operands) {
2910 if (linalgOp.isScalar(&opOperand))
2912 Value src = opOperand.get();
2913 auto sourceType = llvm::cast<RankedTensorType>(src.
getType());
2914 auto sourceMap = linalgOp.getMatchingIndexingMap(&opOperand);
2920 ArrayRef<int64_t> sourceShape = sourceType.getShape();
2922 if (
auto castOp = dyn_cast<tensor::CastOp>(parentOp)) {
2923 Value castSource = castOp.getSource();
2924 auto castSourceType =
2925 llvm::dyn_cast<RankedTensorType>(castSource.
getType());
2926 if (castSourceType && castSourceType.hasStaticShape())
2927 sourceShape = castSourceType.getShape();
2933 for (
unsigned i = 0; i < sourceShape.size(); i++) {
2934 if (sourceType.isDynamicDim(i))
2936 if (
auto affineDimExpr = dyn_cast<AffineDimExpr>(sourceMap.getResult(i)))
2937 affineExprToSize.try_emplace(affineDimExpr, sourceShape[i]);
2947static void createNewOperandWithStaticSizes(
2948 Location loc, PatternRewriter &rewriter, OpOperand *opOperand,
2949 llvm::DenseMap<AffineExpr, int64_t> &affineExprToSize, LinalgOp linalgOp,
2950 SmallVector<Value> &newOperands, SmallVector<Type> &resultTypes,
2951 bool &changeNeeded) {
2952 Value src = opOperand->
get();
2953 newOperands.push_back(src);
2954 if (linalgOp.isScalar(opOperand))
2956 auto sourceType = llvm::cast<RankedTensorType>(src.
getType());
2957 Type resultType = sourceType;
2958 if (sourceType.hasStaticShape() && linalgOp.isDpsInit(opOperand)) {
2959 resultTypes.push_back(resultType);
2962 ArrayRef<int64_t> sourceShape = sourceType.getShape();
2963 AffineMap sourceMap = linalgOp.getMatchingIndexingMap(opOperand);
2964 SmallVector<int64_t> newShape;
2967 bool newOperandNeeded =
false;
2968 for (
unsigned i = 0; i < sourceShape.size(); i++) {
2969 int64_t dimShape = sourceShape[i];
2970 AffineExpr dimExpr = sourceMap.
getResult(i);
2971 if (!affineExprToSize.contains(dimExpr) || !sourceType.isDynamicDim(i)) {
2972 newShape.push_back(dimShape);
2978 newShape.push_back(affineExprToSize[dimExpr]);
2979 newOperandNeeded =
true;
2981 resultType = RankedTensorType::get(newShape, sourceType.getElementType(),
2982 sourceType.getEncoding());
2983 if (newOperandNeeded) {
2984 changeNeeded =
true;
2987 Value newOperand = tensor::CastOp::create(rewriter, loc, resultType, src);
2989 newOperands[index] = newOperand;
2991 if (linalgOp.isDpsInit(opOperand))
2992 resultTypes.push_back(resultType);
2998struct InferStaticShapeOfOperands :
public OpInterfaceRewritePattern<LinalgOp> {
2999 using OpInterfaceRewritePattern<LinalgOp>::OpInterfaceRewritePattern;
3001 LogicalResult matchAndRewrite(LinalgOp linalgOp,
3002 PatternRewriter &rewriter)
const override {
3003 if (!linalgOp.hasPureTensorSemantics())
3007 if (llvm::any_of(linalgOp.getIndexingMapsArray(), [](AffineMap map) {
3008 return !map.isProjectedPermutation();
3013 llvm::DenseMap<AffineExpr, int64_t> affineExprToSize;
3014 Location loc = linalgOp.getLoc();
3018 populateMap(linalgOp, linalgOp->getOpOperands(), affineExprToSize);
3020 SmallVector<Value> newOperands;
3021 SmallVector<Type> resultTypes;
3025 bool changeNeeded =
false;
3026 newOperands.reserve(linalgOp->getNumOperands());
3027 resultTypes.reserve(linalgOp.getNumDpsInits());
3030 for (OpOperand &opOperand : linalgOp->getOpOperands()) {
3031 createNewOperandWithStaticSizes(loc, rewriter, &opOperand,
3032 affineExprToSize, linalgOp, newOperands,
3033 resultTypes, changeNeeded);
3042 Operation *newOp =
clone(rewriter, linalgOp, resultTypes, newOperands);
3043 SmallVector<Value> replacements;
3045 for (
auto it : llvm::zip(linalgOp->getResults(), newOp->
getResults())) {
3046 Value newResult = std::get<1>(it);
3047 Value oldResult = std::get<0>(it);
3048 Type newType = newResult.
getType();
3049 Type oldType = oldResult.
getType();
3050 replacements.push_back(
3051 (newType != oldType)
3052 ? tensor::CastOp::create(rewriter, loc, oldType, newResult)
3055 rewriter.
replaceOp(linalgOp, replacements);
3069LogicalResult SoftmaxOp::verify() {
3070 ShapedType inputType = getInputOperandType();
3071 ShapedType outputType = getOutputOperandType();
3073 ArrayRef<int64_t> inputShape = inputType.getShape();
3074 ArrayRef<int64_t> outputShape = outputType.getShape();
3076 return emitOpError(
"incompatible output shape");
3078 int64_t inputRank = getInputOperandRank();
3079 int64_t dimension = getDimension();
3080 if ((dimension < 0) || (dimension >= inputRank))
3081 return emitOpError(
"incorrect dimension specified");
3086SmallVector<Range> SoftmaxOp::getIterationDomain(OpBuilder &builder) {
3087 int64_t operandRank = getInputOperandRank();
3088 SmallVector<Range> loopBounds(operandRank);
3089 Location loc = getLoc();
3092 Value source = getInput();
3093 for (
auto dim : llvm::seq<int64_t>(0, operandRank)) {
3094 loopBounds[dim].offset = zero;
3095 loopBounds[dim].size =
getDimValue(builder, loc, source, dim);
3096 loopBounds[dim].stride = one;
3101SmallVector<utils::IteratorType> SoftmaxOp::getLoopIteratorTypes() {
3102 SmallVector<utils::IteratorType> iteratorTypes(getInputOperandRank(),
3103 utils::IteratorType::parallel);
3104 iteratorTypes[getDimension()] = utils::IteratorType::reduction;
3105 return iteratorTypes;
3111FailureOr<TilingResult> SoftmaxOp::getTiledImplementation(
3112 OpBuilder &builder, ArrayRef<OpFoldResult> offsets,
3113 ArrayRef<OpFoldResult> sizes, ArrayRef<InnerTileAlignment>) {
3117FailureOr<TilingResult>
3118SoftmaxOp::getTiledImplementation(OpBuilder &builder,
3119 ArrayRef<OpFoldResult> offsets,
3120 ArrayRef<OpFoldResult> sizes) {
3121 int64_t rank = getInputOperandRank();
3123 SmallVector<OpFoldResult> strides(rank, oneAttr);
3124 SmallVector<Value> tiledOperands;
3125 Operation *inputSlice =
3126 getSlice(builder, getLoc(), getInput(), offsets, sizes, strides);
3128 return emitOpError(
"failed to compute input slice");
3130 tiledOperands.emplace_back(inputSlice->
getResult(0));
3131 Operation *outputSlice =
3132 getSlice(builder, getLoc(), getOutput(), offsets, sizes, strides);
3134 return emitOpError(
"failed to compute output slice");
3136 tiledOperands.emplace_back(outputSlice->
getResult(0));
3138 SmallVector<Type, 4> resultTypes;
3139 if (hasPureTensorSemantics())
3140 resultTypes.push_back(tiledOperands[1].
getType());
3141 Operation *tiledOp =
3142 mlir::clone(builder, getOperation(), resultTypes, tiledOperands);
3144 return TilingResult{
3147 llvm::to_vector(ArrayRef<Operation *>{inputSlice, outputSlice})};
3150LogicalResult SoftmaxOp::getResultTilePosition(
3151 OpBuilder &builder,
unsigned resultNumber, ArrayRef<OpFoldResult> offsets,
3152 ArrayRef<OpFoldResult> sizes, SmallVector<OpFoldResult> &resultOffsets,
3153 SmallVector<OpFoldResult> &resultSizes) {
3154 if (resultNumber == 0) {
3155 resultOffsets.assign(offsets.begin(), offsets.end());
3156 resultSizes.assign(sizes.begin(), sizes.end());
3163LogicalResult SoftmaxOp::fold(FoldAdaptor, SmallVectorImpl<OpFoldResult> &) {
3168SoftmaxOp::reifyResultShapes(OpBuilder &
b,
3170 SmallVector<OpFoldResult> shapes;
3171 Location loc = getOperation()->getLoc();
3172 IRRewriter rewriter(
b);
3173 auto inputShapedType = llvm::cast<ShapedType>(getInputOperandType());
3174 auto outputShapedType = llvm::cast<ShapedType>(getOutputOperandType());
3175 for (int64_t dim : llvm::seq<int64_t>(0, getOutputOperandRank())) {
3176 if (!outputShapedType.isDynamicDim(dim)) {
3178 shapes.push_back(
b.getIndexAttr(inputShapedType.getDimSize(dim)));
3185 reifiedReturnShapes.emplace_back(std::move(shapes));
3189void SoftmaxOp::getEffects(
3190 SmallVectorImpl<SideEffects::EffectInstance<MemoryEffects::Effect>>
3192 for (
auto [index, operand] : llvm::enumerate(getDpsInputs())) {
3193 if (!llvm::isa<MemRefType>(operand.
getType()))
3196 &getOperation()->getOpOperand(index), 0,
3201 for (OpOperand &operand : getDpsInitsMutable()) {
3202 if (!llvm::isa<MemRefType>(operand.get().
getType()))
3233static std::tuple<SmallVector<utils::IteratorType>, SmallVector<AffineMap>>
3235 int64_t dim,
bool allParallel =
false) {
3237 utils::IteratorType::parallel);
3239 iteratorTypes[dim] = utils::IteratorType::reduction;
3243 for (
int i = 0; i < inputRank; i++) {
3250 return std::make_tuple(iteratorTypes, indexingMaps);
3255template <
typename T>
3258 auto inputType = cast<ShapedType>(input.
getType());
3260 int64_t inputRank = inputShape.size();
3261 auto [iteratorTypes, indexingMaps] =
3263 assert(indexingMaps.size() == 2 &&
3264 "We should have two maps: 1 for the input, 1 for the output");
3265 assert(indexingMaps[0].isIdentity() &&
"input map should be identity");
3267 auto genericOp = linalg::GenericOp::create(
3268 builder, loc, output.
getType(), input, output, indexingMaps,
3270 Value result = T::create(b, loc, args[0], args[1]);
3271 linalg::YieldOp::create(b, loc, result);
3273 return genericOp.getResult(0);
3281 auto inputType = cast<ShapedType>(input.
getType());
3283 int64_t inputRank = inputShape.size();
3285 builder, inputRank, dim,
true);
3286 assert(indexingMaps.size() == 2 &&
"We should have one map for each input");
3287 assert(indexingMaps[0].isIdentity() &&
"input map should be identity");
3289 indexingMaps.push_back(indexingMaps[0]);
3290 auto genericOp = linalg::GenericOp::create(
3292 indexingMaps, iteratorTypes,
3294 Value diff = arith::SubFOp::create(b, loc, args[0], args[1]);
3295 Value result = math::ExpOp::create(b, loc, diff);
3296 linalg::YieldOp::create(b, loc, result);
3298 return genericOp.getResult(0);
3308 auto inputType = cast<ShapedType>(numerator.
getType());
3310 int64_t inputRank = inputShape.size();
3312 builder, inputRank, dim,
true);
3313 assert(indexingMaps.size() == 2 &&
3314 "We should have one map for each input (2)");
3315 assert(indexingMaps[0].isIdentity() &&
"Numerator map should be identity");
3317 indexingMaps.push_back(indexingMaps[0]);
3318 auto genericOp = linalg::GenericOp::create(
3320 output, indexingMaps, iteratorTypes,
3322 Value result = arith::DivFOp::create(b, loc, args[0], args[1]);
3323 linalg::YieldOp::create(b, loc, result);
3325 return genericOp.getResult(0);
3347FailureOr<SmallVector<Value>> SoftmaxOp::decomposeOperation(OpBuilder &
b) {
3348 OpBuilder::InsertionGuard guard(
b);
3349 b.setInsertionPoint(*
this);
3350 Location loc = getLoc();
3351 Value input = getInput();
3352 ShapedType inputType = getInputOperandType();
3353 Type elementType = inputType.getElementType();
3354 int64_t reductionDim = getDimension();
3356 Value output = getOutput();
3357 dims.erase(dims.begin() + reductionDim);
3359 Value outputReduce = tensor::EmptyOp::create(
b, loc, dims, elementType);
3361 elementType,
b, loc,
3363 Value neutralForMaxFInit =
3364 linalg::FillOp::create(
b, loc, Value{neutralForMaxF}, outputReduce)
3376 linalg::FillOp::create(
b, loc, Value{zero}, outputReduce).
result();
3382 buildDivOp(
b, loc, numerator, denominator, output, reductionDim);
3383 return SmallVector<Value>{
result};
3390LogicalResult WinogradFilterTransformOp::verify() {
3391 auto filterType = cast<ShapedType>(getFilter().
getType());
3392 ArrayRef<int64_t> filterShape = filterType.getShape();
3393 int64_t filterH = filterShape[getFilterHDim()];
3394 int64_t filterW = filterShape[getFilterWDim()];
3395 WinogradConv2DFmr fmr = getFmr();
3399 if (filterH != r && filterH != 1)
3400 return emitOpError(
"expect filter height either equals to r or 1");
3401 if (filterW != r && filterW != 1)
3402 return emitOpError(
"expect filter width either equals to r or 1");
3403 if (filterH == 1 && filterW == 1)
3404 return emitOpError(
"expect either filter height or width equals to r");
3406 SmallVector<int64_t> expectedOutputShape;
3407 expectedOutputShape.push_back(filterH == r ? m + r - 1 : 1);
3408 expectedOutputShape.push_back(filterW == r ? m + r - 1 : 1);
3409 expectedOutputShape.push_back(filterShape[getFilterCDim()]);
3410 expectedOutputShape.push_back(filterShape[getFilterFDim()]);
3412 auto outputType = cast<ShapedType>(getOutput().
getType());
3413 ArrayRef<int64_t> outputShape = outputType.getShape();
3415 return emitOpError(
"the output shape is not expected");
3421WinogradFilterTransformOp::getIterationDomain(OpBuilder &builder) {
3422 Location loc = getLoc();
3425 Value filter = getFilter();
3426 int64_t filterRank = getFilterOperandRank();
3427 SmallVector<Range> loopBounds(filterRank);
3428 for (
unsigned dim = 0; dim < filterRank; ++dim) {
3429 loopBounds[dim].offset = zeroAttr;
3430 loopBounds[dim].size =
getDimValue(builder, loc, filter, dim);
3431 loopBounds[dim].stride = oneAttr;
3436SmallVector<utils::IteratorType>
3437WinogradFilterTransformOp::getLoopIteratorTypes() {
3438 int64_t filterRank = getFilterOperandRank();
3439 SmallVector<utils::IteratorType> iteratorTypes(filterRank,
3440 utils::IteratorType::parallel);
3441 return iteratorTypes;
3444LogicalResult WinogradFilterTransformOp::getResultTilePosition(
3445 OpBuilder &builder,
unsigned resultNumber, ArrayRef<OpFoldResult> offsets,
3446 ArrayRef<OpFoldResult> sizes, SmallVector<OpFoldResult> &resultOffsets,
3447 SmallVector<OpFoldResult> &resultSizes) {
3449 ShapedType filterType = getFilterOperandType();
3450 ArrayRef<int64_t> filterShape = filterType.getShape();
3451 int64_t filterH = filterShape[getFilterHDim()];
3452 int64_t filterW = filterShape[getFilterWDim()];
3453 WinogradConv2DFmr fmr = getFmr();
3456 int64_t alpha = m + r - 1;
3457 int64_t alphaH = filterH != 1 ? alpha : 1;
3458 int64_t alphaW = filterW != 1 ? alpha : 1;
3462 resultOffsets.append(
3463 {zeroAttr, zeroAttr, offsets[getFilterCDim()], offsets[getFilterFDim()]});
3465 {alphaHAttr, alphaWAttr, sizes[getFilterCDim()], sizes[getFilterFDim()]});
3473FailureOr<TilingResult> WinogradFilterTransformOp::getTiledImplementation(
3474 OpBuilder &builder, ArrayRef<OpFoldResult> offsets,
3475 ArrayRef<OpFoldResult> sizes, ArrayRef<InnerTileAlignment>) {
3485FailureOr<TilingResult> WinogradFilterTransformOp::getTiledImplementation(
3486 OpBuilder &builder, ArrayRef<OpFoldResult> offsets,
3487 ArrayRef<OpFoldResult> sizes) {
3490 ShapedType filterType = getFilterOperandType();
3491 ArrayRef<int64_t> filterShape = filterType.getShape();
3492 int64_t filterH = filterShape[getFilterHDim()];
3493 int64_t filterW = filterShape[getFilterWDim()];
3496 SmallVector<Value> tiledOperands;
3497 SmallVector<OpFoldResult> sliceOffsets, sliceSizes;
3499 sliceOffsets.append(
3500 {offsets[getFilterFDim()], zeroAttr, zeroAttr, offsets[getFilterCDim()]});
3501 sliceSizes.append({sizes[getFilterFDim()], filterHAttr, filterWAttr,
3502 sizes[getFilterCDim()]});
3503 int64_t filterRank = getFilterOperandRank();
3504 SmallVector<OpFoldResult> filterStrides(filterRank, oneAttr);
3505 Location loc = getLoc();
3506 auto filterSlice = tensor::ExtractSliceOp::create(
3507 builder, loc, getFilter(), sliceOffsets, sliceSizes, filterStrides);
3508 tiledOperands.emplace_back(filterSlice);
3510 SmallVector<OpFoldResult> resultOffsets, resultSizes;
3515 int64_t outputRank = getOutputOperandRank();
3516 SmallVector<OpFoldResult> outputStrides(outputRank, oneAttr);
3517 auto outputSlice = tensor::ExtractSliceOp::create(
3518 builder, loc, getOutput(), resultOffsets, resultSizes, outputStrides);
3519 tiledOperands.emplace_back(outputSlice);
3521 SmallVector<Type> resultTypes;
3522 resultTypes.push_back(tiledOperands[1].
getType());
3523 Operation *tiledOp =
3524 mlir::clone(builder, getOperation(), resultTypes, tiledOperands);
3526 return TilingResult{
3529 llvm::to_vector(ArrayRef<Operation *>{filterSlice, outputSlice})};
3536LogicalResult WinogradInputTransformOp::verify() {
3537 auto inputType = cast<ShapedType>(getInput().
getType());
3538 ArrayRef<int64_t> inputShape = inputType.getShape();
3539 int64_t inputH = inputShape[getInputHDim()];
3540 int64_t inputW = inputShape[getInputWDim()];
3541 WinogradConv2DFmr fmr = getFmr();
3544 int64_t tileSize = m + r - 1;
3546 auto outputType = cast<ShapedType>(getOutput().
getType());
3547 ArrayRef<int64_t> outputShape = outputType.getShape();
3548 bool leftTransform = outputShape[getOutputAlphaHDim()] != 1;
3549 bool rightTransform = outputShape[getOutputAlphaWDim()] != 1;
3551 SmallVector<int64_t> expectedOutputShape(6, inputH);
3552 if (ShapedType::isDynamic(inputH)) {
3553 expectedOutputShape[getOutputAlphaHDim()] = tileSize;
3554 expectedOutputShape[getOutputTileHDim()] = ShapedType::kDynamic;
3556 expectedOutputShape[getOutputAlphaHDim()] = leftTransform ? tileSize : 1;
3557 expectedOutputShape[getOutputTileHDim()] =
3558 leftTransform ? (inputH - (r - 1)) / m : inputH;
3560 if (ShapedType::isDynamic(inputW)) {
3561 expectedOutputShape[getOutputAlphaWDim()] = tileSize;
3562 expectedOutputShape[getOutputTileWDim()] = ShapedType::kDynamic;
3564 expectedOutputShape[getOutputAlphaWDim()] = rightTransform ? tileSize : 1;
3565 expectedOutputShape[getOutputTileWDim()] =
3566 rightTransform ? (inputW - (r - 1)) / m : inputW;
3568 expectedOutputShape[getOutputNDim()] = inputShape[getInputNDim()];
3569 expectedOutputShape[getOutputCDim()] = inputShape[getInputCDim()];
3572 return emitOpError(
"the output shape is not expected");
3578WinogradInputTransformOp::getIterationDomain(OpBuilder &builder) {
3579 Location loc = getLoc();
3582 Value output = getOutput();
3583 int64_t outputRank = getOutputOperandRank();
3584 SmallVector<Range> loopBounds(outputRank);
3585 for (
unsigned dim = 0; dim < outputRank; ++dim) {
3586 loopBounds[dim].offset = zeroAttr;
3588 loopBounds[dim].size =
getDimValue(builder, loc, output, dim);
3589 loopBounds[dim].stride = oneAttr;
3594SmallVector<utils::IteratorType>
3595WinogradInputTransformOp::getLoopIteratorTypes() {
3596 int64_t outputRank = getOutputOperandRank();
3597 SmallVector<utils::IteratorType> iteratorTypes(outputRank,
3598 utils::IteratorType::parallel);
3599 return iteratorTypes;
3602LogicalResult WinogradInputTransformOp::getResultTilePosition(
3603 OpBuilder &builder,
unsigned resultNumber, ArrayRef<OpFoldResult> offsets,
3604 ArrayRef<OpFoldResult> sizes, SmallVector<OpFoldResult> &resultOffsets,
3605 SmallVector<OpFoldResult> &resultSizes) {
3607 ShapedType outputType = getOutputOperandType();
3608 ArrayRef<int64_t> outputShape = outputType.getShape();
3609 int64_t outputAlphaH = outputShape[getOutputAlphaHDim()];
3610 int64_t outputAlphaW = outputShape[getOutputAlphaWDim()];
3612 WinogradConv2DFmr fmr = getFmr();
3615 int64_t alpha = m + r - 1;
3616 int64_t alphaH = outputAlphaH != 1 ? alpha : 1;
3617 int64_t alphaW = outputAlphaW != 1 ? alpha : 1;
3622 resultOffsets.append({zeroAttr, zeroAttr, offsets[getOutputTileHDim()],
3623 offsets[getOutputTileWDim()], offsets[getOutputNDim()],
3624 offsets[getOutputCDim()]});
3625 resultSizes.append({alphaHAttr, alphaWAttr, sizes[getOutputTileHDim()],
3626 sizes[getOutputTileWDim()], sizes[getOutputNDim()],
3627 sizes[getOutputCDim()]});
3635FailureOr<TilingResult> WinogradInputTransformOp::getTiledImplementation(
3636 OpBuilder &builder, ArrayRef<OpFoldResult> offsets,
3637 ArrayRef<OpFoldResult> sizes, ArrayRef<InnerTileAlignment>) {
3647FailureOr<TilingResult>
3648WinogradInputTransformOp::getTiledImplementation(OpBuilder &builder,
3649 ArrayRef<OpFoldResult> offsets,
3650 ArrayRef<OpFoldResult> sizes) {
3652 WinogradConv2DFmr fmr = getFmr();
3656 ShapedType outputType = getOutputOperandType();
3657 ArrayRef<int64_t> outputShape = outputType.getShape();
3658 int64_t alphaH = outputShape[getOutputAlphaHDim()];
3659 int64_t alphaW = outputShape[getOutputAlphaWDim()];
3661 Location loc = getLoc();
3663 auto identityAffineMap =
3665 auto offsetAffineMap =
3668 builder, loc, (alphaH != 1 ? offsetAffineMap : identityAffineMap),
3669 offsets[getOutputTileHDim()]);
3671 builder, loc, (alphaW != 1 ? offsetAffineMap : identityAffineMap),
3672 offsets[getOutputTileWDim()]);
3676 builder, loc, sizeAffineMap, sizes[getOutputTileHDim()]);
3678 builder, loc, sizeAffineMap, sizes[getOutputTileWDim()]);
3680 SmallVector<Value> tiledOperands;
3681 SmallVector<OpFoldResult> sliceOffsets, sliceSizes;
3683 OpFoldResult offsetH = OpFoldResult(mappedOffsetH);
3684 OpFoldResult offsetW = OpFoldResult(mappedOffsetW);
3685 sliceOffsets.append(
3686 {offsets[getOutputNDim()], offsetH, offsetW, offsets[getOutputCDim()]});
3687 OpFoldResult sizeH =
3688 alphaH != 1 ? OpFoldResult(mappedSizeH) : OpFoldResult(oneAttr);
3689 OpFoldResult sizeW =
3690 alphaW != 1 ? OpFoldResult(mappedSizeW) : OpFoldResult(oneAttr);
3692 {sizes[getOutputNDim()], sizeH, sizeW, sizes[getOutputCDim()]});
3693 int64_t inputRank = getInputOperandRank();
3694 SmallVector<OpFoldResult> inputStrides(inputRank, oneAttr);
3695 auto inputSlice = tensor::ExtractSliceOp::create(
3696 builder, loc, getInput(), sliceOffsets, sliceSizes, inputStrides);
3697 tiledOperands.emplace_back(inputSlice);
3699 SmallVector<OpFoldResult> resultOffsets, resultSizes;
3704 int64_t outputRank = getOutputOperandRank();
3705 SmallVector<OpFoldResult> outputStrides(outputRank, oneAttr);
3706 auto outputSlice = tensor::ExtractSliceOp::create(
3707 builder, loc, getOutput(), resultOffsets, resultSizes, outputStrides);
3708 tiledOperands.emplace_back(outputSlice);
3710 SmallVector<Type> resultTypes;
3711 resultTypes.push_back(tiledOperands[1].
getType());
3712 Operation *tiledOp =
3713 mlir::clone(builder, getOperation(), resultTypes, tiledOperands);
3715 return TilingResult{
3718 llvm::to_vector(ArrayRef<Operation *>{inputSlice, outputSlice})};
3725LogicalResult WinogradOutputTransformOp::verify() {
3726 auto valueType = cast<ShapedType>(getValue().
getType());
3727 ArrayRef<int64_t> valueShape = valueType.getShape();
3728 int64_t valueH = valueShape[getValueAlphaHDim()];
3729 int64_t valueW = valueShape[getValueAlphaWDim()];
3730 int64_t valueTileH = valueShape[getValueTileHDim()];
3731 int64_t valueTileW = valueShape[getValueTileWDim()];
3732 WinogradConv2DFmr fmr = getFmr();
3735 bool leftTransform = valueH != 1;
3736 bool rightTransform = valueW != 1;
3738 int64_t outputRank = getOutputOperandRank();
3739 SmallVector<int64_t> expectedOutputShape(outputRank, valueH);
3740 if (ShapedType::isDynamic(valueH) || ShapedType::isDynamic(valueTileH)) {
3741 expectedOutputShape[getOutputHDim()] = ShapedType::kDynamic;
3743 if (valueH != (leftTransform ? m + r - 1 : 1))
3744 return emitOpError(
"expect input height equals to input tile size");
3745 expectedOutputShape[getOutputHDim()] = (leftTransform ? m : 1) * valueTileH;
3747 if (ShapedType::isDynamic(valueW) || ShapedType::isDynamic(valueTileW)) {
3748 expectedOutputShape[getOutputWDim()] = ShapedType::kDynamic;
3750 if (valueW != (rightTransform ? m + r - 1 : 1))
3751 return emitOpError(
"expect input width equals to input tile size");
3752 expectedOutputShape[getOutputWDim()] =
3753 (rightTransform ? m : 1) * valueTileW;
3755 expectedOutputShape[getOutputNDim()] = valueShape[getValueNDim()];
3756 expectedOutputShape[getOutputFDim()] = valueShape[getValueFDim()];
3758 auto outputType = cast<ShapedType>(getOutput().
getType());
3759 ArrayRef<int64_t> outputShape = outputType.getShape();
3761 return emitOpError(
"the output shape is not expected");
3767WinogradOutputTransformOp::getIterationDomain(OpBuilder &builder) {
3768 Location loc = getLoc();
3771 Value value = getValue();
3772 int64_t valueRank = getValueOperandRank();
3773 SmallVector<Range> loopBounds(valueRank);
3774 for (
unsigned dim = 0; dim < valueRank; ++dim) {
3775 loopBounds[dim].offset = zeroAttr;
3777 loopBounds[dim].size =
getDimValue(builder, loc, value, dim);
3778 loopBounds[dim].stride = oneAttr;
3783SmallVector<utils::IteratorType>
3784WinogradOutputTransformOp::getLoopIteratorTypes() {
3785 int64_t valueRank = getValueOperandRank();
3786 SmallVector<utils::IteratorType> iteratorTypes(valueRank,
3787 utils::IteratorType::parallel);
3788 return iteratorTypes;
3791LogicalResult WinogradOutputTransformOp::getResultTilePosition(
3792 OpBuilder &builder,
unsigned resultNumber, ArrayRef<OpFoldResult> offsets,
3793 ArrayRef<OpFoldResult> sizes, SmallVector<OpFoldResult> &resultOffsets,
3794 SmallVector<OpFoldResult> &resultSizes) {
3795 WinogradConv2DFmr fmr = getFmr();
3799 Location loc = getLoc();
3801 auto identityAffineMap =
3806 ShapedType valueType = getValueOperandType();
3807 ArrayRef<int64_t> valueShape = valueType.getShape();
3808 int64_t valueH = valueShape[0];
3809 int64_t valueW = valueShape[1];
3811 builder, loc, (valueH != 1 ? affineMap : identityAffineMap),
3812 offsets[getValueTileHDim()]);
3814 builder, loc, (valueW != 1 ? affineMap : identityAffineMap),
3815 offsets[getValueTileWDim()]);
3817 builder, loc, affineMap, sizes[getValueTileHDim()]);
3819 builder, loc, affineMap, sizes[getValueTileWDim()]);
3822 OpFoldResult offsetH = OpFoldResult(mappedOffsetH);
3823 OpFoldResult offsetW = OpFoldResult(mappedOffsetW);
3824 OpFoldResult sizeH =
3825 valueH != 1 ? OpFoldResult(mappedSizeH) : OpFoldResult(oneAttr);
3826 OpFoldResult sizeW =
3827 valueW != 1 ? OpFoldResult(mappedSizeW) : OpFoldResult(oneAttr);
3829 resultOffsets.append(
3830 {offsets[getValueNDim()], offsetH, offsetW, offsets[getValueFDim()]});
3832 {sizes[getValueNDim()], sizeH, sizeW, sizes[getValueFDim()]});
3839FailureOr<TilingResult> WinogradOutputTransformOp::getTiledImplementation(
3840 OpBuilder &builder, ArrayRef<OpFoldResult> offsets,
3841 ArrayRef<OpFoldResult> sizes, ArrayRef<InnerTileAlignment>) {
3851FailureOr<TilingResult> WinogradOutputTransformOp::getTiledImplementation(
3852 OpBuilder &builder, ArrayRef<OpFoldResult> offsets,
3853 ArrayRef<OpFoldResult> sizes) {
3856 Location loc = getLoc();
3857 SmallVector<Value> tiledOperands;
3858 SmallVector<OpFoldResult> sliceOffsets, sliceSizes;
3860 ShapedType valueType = getValueOperandType();
3861 ArrayRef<int64_t> valueShape = valueType.getShape();
3862 int64_t alphaH = valueShape[getValueAlphaHDim()];
3863 int64_t alphaW = valueShape[getValueAlphaWDim()];
3867 sliceOffsets.append({zeroAttr, zeroAttr, offsets[getValueTileHDim()],
3868 offsets[getValueTileWDim()], offsets[getValueNDim()],
3869 offsets[getValueFDim()]});
3870 sliceSizes.append({alphaHAttr, alphaWAttr, sizes[getValueTileHDim()],
3871 sizes[getValueTileWDim()], sizes[getValueNDim()],
3872 sizes[getValueFDim()]});
3873 int64_t valueRank = getValueOperandRank();
3874 SmallVector<OpFoldResult> sliceStrides(valueRank, oneAttr);
3875 auto valueSlice = tensor::ExtractSliceOp::create(
3876 builder, loc, getValue(), sliceOffsets, sliceSizes, sliceStrides);
3877 tiledOperands.emplace_back(valueSlice);
3879 SmallVector<OpFoldResult> resultOffsets, resultSizes;
3884 int64_t outputRank = getOutputOperandRank();
3885 SmallVector<OpFoldResult> strides(outputRank, oneAttr);
3886 auto outputSlice = tensor::ExtractSliceOp::create(
3887 builder, loc, getOutput(), resultOffsets, resultSizes, strides);
3888 tiledOperands.emplace_back(outputSlice);
3890 SmallVector<Type> resultTypes;
3891 resultTypes.push_back(tiledOperands[1].
getType());
3892 Operation *tiledOp =
3893 mlir::clone(builder, getOperation(), resultTypes, tiledOperands);
3895 return TilingResult{
3898 llvm::to_vector(ArrayRef<Operation *>{valueSlice, outputSlice})};
3912 llvm::set_union(explicitSet, defaultSet);
3913 return explicitSet == defaultSet;
3933 matmulOp.getDefaultIndexingMaps(matmulOp->getContext());
3935 auto opIndexingMap = opIndexingMaps[opIndex];
3936 auto defaultIndexingMap = defaultIndexingMaps[opIndex];
3939 return matmulOp->emitOpError()
3940 <<
"Unexpected dim expression in map result.";
3943 if (!matmulOp.isValidLhsRhsBroadcastMap(opIndexingMap)) {
3944 return matmulOp->emitOpError()
3945 <<
"Invalid broadcast requested, should be (d2).";
3954template <
typename OpTy>
3957 AffineMap defaultIndexingMap,
bool isLHS) {
3958 assert((isa<BatchMatmulOp>(batchVariantMatmulOp) ||
3959 isa<BatchReduceMatmulOp>(batchVariantMatmulOp)) &&
3960 "Expected BatchMatmulOp or BatchReduceMatmulOp");
3963 return batchVariantMatmulOp->emitOpError()
3964 <<
"Unexpected result dim expression (outside the set of default "
3969 return batchVariantMatmulOp->emitOpError()
3970 <<
"no. of result dim expressions exceeds 3.";
3972 auto hasValidBatchDim = [](
AffineMap map) {
3979 if (!batchVariantMatmulOp.isValidLhsRhsBroadcastMap(opIndexingMap, isLHS))
3980 return batchVariantMatmulOp->emitOpError()
3981 <<
"Invalid broadcast requested.";
3982 }
else if (!hasValidBatchDim(opIndexingMap)) {
3983 return batchVariantMatmulOp->emitOpError()
3984 <<
"Invalid batch dimension expression.";
3992template <
typename OpTy>
3995 assert((isa<BatchMatmulOp>(batchVariantMatmulOp) ||
3996 isa<BatchReduceMatmulOp>(batchVariantMatmulOp)) &&
3997 "Expected BatchMatmulOp or BatchReduceMatmulOp");
3998 if (isa<BatchMatmulOp>(batchVariantMatmulOp) &&
4001 return batchVariantMatmulOp->emitOpError()
4002 <<
"expects 3 dims, but got (" << opIndexingMap.
getNumResults()
4005 if (isa<BatchReduceMatmulOp>(batchVariantMatmulOp) &&
4007 return batchVariantMatmulOp->emitOpError()
4008 <<
"expects 2 dims, but got (" << opIndexingMap.
getNumResults()
4012 auto areValidOutputResultDim = [&](
AffineMap outputMap) {
4013 return isa<BatchMatmulOp>(batchVariantMatmulOp)
4014 ? outputMap.getResult(0).isFunctionOfDim(0) &&
4015 outputMap.getResult(1).isFunctionOfDim(1) &&
4016 outputMap.getResult(2).isFunctionOfDim(2)
4017 : outputMap.getResult(0).isFunctionOfDim(1) &&
4018 outputMap.getResult(1).isFunctionOfDim(2);
4021 if (!areValidOutputResultDim(opIndexingMap)) {
4022 return batchVariantMatmulOp->emitOpError()
4023 <<
"Invalid output map result dimension.";
4032template <
typename OpTy>
4037 batchVariantMatmulOp.getIndexingMapsArray();
4039 batchVariantMatmulOp.getDefaultIndexingMaps(
4040 batchVariantMatmulOp->getContext());
4042 if (opIndexingMaps.size() != 3)
4043 return batchVariantMatmulOp->emitOpError()
4044 <<
"Indexing_map attribute must have 3 affine maps.";
4046 auto opIndexingMap = opIndexingMaps[opIndex];
4047 auto defaultIndexingMap = defaultIndexingMaps[opIndex];
4055 defaultIndexingMap, opIndex == 0)))
4065 if (m == 2 && r == 3)
4066 return WinogradConv2DFmr::F_2_3;
4067 if (m == 4 && r == 3)
4068 return WinogradConv2DFmr::F_4_3;
4069 if (m == 2 && r == 5)
4070 return WinogradConv2DFmr::F_2_5;
4071 return std::nullopt;
4076 case WinogradConv2DFmr::F_2_3:
4078 case WinogradConv2DFmr::F_4_3:
4080 case WinogradConv2DFmr::F_2_5:
4083 llvm_unreachable(
"Unkown WinogradConv2DFmr");
4090static FailureOr<SmallVector<SmallVector<int64_t>>>
4093 for (
auto map : maps) {
4094 AffineMapAttr attr = dyn_cast<AffineMapAttr>(map);
4098 for (
auto result : attr.getAffineMap().getResults()) {
4099 auto dim = dyn_cast<AffineDimExpr>(
result);
4102 pos.push_back(dim.getPosition());
4104 positions.push_back(pos);
4117 return indexingMaps;
4120bool MatmulOp::isDefaultIndexingMaps(Attribute attr) {
4121 ArrayAttr maps = dyn_cast<ArrayAttr>(attr);
4124 if (maps.size() != 3)
4129 return (*positions)[0] == SmallVector<int64_t>{0, 2} &&
4130 (*positions)[1] == SmallVector<int64_t>{2, 1} &&
4131 (*positions)[2] == SmallVector<int64_t>{0, 1};
4134SmallVector<utils::IteratorType> MatmulOp::getIteratorTypesArray() {
4135 return SmallVector<utils::IteratorType>{utils::IteratorType::parallel,
4136 utils::IteratorType::parallel,
4137 utils::IteratorType::reduction};
4140unsigned MatmulOp::getNumRegionArgs() {
return 3; }
4142std::string MatmulOp::getLibraryCallName() {
4146bool MatmulOp::hasDynamicIndexingMaps() {
return true; }
4150bool MatmulOp::hasUserDefinedMaps() {
4151 SmallVector<AffineMap, 3> defaultMaps =
4153 SmallVector<AffineMap, 3> explicitMaps = getIndexingMapsArray();
4154 return defaultMaps != explicitMaps;
4159void MatmulOp::regionBuilder(ImplicitLocOpBuilder &
b,
Block &block,
4160 ArrayRef<NamedAttribute> attrs,
4163 emitError() <<
"MatmulOp regionBuilder expects 3 args, got "
4168 "MatmulOp regionBuilder expects 3 args");
4169 RegionBuilderHelper helper(
b, block);
4170 SmallVector<Value> yields;
4172 TypeFn castVal = TypeFn::cast_signed;
4173 const auto *castIter = llvm::find_if(attrs, [&](
const NamedAttribute &attr) {
4174 return attr.
getName() ==
"cast";
4176 if (castIter != attrs.end()) {
4177 if (
auto attr = llvm::dyn_cast<TypeFnAttr>(castIter->getValue()))
4185 Value value3 = helper.buildBinaryFn(BinaryFn::mul, value1, value2,
emitError);
4186 if (!value1 || !value2 || !value3)
4188 Value value4 = helper.buildBinaryFn(BinaryFn::add, block.
getArgument(2),
4192 yields.push_back(value4);
4193 helper.yieldOutputs(yields);
4203bool MatmulOp::isValidLhsRhsBroadcastMap(AffineMap bcastMap) {
4204 assert(bcastMap.
getNumResults() == 1 &&
"Expected single result dim expr.");
4205 AffineExpr expr = bcastMap.
getResult(0);
4215 ArrayAttr arrayAttr;
4219 if (llvm::any_of(arrayAttr,
4220 [](
auto elt) {
return !dyn_cast<AffineMapAttr>(elt); }))
4222 <<
"element of indexing_maps array is not an affine_map";
4229 if (failed(indexingMapsAttr))
4232 if (*indexingMapsAttr ==
nullptr) {
4233 auto indexingMapAttrs = llvm::map_to_vector(
4234 MatmulOp::getDefaultIndexingMaps(parser.
getContext()),
4239 result.addAttribute(
"indexing_maps", *indexingMapsAttr);
4241 MatmulOp::getRegionBuilder());
4244void MatmulOp::print(OpAsmPrinter &p) {
4245 SmallVector<Attribute, 3> indexingMaps = llvm::map_to_vector<3>(
4246 MatmulOp::getDefaultIndexingMaps(
getContext()),
4247 [](AffineMap map) -> Attribute {
return AffineMapAttr::get(map); });
4248 if (!llvm::equal(getIndexingMaps(), indexingMaps))
4249 p <<
" indexing_maps = " << llvm::interleaved_array(getIndexingMaps());
4251 std::array<StringRef, 3> elidedAttrs = {
4252 "operandSegmentSizes",
"linalg.memoized_indexing_maps",
"indexing_maps"};
4258LogicalResult MatmulOp::verify() {
4260 if (!hasUserDefinedMaps())
4263 for (
unsigned opIndex = 0; opIndex < 2; opIndex++) {
4270LogicalResult MatmulOp::fold(FoldAdaptor, SmallVectorImpl<OpFoldResult> &) {
4274void MatmulOp::getEffects(
4275 SmallVectorImpl<SideEffects::EffectInstance<MemoryEffects::Effect>>
4277 if (hasPureTensorSemantics())
4286SmallVector<AffineMap>
4287MatmulTransposeAOp::getDefaultIndexingMaps(OpBuilder &builder) {
4288 AffineExpr d0, d1, d2;
4294 return {mapLHS, mapRHS, mapOut};
4298 ArrayAttr maps = dyn_cast<ArrayAttr>(attr);
4301 if (maps.size() != 3)
4304 if (failed(positions))
4316 MatmulOp::getRegionBuilder(), getDefaultIndexingMaps(builder));
4324 build(builder, state, inputs, outputs, attributes);
4325 auto res = dyn_cast<MatmulTransposeAOp>(builder.
create(state));
4326 assert(res &&
"builder didn't return the right type");
4336 MatmulOp::getRegionBuilder(), getDefaultIndexingMaps(builder));
4345 build(builder, state, resultTensorTypes, inputs, outputs, attributes);
4346 auto res = dyn_cast<MatmulTransposeAOp>(builder.
create(state));
4347 assert(res &&
"builder didn't return the right type");
4357 result.addAttribute(
"cast", cast);
4359 MatmulOp::getRegionBuilder(), getDefaultIndexingMaps(builder));
4368 build(builder, state, resultTensorTypes, inputs, outputs, cast, attributes);
4369 auto res = dyn_cast<MatmulTransposeAOp>(builder.
create(state));
4370 assert(res &&
"builder didn't return the right type");
4375 auto matmulOp = dyn_cast_or_null<linalg::MatmulOp>(op);
4377 matmulOp.getIndexingMapsAttr());
4381MatmulTransposeBOp::getDefaultIndexingMaps(
OpBuilder &builder) {
4388 return {mapLHS, mapRHS, mapOut};
4392 ArrayAttr maps = dyn_cast<ArrayAttr>(attr);
4395 if (maps.size() != 3)
4398 if (failed(positions))
4410 MatmulOp::getRegionBuilder(), getDefaultIndexingMaps(builder));
4418 build(builder, state, inputs, outputs, attributes);
4419 auto res = dyn_cast<MatmulTransposeBOp>(builder.
create(state));
4420 assert(res &&
"builder didn't return the right type");
4430 MatmulOp::getRegionBuilder(), getDefaultIndexingMaps(builder));
4439 build(builder, state, resultTensorTypes, inputs, outputs, attributes);
4440 auto res = dyn_cast<MatmulTransposeBOp>(builder.
create(state));
4441 assert(res &&
"builder didn't return the right type");
4451 result.addAttribute(
"cast", cast);
4453 MatmulOp::getRegionBuilder(), getDefaultIndexingMaps(builder));
4462 build(builder, state, resultTensorTypes, inputs, outputs, cast, attributes);
4463 auto res = dyn_cast<MatmulTransposeBOp>(builder.
create(state));
4464 assert(res &&
"builder didn't return the right type");
4469 auto matmulOp = dyn_cast_or_null<linalg::MatmulOp>(op);
4471 matmulOp.getIndexingMapsAttr());
4475BatchMatmulTransposeAOp::getDefaultIndexingMaps(
OpBuilder &builder) {
4482 return {mapLHS, mapRHS, mapOut};
4486 ArrayAttr maps = dyn_cast<ArrayAttr>(attr);
4489 if (maps.size() != 3)
4492 if (failed(positions))
4503 BatchMatmulOp::getRegionBuilder(),
4504 getDefaultIndexingMaps(builder));
4512 build(builder, state, inputs, outputs, attributes);
4513 auto res = dyn_cast<BatchMatmulTransposeAOp>(builder.
create(state));
4514 assert(res &&
"builder didn't return the right type");
4523 BatchMatmulOp::getRegionBuilder(),
4524 getDefaultIndexingMaps(builder));
4533 build(builder, state, resultTensorTypes, inputs, outputs, attributes);
4534 auto res = dyn_cast<BatchMatmulTransposeAOp>(builder.
create(state));
4535 assert(res &&
"builder didn't return the right type");
4543 result.addAttribute(
"cast", cast);
4545 BatchMatmulOp::getRegionBuilder(),
4546 getDefaultIndexingMaps(builder));
4555 build(builder, state, resultTensorTypes, inputs, outputs, cast, attributes);
4556 auto res = dyn_cast<BatchMatmulTransposeAOp>(builder.
create(state));
4557 assert(res &&
"builder didn't return the right type");
4562 auto matmulOp = dyn_cast_or_null<linalg::BatchMatmulOp>(op);
4564 matmulOp.getIndexingMapsAttr());
4568BatchMatmulTransposeBOp::getDefaultIndexingMaps(
OpBuilder &builder) {
4575 return {mapLHS, mapRHS, mapOut};
4579 ArrayAttr maps = dyn_cast<ArrayAttr>(attr);
4582 if (maps.size() != 3)
4585 if (failed(positions))
4596 BatchMatmulOp::getRegionBuilder(),
4597 getDefaultIndexingMaps(builder));
4605 build(builder, state, inputs, outputs, attributes);
4606 auto res = dyn_cast<BatchMatmulTransposeBOp>(builder.
create(state));
4607 assert(res &&
"builder didn't return the right type");
4616 BatchMatmulOp::getRegionBuilder(),
4617 getDefaultIndexingMaps(builder));
4626 build(builder, state, resultTensorTypes, inputs, outputs, attributes);
4627 auto res = dyn_cast<BatchMatmulTransposeBOp>(builder.
create(state));
4628 assert(res &&
"builder didn't return the right type");
4636 result.addAttribute(
"cast", cast);
4638 BatchMatmulOp::getRegionBuilder(),
4639 getDefaultIndexingMaps(builder));
4648 build(builder, state, resultTensorTypes, inputs, outputs, cast, attributes);
4649 auto res = dyn_cast<BatchMatmulTransposeBOp>(builder.
create(state));
4650 assert(res &&
"builder didn't return the right type");
4655 auto matmulOp = dyn_cast_or_null<linalg::BatchMatmulOp>(op);
4657 matmulOp.getIndexingMapsAttr());
4665 AffineMap outAffineMap = getIndexingMapsArray().pop_back_val();
4676 auto dimExpr = dyn_cast<AffineDimExpr>(
result);
4677 assert(dimExpr &&
"affine_map is a projected permutation");
4678 dimsInOutput[dimExpr.getPosition()] =
true;
4682 for (
auto dimOccursInOutput : dimsInOutput)
4683 iteratorTypes.push_back(dimOccursInOutput ? utils::IteratorType::parallel
4684 : utils::IteratorType::reduction);
4686 return iteratorTypes;
4689unsigned ContractOp::getNumRegionArgs() {
return 3; }
4692void ContractOp::regionBuilder(ImplicitLocOpBuilder &
b,
Block &block,
4693 ArrayRef<NamedAttribute> attrs,
4696 emitError() <<
"ContractOp regionBuilder expects 3 args, got "
4701 "ContractOp regionBuilder expects 3 args");
4702 RegionBuilderHelper helper(
b, block);
4704 TypeFn castSignedness = TypeFn::cast_signed;
4705 auto castIter = llvm::find_if(attrs, [&](
const NamedAttribute &attr) {
4706 return attr.
getName() ==
"cast";
4708 if (castIter != attrs.end()) {
4709 if (
auto attr = llvm::dyn_cast<TypeFnAttr>(castIter->getValue()))
4715 Value lhsAtOutType =
4716 helper.buildTypeFn(castSignedness, outType, block.
getArgument(0));
4717 Value rhsAtOutType =
4718 helper.buildTypeFn(castSignedness, outType, block.
getArgument(1));
4719 Value productAtOutType = helper.buildBinaryFn(BinaryFn::mul, lhsAtOutType,
4721 if (!productAtOutType)
4727 helper.yieldOutputs({
result});
4730ParseResult ContractOp::parse(OpAsmParser &parser, OperationState &
result) {
4732 if (
failed(indexingMapsAttr) || *indexingMapsAttr ==
nullptr)
4734 "expected 'indexing_maps' attribute");
4735 result.addAttribute(
"indexing_maps", *indexingMapsAttr);
4741void ContractOp::print(OpAsmPrinter &p) {
4742 p <<
" indexing_maps = " << llvm::interleaved_array(getIndexingMaps());
4744 p, getOperation(), getInputs(), getOutputs(),
4745 {
"indexing_maps",
"operandSegmentSizes"});
4756 bool isInput,
int &iterationSpaceDims,
4761 return emitError() <<
"provided affine_map is not a projected permutation";
4763 if (
auto shapedType = dyn_cast<ShapedType>(operandType)) {
4766 <<
"ranks of shaped operand and results of corresponding "
4767 "affine_map differ";
4770 <<
"affine_map specifies shaped access while operand has "
4774 if (iterationSpaceDims == -1) {
4778 }
else if (iterationSpaceDims != (
int)affineMap.
getNumDims()) {
4779 return emitError() <<
"iteration spaces of provided affine_maps differ";
4784 auto affineDimExpr = dyn_cast<AffineDimExpr>(affineExpr);
4786 llvm_unreachable(
"affine_map is a projected permutation");
4789 inOccurrences[affineDimExpr.getPosition()] += 1;
4791 outOccurrences[affineDimExpr.getPosition()] += 1;
4807 bool hasContractingDim =
false;
4808 for (
size_t dimIndex = 0; dimIndex < iterationSpaceDims; dimIndex++) {
4809 size_t inOccCount = inOccurrences[dimIndex];
4810 size_t outOccCount = outOccurrences[dimIndex];
4813 hasContractingDim |= inOccCount == 2 && outOccCount == 0;
4815 if (inOccCount == 0 && outOccCount == 0)
4816 return emitError() <<
"iteration space dim at index " << dimIndex
4817 <<
" not used to access any operand";
4828 if (inOccCount == 1 && outOccCount != 1)
4830 <<
"iteration space dim at index " << dimIndex
4831 <<
" is neither a contracting dim nor of parallel iteration type";
4834 if (!hasContractingDim)
4836 <<
"'indexing_maps' do not specify a contracting dimension";
4841LogicalResult ContractOp::verify() {
4842 int iterationSpaceDims = -1;
4850 for (
auto &&[affineMap, operandType, isInput] :
4851 llvm::zip(getIndexingMapsArray(), getOperandTypes(),
4854 affineMap, operandType, isInput, iterationSpaceDims, inOccurrences,
4855 outOccurrences, [&]() {
return emitError(); })))
4860 inOccurrences, outOccurrences,
4864LogicalResult ContractOp::fold(FoldAdaptor, SmallVectorImpl<OpFoldResult> &) {
4868void ContractOp::getEffects(
4869 SmallVectorImpl<SideEffects::EffectInstance<MemoryEffects::Effect>>
4871 if (hasPureTensorSemantics())
4883SmallVector<AffineMap>
4884BatchMatmulOp::getDefaultIndexingMaps(MLIRContext *context) {
4885 AffineExpr d0, d1, d2, d3;
4886 SmallVector<AffineMap> indexingMaps;
4888 indexingMaps.push_back(
AffineMap::get(4, 0, {d0, d1, d3}, context));
4889 indexingMaps.push_back(
AffineMap::get(4, 0, {d0, d3, d2}, context));
4890 indexingMaps.push_back(
AffineMap::get(4, 0, {d0, d1, d2}, context));
4891 return indexingMaps;
4894bool BatchMatmulOp::isDefaultIndexingMaps(Attribute attr) {
4895 ArrayAttr maps = dyn_cast<ArrayAttr>(attr);
4898 if (maps.size() != 3)
4903 return (*positions)[0] == SmallVector<int64_t>{0, 1, 3} &&
4904 (*positions)[1] == SmallVector<int64_t>{0, 3, 2} &&
4905 (*positions)[2] == SmallVector<int64_t>{0, 1, 2};
4908SmallVector<utils::IteratorType> BatchMatmulOp::getIteratorTypesArray() {
4909 return SmallVector<utils::IteratorType>{
4910 utils::IteratorType::parallel, utils::IteratorType::parallel,
4911 utils::IteratorType::parallel, utils::IteratorType::reduction};
4914unsigned BatchMatmulOp::getNumRegionArgs() {
return 3; }
4916std::string BatchMatmulOp::getLibraryCallName() {
4922bool BatchMatmulOp::hasUserDefinedMaps() {
4923 SmallVector<AffineMap, 3> defaultMaps =
4925 SmallVector<AffineMap, 3> explicitMaps = getIndexingMapsArray();
4926 return defaultMaps != explicitMaps;
4936bool BatchMatmulOp::isValidLhsRhsBroadcastMap(AffineMap bcastMap,
bool isLHS) {
4938 "Expected less than 3 result dim expr.");
4939 bool isValid =
false;
4940 enum Indices { batchPos, mPos, nPos, kPos };
4942 AffineExpr expr = bcastMap.
getResult(0);
4945 AffineExpr expr0 = bcastMap.
getResult(0);
4946 AffineExpr expr1 = bcastMap.
getResult(1);
4951 : ((expr0.isFunctionOfDim(batchPos) &&
4952 expr1.isFunctionOfDim(kPos)) ||
4953 (expr0.isFunctionOfDim(kPos) && expr1.isFunctionOfDim(nPos)));
4958void BatchMatmulOp::regionBuilder(
4959 ImplicitLocOpBuilder &
b,
Block &block, ArrayRef<NamedAttribute> attrs,
4962 emitError() <<
"BatchMatmulOp regionBuilder expects 3 args, got "
4967 "BatchMatmulOp regionBuilder expects 3 args");
4968 RegionBuilderHelper helper(
b, block);
4969 SmallVector<Value> yields;
4971 TypeFn castVal = TypeFn::cast_signed;
4972 auto castIter = llvm::find_if(attrs, [&](
const NamedAttribute &attr) {
4973 return attr.
getName() ==
"cast";
4975 if (castIter != attrs.end()) {
4976 if (
auto attr = llvm::dyn_cast<TypeFnAttr>(castIter->getValue()))
4981 Value castValA = helper.buildTypeFn(castVal, toType, block.
getArgument(0));
4982 Value castValB = helper.buildTypeFn(castVal, toType, block.
getArgument(1));
4984 helper.buildBinaryFn(BinaryFn::mul, castValA, castValB,
emitError);
4985 if (!castValA || !castValB || !mulVal)
4987 Value addVal = helper.buildBinaryFn(BinaryFn::add, block.
getArgument(2),
4991 yields.push_back(addVal);
4992 helper.yieldOutputs(yields);
4995ParseResult BatchMatmulOp::parse(OpAsmParser &parser, OperationState &
result) {
4996 SmallVector<Attribute, 3> indexingMapsAttr;
5008 if (!isa<AffineMapAttr>(mapAttr)) {
5010 "expected affine map attribute");
5012 indexingMapsAttr.push_back(mapAttr);
5022 if (indexingMapsAttr.empty()) {
5023 indexingMapsAttr = llvm::map_to_vector(
5024 BatchMatmulOp::getDefaultIndexingMaps(parser.
getContext()),
5025 [](AffineMap map) -> Attribute { return AffineMapAttr::get(map); });
5027 result.addAttribute(
"indexing_maps",
5030 return ::parseNamedStructuredOp(parser,
result,
5031 BatchMatmulOp::getNumRegionArgs(),
5032 BatchMatmulOp::getRegionBuilder());
5035void BatchMatmulOp::print(OpAsmPrinter &p) {
5036 SmallVector<Attribute, 3> indexingMaps = llvm::map_to_vector<3>(
5037 BatchMatmulOp::getDefaultIndexingMaps(
getContext()),
5038 [](AffineMap map) -> Attribute {
return AffineMapAttr::get(map); });
5039 if (!llvm::equal(getIndexingMaps(), indexingMaps))
5040 p <<
" indexing_maps = " << llvm::interleaved_array(getIndexingMaps());
5042 std::array<StringRef, 3> elidedAttrs = {
5043 "operandSegmentSizes",
"linalg.memoized_indexing_maps",
"indexing_maps"};
5049LogicalResult BatchMatmulOp::verify() {
5052 if (!hasUserDefinedMaps())
5055 for (
unsigned opIndex = 0; opIndex < 3; opIndex++) {
5062LogicalResult BatchMatmulOp::fold(FoldAdaptor,
5063 SmallVectorImpl<OpFoldResult> &) {
5067void BatchMatmulOp::getEffects(
5068 SmallVectorImpl<SideEffects::EffectInstance<MemoryEffects::Effect>>
5070 if (hasPureTensorSemantics())
5084struct ArityGroupAndKind {
5086 ElementwiseArityGroup arityGroup;
5092 TernaryFn ternaryFn;
5096unsigned getArityGroupAsUInt(ElementwiseArityGroup arityGroup) {
5097 return static_cast<unsigned>(arityGroup);
5102 constexpr int lastUnary =
static_cast<int>(ElementwiseCaseLimits::LastUnary);
5103 constexpr int lastBinary =
5104 static_cast<int>(ElementwiseCaseLimits::LastBinary);
5105 constexpr int lastTernary =
5106 static_cast<int>(ElementwiseCaseLimits::LastTernary);
5108 int val =
static_cast<int>(kind);
5109 ArityGroupAndKind
result;
5111 if (val < lastUnary) {
5112 result.arityGroup = ElementwiseArityGroup::Unary;
5113 result.kind.unaryFn =
static_cast<UnaryFn
>(val);
5116 if (val < lastBinary) {
5117 result.arityGroup = ElementwiseArityGroup::Binary;
5118 result.kind.binaryFn =
static_cast<BinaryFn
>(val - lastUnary);
5121 if (val >= lastTernary) {
5122 llvm_unreachable(
"unhandled ElementwiseFn");
5124 result.arityGroup = ElementwiseArityGroup::Ternary;
5125 result.kind.ternaryFn =
static_cast<TernaryFn
>(val - lastBinary);
5130 auto rank = getResultRank();
5135ElementwiseOp::getDefaultIndexingMaps(
unsigned numMaps,
unsigned numDims,
5141ParseResult ElementwiseOp::parse(OpAsmParser &parser, OperationState &
result) {
5144 ElementwiseKindAttr kindAttr;
5145 mlir::linalg::ElementwiseKind elemwiseKindVal;
5148 elemwiseKindVal = kindAttr.getValue();
5149 result.addAttribute(
"kind", kindAttr);
5152 SmallVector<Attribute, 3> indexingMapsAttr;
5162 if (!isa<AffineMapAttr>(mapAttr))
5164 "expected affine map attribute");
5165 indexingMapsAttr.push_back(mapAttr);
5176 getArityGroupAsUInt(arityGroupAndKind.arityGroup) + 1 ;
5178 ElementwiseOp::getRegionBuilder())) {
5180 "unable to parse elemwise op");
5184 if (indexingMapsAttr.empty()) {
5187 auto resultType =
result.operands[
result.operands.size() - 1].getType();
5188 auto shapedType = llvm::dyn_cast<ShapedType>(resultType);
5191 "return type needs to be shaped type");
5192 auto numDims = shapedType.getRank();
5193 indexingMapsAttr = llvm::map_to_vector(
5194 ElementwiseOp::getDefaultIndexingMaps(numRegionArgs, numDims,
5196 [](AffineMap map) -> Attribute { return AffineMapAttr::get(map); });
5199 result.addAttribute(
"indexing_maps",
5204void ElementwiseOp::print(OpAsmPrinter &p) {
5207 SmallVector<StringRef, 3> elidedAttrs = {
"operandSegmentSizes",
"kind",
5211 unsigned numDims = getResultRank();
5213 SmallVector<Attribute, 3> indexingMaps = llvm::map_to_vector<3>(
5214 ElementwiseOp::getDefaultIndexingMaps(arity + 1 , numDims,
5216 [](AffineMap map) -> Attribute {
return AffineMapAttr::get(map); });
5218 if (!llvm::equal(getIndexingMaps(), indexingMaps))
5219 p <<
" indexing_maps = " << llvm::interleaved_array(getIndexingMaps());
5227void ElementwiseOp::regionBuilder(
5228 ImplicitLocOpBuilder &
b,
Block &block, ArrayRef<NamedAttribute> attrs,
5230 std::optional<ElementwiseKind> elemwiseKind;
5231 for (
auto attr : attrs) {
5232 if (attr.
getName() ==
"kind") {
5233 auto kindAttr = dyn_cast<ElementwiseKindAttr>(attr.
getValue());
5236 emitError() <<
"'kind' must be an ElementwiseKindAttr";
5239 elemwiseKind = kindAttr.getValue();
5244 if (!elemwiseKind) {
5246 emitError() <<
"missing required 'kind' attribute";
5251 auto arityGroup = groupAndKind.arityGroup;
5252 auto kind = groupAndKind.kind;
5254 getArityGroupAsUInt(arityGroup) + 1 ) {
5255 emitError() <<
"Elementwise regionBuilder expects "
5256 << (getArityGroupAsUInt(arityGroup) + 1) <<
" args, got "
5261 getArityGroupAsUInt(arityGroup) + 1
5262 &&
"Elementwise regionBuilder number of block args mismatch");
5264 RegionBuilderHelper helper(
b, block);
5265 SmallVector<Value> yields;
5268 if (arityGroup == ElementwiseArityGroup::Unary) {
5271 }
else if (arityGroup == ElementwiseArityGroup::Binary) {
5275 }
else if (arityGroup == ElementwiseArityGroup::Ternary) {
5280 assert(
false &&
"found unhandled category in elemwise");
5283 yields.push_back(
result);
5284 helper.yieldOutputs(yields);
5287LogicalResult ElementwiseOp::fold(FoldAdaptor,
5288 SmallVectorImpl<OpFoldResult> &) {
5292void ElementwiseOp::getEffects(
5293 SmallVectorImpl<SideEffects::EffectInstance<MemoryEffects::Effect>>
5295 if (hasPureTensorSemantics())
5308template <
typename OpTy,
typename>
5311 ShapedType packedType = (std::is_same<OpTy, PackOp>::value)
5312 ? packOrUnPack.getDestType()
5313 : packOrUnPack.getSourceType();
5314 ShapedType unpackedType = (std::is_same<OpTy, PackOp>::value)
5315 ? packOrUnPack.getSourceType()
5316 : packOrUnPack.getDestType();
5318 packedType.getShape().take_front(unpackedType.getRank()));
5319 if (!packOrUnPack.getOuterDimsPerm().empty()) {
5340 for (
auto it : llvm::zip(cast<ShapedType>(newPackedTy)
5342 .take_back(mixedTiles.size()),
5344 int64_t dimSize = std::get<0>(it);
5345 if (dimSize == ShapedType::kDynamic) {
5346 newMixedTileSizes.push_back(std::get<1>(it));
5349 newMixedTileSizes.push_back(rewriter.
getIndexAttr(dimSize));
5352 return newMixedTileSizes;
5355template <
typename OpTy>
5359 static_assert(llvm::is_one_of<OpTy, PackOp, UnPackOp>::value,
5360 "applies to only pack or unpack operations");
5361 int64_t destRank = op.getDestRank();
5363 for (
auto dim : llvm::seq<int64_t>(0, destRank))
5364 reifiedReturnShapes[0][dim] =
5369template <
typename OpTy>
5371 static_assert(llvm::is_one_of<OpTy, PackOp, UnPackOp>::value,
5372 "applies to only pack or unpack operations");
5376 assert(tiles.size() == dimsToTile.size() &&
5377 "tiles must match indices of dimension to block");
5379 for (
auto i : llvm::seq<int64_t>(0, dimsToTile.size()))
5380 dimAndTileMapping[dimsToTile[i]] = tiles[i];
5381 return dimAndTileMapping;
5384template <
typename OpTy>
5386 static_assert(llvm::is_one_of<OpTy, PackOp, UnPackOp>::value,
5387 "applies to only pack or unpack operations");
5390 unsigned dynamicValIndex = 0;
5391 for (
int64_t staticTile : op.getStaticInnerTiles()) {
5392 if (ShapedType::isStatic(staticTile))
5395 mixedInnerTiles.push_back(op.getInnerTiles()[dynamicValIndex++]);
5397 return mixedInnerTiles;
5400template <
typename OpTy>
5402 static_assert(llvm::is_one_of<OpTy, PackOp, UnPackOp>::value,
5403 "applies to only pack or unpack operations");
5416 size_t dimsPosSize = dimsPos.size();
5417 if (dimsPosSize > rank)
5420 if (dimsPosSize != uniqued.size())
5422 return llvm::any_of(dimsPos, [rank](
int64_t dimPos) {
5423 return dimPos < 0 || dimPos >=
static_cast<int64_t>(rank);
5427template <
typename OpTy>
5429 static_assert(llvm::is_one_of<OpTy, PackOp, UnPackOp>::value,
5430 "applies to only pack or unpack operations");
5431 Operation *op = packOrUnPack.getOperation();
5441 if (!packOrUnPack.getSourceType().hasRank() ||
5442 !packOrUnPack.getDestType().hasRank())
5443 return op->
emitError(
"expected both source and destination to have rank");
5446 if (!packOrUnPack.hasPureBufferSemantics() &&
5447 !packOrUnPack.hasPureTensorSemantics())
5448 return op->
emitError(
"mixing tensor and buffer semantics is not allowed");
5449 const unsigned numResults = packOrUnPack.getNumResults();
5450 if (packOrUnPack.hasPureTensorSemantics() && numResults != 1)
5451 return op->
emitError(
"expected 1 result, got ") << numResults;
5452 if (packOrUnPack.hasPureBufferSemantics() && numResults != 0)
5453 return op->
emitError(
"expected 0 results, got ") << numResults;
5457 if (hasZeros(mixedTiles))
5458 return op->
emitError(
"invalid zero tile factor");
5461 ShapedType unpackedType = (std::is_same<OpTy, PackOp>::value)
5462 ? packOrUnPack.getSourceType()
5463 : packOrUnPack.getDestType();
5464 size_t unpackedRank = unpackedType.getRank();
5468 return op->
emitError(
"invalid inner_dims_pos vector");
5470 return op->
emitError(
"invalid outer_dims_perm vector");
5471 if (!outerDimPerm.empty() && outerDimPerm.size() != unpackedRank)
5472 return op->
emitError(
"outer_dims_perm must be a permutation or empty");
5476 if (mixedTiles.size() > unpackedRank) {
5477 return op->
emitError(
"tiling factors must be less than or equal to the "
5478 "input rank for pack or output rank for unpack");
5480 if (mixedTiles.size() != innerDimsPos.size()) {
5482 "tiling factors must equal the number of dimensions to tile");
5485 ShapedType packedType = (std::is_same<OpTy, PackOp>::value)
5486 ? packOrUnPack.getDestType()
5487 : packOrUnPack.getSourceType();
5488 size_t packedRank = packedType.getRank();
5490 size_t expectedPackedRank = unpackedRank + mixedTiles.size();
5491 if (expectedPackedRank != packedRank) {
5493 "packed rank != (unpacked rank + num tiling factors), got ")
5494 << packedRank <<
" != " << expectedPackedRank;
5501 unpackedType.getShape(), packOrUnPack.getStaticTiles(),
5502 packOrUnPack.getInnerDimsPos(), packOrUnPack.getOuterDimsPerm());
5503 for (
auto it : llvm::enumerate(llvm::zip(
5504 packedType.getShape().take_back(mixedTiles.size()), mixedTiles))) {
5505 int64_t dimSize = std::get<0>(it.value());
5507 llvm::dyn_cast_if_present<Attribute>(std::get<1>(it.value()))) {
5508 IntegerAttr intAttr = dyn_cast_or_null<IntegerAttr>(attr);
5509 int64_t staticTileSize = intAttr.getValue().getSExtValue();
5510 if (dimSize != staticTileSize)
5512 "mismatch in inner tile sizes specified and shaped of "
5513 "tiled dimension in the packed type at index ")
5514 << it.index() <<
": got " << dimSize <<
" != " << staticTileSize;
5515 }
else if (!ShapedType::isDynamic(dimSize)) {
5516 return op->
emitError(
"mismatch in inner tile sizes specified at index ")
5517 << it.index() <<
": got static shape " << dimSize
5518 <<
" but dynamic tile size";
5523 auto elementType = unpackedType.getElementType();
5524 Type expectedType, actualType;
5525 if (packOrUnPack.hasPureTensorSemantics()) {
5526 expectedType = RankedTensorType::get(expectedPackedShape, elementType);
5527 actualType = RankedTensorType::get(packedType.getShape(), elementType);
5529 expectedType = MemRefType::get(expectedPackedShape, elementType);
5530 actualType = MemRefType::get(packedType.getShape(), elementType);
5533 << expectedType <<
" for the packed domain value, got "
5546struct PackOrUnPackTransposeResult {
5553template <
typename OpTy>
5554static PackOrUnPackTransposeResult
5558 static_assert(llvm::is_one_of<OpTy, PackOp, UnPackOp>::value,
5559 "applies to only pack or unpack operations");
5560 assert((!innerPermutation.empty() || !outerPermutation.empty()) &&
5561 "some permutation must be non-empty");
5562 PackOrUnPackTransposeResult metadata;
5563 metadata.innerDimsPos =
5565 metadata.innerTiles =
5567 int64_t numOuterDims = std::is_same<OpTy, PackOp>::value
5568 ? packOrUnPackOp.getSourceRank()
5569 : packOrUnPackOp.getDestRank();
5570 metadata.outerDimsPerm =
5571 packOrUnPackOp.getOuterDimsPerm().empty()
5572 ? llvm::to_vector(llvm::seq<int64_t>(0, numOuterDims))
5574 if (!innerPermutation.empty()) {
5575 assert(innerPermutation.size() == metadata.innerDimsPos.size() &&
5577 "invalid inner permutation");
5581 if (!outerPermutation.empty()) {
5582 assert(outerPermutation.size() == metadata.outerDimsPerm.size() &&
5584 "invalid outer permutation");
5595 if (!getResults().empty())
5596 setNameFn(getResult(),
"pack");
5606 Type sourceType, destType, resultType;
5623 SmallVector<int64_t> outerDimsPermVec;
5626 if (parser.parseInteger(value))
5628 outerDimsPermVec.push_back(value);
5638 SmallVector<int64_t> innerDimsPosVec;
5641 if (parser.parseInteger(value))
5643 innerDimsPosVec.push_back(value);
5655 for (
auto val : staticTilesAttr.
asArrayRef())
5656 staticTiles.push_back(val);
5673 bool isMemRef = llvm::isa<MemRefType>(sourceType);
5676 "pack/unpack requires '->' and destination type");
5680 resultType = destType;
5686 if (!paddingValue.empty() &&
5691 if (!dynamicTiles.empty() &&
5696 result.addAttribute(
"static_inner_tiles",
5698 result.addAttribute(
"inner_dims_pos", innerDimsPos);
5700 result.addAttribute(
"outer_dims_perm", outerDimsPerm);
5702 SmallVector<int32_t> segmentSizes = {
5703 1, 1,
static_cast<int32_t
>(paddingValue.size()),
5704 static_cast<int32_t
>(dynamicTiles.size())};
5705 result.addAttribute(
"operandSegmentSizes",
5709 result.addTypes(resultType);
5714void PackOp::print(OpAsmPrinter &p) {
5715 p <<
" " << getSource();
5717 if (getPaddingValue()) {
5718 p <<
" padding_value(" << getPaddingValue() <<
" : "
5719 << getPaddingValue().getType() <<
")";
5722 if (!getOuterDimsPerm().empty()) {
5723 p <<
" outer_dims_perm = [";
5724 llvm::interleaveComma(getOuterDimsPerm(), p);
5728 p <<
" inner_dims_pos = [";
5729 llvm::interleaveComma(getInnerDimsPos(), p);
5732 p <<
" inner_tiles = ";
5735 p <<
" into " << getDest();
5738 {
"static_inner_tiles",
"inner_dims_pos",
5739 "outer_dims_perm",
"operandSegmentSizes"});
5741 p <<
" : " << getSource().getType();
5742 p <<
" -> " << getDest().getType();
5745void PackOp::build(OpBuilder &builder, OperationState &state, Value source,
5746 Value dest, ArrayRef<int64_t> innerDimsPos,
5747 ArrayRef<OpFoldResult> innerTiles,
5748 std::optional<Value> paddingValue,
5749 ArrayRef<int64_t> outerDimsPerm) {
5750 assert(innerDimsPos.size() == innerTiles.size() &&
5751 "number of tile sizes specified must match the specified number of "
5752 "original dimensions to be tiled");
5753 SmallVector<int64_t> staticTileSizes;
5754 SmallVector<Value> dynamicTileSizes;
5756 build(builder, state, dest.
getType(), source, dest,
5757 paddingValue ? *paddingValue :
nullptr,
5758 outerDimsPerm.empty() ?
nullptr
5765PackOp::reifyResultShapes(OpBuilder &builder,
5774SmallVector<OpFoldResult> PackOp::getMixedTiles() {
5778SmallVector<int64_t> PackOp::getStaticTiles() {
5782ArrayRef<int64_t> PackOp::getAllOuterDims() {
5783 ShapedType inputType = getSourceType();
5784 int64_t inputRank = inputType.getRank();
5785 return getDestType().getShape().take_front(inputRank);
5788SmallVector<int64_t> PackOp::getTiledOuterDims() {
5789 auto innerDimsPos = getInnerDimsPos();
5790 SmallVector<int64_t> outerDims(getAllOuterDims());
5791 SmallVector<int64_t> res;
5794 SmallVector<int64_t> outerDimPermInv(getOuterDimsPerm());
5796 if (!outerDimPermInv.empty())
5800 for (
auto index : innerDimsPos)
5801 res.push_back(outerDims[index]);
5806bool PackOp::requirePaddingValue(ArrayRef<int64_t> inputShape,
5807 ArrayRef<int64_t> innerDimsPos,
5808 ArrayRef<int64_t> outputShape,
5809 ArrayRef<int64_t> outerDimsPerm,
5810 ArrayRef<OpFoldResult> innerTiles) {
5811 SmallVector<int64_t> outputTileSizes(
5812 outputShape.take_front(inputShape.size()));
5813 if (!outerDimsPerm.empty()) {
5814 assert(outerDimsPerm.size() == outputTileSizes.size() &&
5815 "expected output and outer_dims_perm to have same size");
5819 for (
auto [pos, tileSize] : llvm::zip_equal(innerDimsPos, innerTiles)) {
5820 if (ShapedType::isDynamic(inputShape[pos]))
5823 if (!constantTile) {
5824 if (ShapedType::isStatic(outputTileSizes[pos]) &&
5825 (inputShape[pos] % outputTileSizes[pos] != 0))
5828 assert(*constantTile != 0 &&
"static tile size can't be zero");
5829 if (inputShape[pos] % (*constantTile) != 0) {
5837bool PackOp::requirePaddingValueStrict(ArrayRef<int64_t> inputShape,
5838 ArrayRef<int64_t> innerDimsPos,
5839 ArrayRef<int64_t> outputShape,
5840 ArrayRef<int64_t> outerDimsPerm,
5841 ArrayRef<OpFoldResult> innerTiles) {
5842 SmallVector<int64_t> outputTileSizes(
5843 outputShape.take_front(inputShape.size()));
5844 if (!outerDimsPerm.empty()) {
5845 assert(outerDimsPerm.size() == outputTileSizes.size() &&
5846 "expected output and outer_dims_perm to have same size");
5850 for (
auto [pos, tileSize] : llvm::zip_equal(innerDimsPos, innerTiles)) {
5851 if (ShapedType::isDynamic(inputShape[pos]) ||
5852 ShapedType::isDynamic(outputTileSizes[pos]))
5857 assert(*constantTile != 0 &&
"static tile size can't be zero");
5858 if (inputShape[pos] % (*constantTile) != 0)
5864LogicalResult PackOp::verify() {
5871 auto paddingValue = getPaddingValue();
5874 return emitOpError(
"expected padding_value has ")
5875 << getSourceType().getElementType()
5876 <<
" but got: " << paddingValue.getType();
5879 if (!paddingValue &&
5880 requirePaddingValue(getSourceType().
getShape(), getInnerDimsPos(),
5881 getDestType().
getShape(), getOuterDimsPerm(),
5884 "invalid tile factor or output size provided. Only full tiles are "
5885 "supported when padding_value is not set");
5892static SmallVector<int64_t>
5895 for (
auto o : ofrs) {
5897 if (llvm::dyn_cast_if_present<Value>(o))
5898 result.push_back(ShapedType::kDynamic);
5910 for (
auto tiledDim : llvm::enumerate(llvm::to_vector(innerDimsPos))) {
5911 if (ShapedType::isDynamic(resultShape[tiledDim.value()]))
5913 if (ShapedType::isDynamic(innerTileSizes[tiledDim.index()])) {
5914 resultShape[tiledDim.value()] = ShapedType::kDynamic;
5917 resultShape[tiledDim.value()] = llvm::divideCeilSigned(
5918 resultShape[tiledDim.value()], innerTileSizes[tiledDim.index()]);
5922 if (!outerDimsPerm.empty())
5926 resultShape.append(innerTileSizes.begin(), innerTileSizes.end());
5930SmallVector<OpFoldResult> PackOp::getResultShape(
5931 OpBuilder &builder, Location loc, ArrayRef<OpFoldResult> sourceDims,
5932 ArrayRef<OpFoldResult> innerTileSizes, ArrayRef<int64_t> innerDimsPos,
5933 ArrayRef<int64_t> outerDimsPerm) {
5934 SmallVector<OpFoldResult> resultDims = llvm::to_vector(sourceDims);
5938 AffineExpr ceilDivExpr = s0.
ceilDiv(s1);
5939 for (
auto tiledDim : llvm::enumerate(llvm::to_vector(innerDimsPos))) {
5941 builder, loc, ceilDivExpr,
5942 {resultDims[tiledDim.value()], innerTileSizes[tiledDim.index()]});
5944 if (!outerDimsPerm.empty())
5946 resultDims.append(innerTileSizes.begin(), innerTileSizes.end());
5948 SmallVector<int64_t> resultTypeShape =
5951 innerDimsPos, outerDimsPerm);
5957 for (
unsigned i = 0; i < resultDims.size(); ++i) {
5958 if (ShapedType::isStatic(resultTypeShape[i]))
5967RankedTensorType PackOp::inferPackedTensorType(
5968 RankedTensorType sourceType, ArrayRef<int64_t> innerTileSizes,
5969 ArrayRef<int64_t> innerDimsPos, ArrayRef<int64_t> outerDimsPerm) {
5970 SmallVector<int64_t> resultShape = inferPackedShape(
5971 sourceType.getShape(), innerTileSizes, innerDimsPos, outerDimsPerm);
5972 return RankedTensorType::get(resultShape, sourceType.getElementType());
5975MemRefType PackOp::inferPackedMemRefType(MemRefType sourceType,
5976 ArrayRef<int64_t> innerTileSizes,
5977 ArrayRef<int64_t> innerDimsPos,
5978 ArrayRef<int64_t> outerDimsPerm) {
5979 SmallVector<int64_t> resultShape = inferPackedShape(
5980 sourceType.getShape(), innerTileSizes, innerDimsPos, outerDimsPerm);
5981 return MemRefType::get(resultShape, sourceType.getElementType());
5984Value PackOp::createDestinationTensor(OpBuilder &
b, Location loc, Value source,
5985 ArrayRef<OpFoldResult> innerTileSizes,
5986 ArrayRef<int64_t> innerDimsPos,
5987 ArrayRef<int64_t> outerDimsPerm) {
5988 AffineExpr dim0, dim1;
5990 auto ceilDiv = [&](OpFoldResult v1, OpFoldResult v2) -> OpFoldResult {
5995 SmallVector<OpFoldResult> mixedSizes;
5996 for (
auto [index, value] : llvm::enumerate(
5997 llvm::cast<RankedTensorType>(source.
getType()).getShape())) {
5998 if (ShapedType::isDynamic(value))
5999 mixedSizes.push_back(
6000 tensor::DimOp::create(
b, loc, source, index).getResult());
6002 mixedSizes.push_back(
b.getIndexAttr(value));
6004 for (
auto it : llvm::zip(innerDimsPos, innerTileSizes)) {
6005 int64_t dimPos = std::get<0>(it);
6006 OpFoldResult tileSize = std::get<1>(it);
6007 mixedSizes[dimPos] = ceilDiv(mixedSizes[dimPos], tileSize);
6009 if (!outerDimsPerm.empty())
6012 mixedSizes.append(innerTileSizes.begin(), innerTileSizes.end());
6013 auto elemType = llvm::cast<ShapedType>(source.
getType()).getElementType();
6014 return tensor::EmptyOp::create(
b, loc, mixedSizes, elemType);
6017PackOp PackOp::createTransposedClone(OpBuilder &
b, Location loc,
6018 ArrayRef<int64_t> innerPermutation,
6019 ArrayRef<int64_t> outerPermutation) {
6021 *
this, innerPermutation, outerPermutation);
6022 Value transposedDest =
6023 createDestinationTensor(
b, loc, getSource(), metadata.innerTiles,
6024 metadata.innerDimsPos, metadata.outerDimsPerm);
6025 return PackOp::create(
b, loc, getSource(), transposedDest,
6026 metadata.innerDimsPos, metadata.innerTiles,
6027 getPaddingValue(), metadata.outerDimsPerm);
6030template <
typename OpTy>
6035 if (op.hasPureTensorSemantics())
6038 for (
OpOperand &opOperand : op.getOperation()->getOpOperands()) {
6039 if (!llvm::isa<MemRefType>(opOperand.
get().
getType()))
6042 if (&opOperand == &op.getSourceMutable()) {
6046 }
else if (&opOperand == &op.getDestMutable()) {
6057void PackOp::getEffects(
6063void UnPackOp::getEffects(
6070template <
typename OpTy>
6072 static_assert(llvm::is_one_of<OpTy, PackOp, UnPackOp>::value,
6073 "applies to only pack or unpack operations");
6074 ShapedType packedType = (std::is_same<OpTy, PackOp>::value)
6076 : op.getSourceType();
6078 for (
auto [dimDest,
tile] : llvm::zip(
6079 packedType.getShape().take_back(mixedTiles.size()), mixedTiles)) {
6081 if (!constTileSize || ShapedType::isDynamic(dimDest))
6088 if (!hasPureTensorSemantics())
6090 if (getPaddingValue())
6105 if (packOp.getInnerDimsPos() != unPackOp.getInnerDimsPos())
6107 if (packOp.getOuterDimsPerm() == unPackOp.getOuterDimsPerm())
6119 auto packTiles = packOp.getMixedTiles();
6120 auto unPackTiles = unPackOp.getMixedTiles();
6121 if (packTiles.size() != unPackTiles.size())
6123 for (
size_t i = 0, e = packTiles.size(); i < e; i++) {
6132 auto srcType = op.getSourceType();
6133 auto innerDimsPos = op.getInnerDimsPos();
6134 auto innerTiles = op.getStaticInnerTiles();
6135 if (ShapedType::isDynamicShape(innerTiles))
6137 for (
auto [pos, tileSize] : llvm::zip_equal(innerDimsPos, innerTiles)) {
6138 if (srcType.isDynamicDim(pos) && tileSize != 1)
6141 return !PackOp::requirePaddingValue(
6142 srcType.getShape(), op.getInnerDimsPos(), op.getDestType().getShape(),
6143 op.getOuterDimsPerm(), op.getMixedTiles());
6150 bool changeNeeded =
false;
6151 srcShape.assign(packOp.getSourceType().getShape().begin(),
6152 packOp.getSourceType().getShape().end());
6153 destShape.assign(packOp.getDestType().getShape().begin(),
6154 packOp.getDestType().getShape().end());
6155 llvm::SmallSetVector<int64_t, 4> innerDims;
6156 innerDims.insert_range(packOp.getInnerDimsPos());
6158 if (!packOp.getOuterDimsPerm().empty())
6160 int srcRank = packOp.getSourceRank();
6161 for (
auto i : llvm::seq<int64_t>(0, srcRank)) {
6162 if (innerDims.contains(i))
6166 if (!inverseOuterDimsPerm.empty())
6167 destPos = inverseOuterDimsPerm[srcPos];
6168 if (ShapedType::isDynamic(srcShape[srcPos]) ==
6169 ShapedType::isDynamic(destShape[destPos])) {
6172 int64_t size = srcShape[srcPos];
6173 if (ShapedType::isDynamic(size))
6174 size = destShape[destPos];
6175 srcShape[srcPos] = size;
6176 destShape[destPos] = size;
6177 changeNeeded =
true;
6179 return changeNeeded;
6182LogicalResult 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,
6283 if (!hasPureTensorSemantics())
6285 std::optional<Attribute> paddingValue;
6286 if (
auto pad = adaptor.getPaddingValue())
6288 if (
OpFoldResult reshapedSource = reshapeConstantSource(
6289 llvm::dyn_cast_if_present<DenseElementsAttr>(adaptor.getSource()),
6290 cast<TensorType>(getDestType()), paddingValue)) {
6291 results.push_back(reshapedSource);
6317 if (!op.hasPureTensorSemantics())
6338 PackOp::create(rewriter, op.getLoc(), newOperands[0], newOperands[1],
6339 op.getInnerDimsPos(), newMixedTileSizes,
6340 op.getPaddingValue(), op.getOuterDimsPerm());
6341 newOp->setDiscardableAttrs(op->getDiscardableAttrDictionary());
6344 Value oldResult = op.getResult();
6345 Value newResult = newOp.getResult();
6348 ? tensor::CastOp::create(rewriter, op->getLoc(),
6349 oldResult.
getType(), newResult)
6362void UnPackOp::getAsmResultNames(
6364 if (!getResults().empty())
6365 setNameFn(getResult(),
"unpack");
6374 Type sourceType, destType, resultType;
6386 if (parser.parseInteger(value))
6388 outerDimsPermVec.push_back(value);
6398 SmallVector<int64_t> innerDimsPosVec;
6401 if (parser.parseInteger(value))
6403 innerDimsPosVec.push_back(value);
6415 for (
auto val : staticTilesAttr.
asArrayRef())
6416 staticTiles.push_back(val);
6433 bool isMemRef = llvm::isa<MemRefType>(sourceType);
6436 "pack/unpack requires '->' and destination type");
6440 resultType = destType;
6446 if (!dynamicTiles.empty() &&
6451 result.addAttribute(
"static_inner_tiles",
6453 result.addAttribute(
"inner_dims_pos", innerDimsPos);
6455 result.addAttribute(
"outer_dims_perm", outerDimsPerm);
6457 SmallVector<int32_t> segmentSizes = {
6458 1, 1, 0,
static_cast<int32_t
>(dynamicTiles.size())};
6459 result.addAttribute(
"operandSegmentSizes",
6463 result.addTypes(resultType);
6468void UnPackOp::print(OpAsmPrinter &p) {
6469 p <<
" " << getSource();
6471 if (!getOuterDimsPerm().empty()) {
6472 p <<
" outer_dims_perm = [";
6473 llvm::interleaveComma(getOuterDimsPerm(), p);
6477 p <<
" inner_dims_pos = [";
6478 llvm::interleaveComma(getInnerDimsPos(), p);
6481 p <<
" inner_tiles = ";
6484 p <<
" into " << getDest();
6487 {
"static_inner_tiles",
"inner_dims_pos",
6488 "outer_dims_perm",
"operandSegmentSizes"});
6490 p <<
" : " << getSource().getType();
6491 p <<
" -> " << getDest().getType();
6495UnPackOp::reifyResultShapes(OpBuilder &builder,
6504SmallVector<OpFoldResult> UnPackOp::getMixedTiles() {
6508SmallVector<int64_t> UnPackOp::getStaticTiles() {
6512ArrayRef<int64_t> UnPackOp::getAllOuterDims() {
6513 ShapedType destType = getDestType();
6514 int64_t destRank = destType.getRank();
6515 return getSourceType().getShape().take_front(destRank);
6518SmallVector<int64_t> UnPackOp::getTiledOuterDims() {
6519 auto innerDimsPos = getInnerDimsPos();
6520 SmallVector<int64_t> outerDims(getAllOuterDims());
6521 SmallVector<int64_t> res;
6524 SmallVector<int64_t> outerDimPermInv(getOuterDimsPerm());
6526 if (!outerDimPermInv.empty())
6530 for (
auto index : innerDimsPos)
6531 res.push_back(outerDims[index]);
6536LogicalResult UnPackOp::verify() {
6541 if (!hasPureTensorSemantics())
6550void UnPackOp::build(OpBuilder &builder, OperationState &state, Value source,
6551 Value dest, ArrayRef<int64_t> innerDimsPos,
6552 ArrayRef<OpFoldResult> innerTiles,
6553 ArrayRef<int64_t> outerDimsPerm) {
6554 assert(innerDimsPos.size() == innerTiles.size() &&
6555 "number of tile sizes specified must match the specified number of "
6556 "original dimensions to be tiled");
6557 SmallVector<int64_t> staticTileSizes;
6558 SmallVector<Value> dynamicTileSizes;
6560 build(builder, state, dest.
getType(), source, dest,
6561 outerDimsPerm.empty() ?
nullptr
6567Value UnPackOp::createDestinationTensor(OpBuilder &
b, Location loc,
6569 ArrayRef<OpFoldResult> innerTileSizes,
6570 ArrayRef<int64_t> innerDimsPos,
6571 ArrayRef<int64_t> outerDimsPerm) {
6572 AffineExpr sym0, sym1;
6574 auto dimMul = [&](OpFoldResult v1, OpFoldResult v2) -> OpFoldResult {
6578 SmallVector<OpFoldResult> mixedSizes;
6579 auto srcType = llvm::cast<RankedTensorType>(source.
getType());
6581 llvm::seq<unsigned>(0, srcType.getRank() - innerTileSizes.size())) {
6582 if (srcType.isDynamicDim(i))
6583 mixedSizes.push_back(
6584 tensor::DimOp::create(
b, loc, source, i).getResult());
6586 mixedSizes.push_back(
b.getIndexAttr(srcType.getDimSize(i)));
6588 if (!outerDimsPerm.empty()) {
6593 for (
auto [dimPos, tileSize] : llvm::zip_equal(innerDimsPos, innerTileSizes))
6594 mixedSizes[dimPos] = dimMul(mixedSizes[dimPos], tileSize);
6596 auto elemType = srcType.getElementType();
6597 return tensor::EmptyOp::create(
b, loc, mixedSizes, elemType);
6600UnPackOp UnPackOp::createTransposedClone(OpBuilder &
b, Location loc,
6601 Value transposedSource,
6602 ArrayRef<int64_t> innerPermutation,
6603 ArrayRef<int64_t> outerPermutation) {
6605 *
this, innerPermutation, outerPermutation);
6606 return UnPackOp::create(
b, loc, transposedSource, getDest(),
6607 metadata.innerDimsPos, metadata.innerTiles,
6608 metadata.outerDimsPerm);
6615 bool changeNeeded =
false;
6616 srcShape.assign(op.getSourceType().getShape().begin(),
6617 op.getSourceType().getShape().end());
6618 destShape.assign(op.getDestType().getShape().begin(),
6619 op.getDestType().getShape().end());
6620 llvm::SmallSetVector<int64_t, 4> innerDims;
6621 innerDims.insert_range(op.getInnerDimsPos());
6623 if (!op.getOuterDimsPerm().empty())
6625 int destRank = op.getDestRank();
6626 for (
auto i : llvm::seq<int64_t>(0, destRank)) {
6627 if (innerDims.contains(i))
6631 if (!inverseOuterDimsPerm.empty())
6632 srcPos = inverseOuterDimsPerm[destPos];
6633 if (ShapedType::isDynamic(srcShape[srcPos]) ==
6634 ShapedType::isDynamic(destShape[destPos])) {
6637 int64_t size = srcShape[srcPos];
6638 if (ShapedType::isDynamic(size))
6639 size = destShape[destPos];
6640 srcShape[srcPos] = size;
6641 destShape[destPos] = size;
6642 changeNeeded =
true;
6644 return changeNeeded;
6647LogicalResult UnPackOp::canonicalize(UnPackOp unPackOp,
6650 if (!unPackOp.hasPureTensorSemantics())
6654 if (PackOp packOp = unPackOp.getSource().getDefiningOp<PackOp>()) {
6655 if (packOp.getSourceType() != unPackOp.getDestType())
6657 if (packOp.getPaddingValue() ||
6661 rewriter.
replaceOp(unPackOp, packOp.getSource());
6665 if (
auto dstStyleOp =
6666 unPackOp.getDest().getDefiningOp<DestinationStyleOpInterface>()) {
6667 auto destValue = cast<OpResult>(unPackOp.getDest());
6668 Value newDest = dstStyleOp.getDpsInits()[destValue.getResultNumber()];
6670 [&]() { unPackOp.setDpsInitOperand(0, newDest); });
6674 if (unPackOp->hasOneUse()) {
6675 auto extractSliceUser =
6676 dyn_cast<tensor::ExtractSliceOp>(*unPackOp->getUsers().begin());
6677 if (extractSliceUser && unPackOp.canFoldSliceOp(extractSliceUser)) {
6678 OpBuilder::InsertionGuard g(rewriter);
6680 auto newDest = tensor::ExtractSliceOp::create(
6681 rewriter, unPackOp->getLoc(), unPackOp.getDest(),
6682 extractSliceUser.getMixedOffsets(), extractSliceUser.getMixedSizes(),
6683 extractSliceUser.getMixedStrides());
6685 unPackOp.setDpsInitOperand(0, newDest);
6686 unPackOp.getResult().setType(newDest.
getType());
6688 rewriter.
replaceOp(extractSliceUser, unPackOp);
6694 SmallVector<int64_t> srcShape, destShape;
6696 Location loc = unPackOp.getLoc();
6697 Value source = unPackOp.getSource();
6698 if (srcShape != unPackOp.getSourceType().getShape()) {
6699 auto newSrcType = unPackOp.getSourceType().clone(srcShape);
6700 source = tensor::CastOp::create(rewriter, loc, newSrcType,
6701 unPackOp.getSource());
6703 Value dest = unPackOp.getDest();
6704 if (destShape != unPackOp.getDestType().getShape()) {
6705 auto newDestType = unPackOp.getDestType().clone(destShape);
6706 dest = tensor::CastOp::create(rewriter, loc, newDestType,
6707 unPackOp.getDest());
6709 UnPackOp newOp = UnPackOp::create(
6710 rewriter, loc, source, dest, unPackOp.getInnerDimsPos(),
6711 unPackOp.getMixedTiles(), unPackOp.getOuterDimsPerm());
6713 unPackOp, unPackOp.getResult().
getType(), newOp.getResult());
6720bool UnPackOp::canFoldSliceOp(tensor::ExtractSliceOp sliceOp) {
6722 if (sliceOp.getResultType().getRank() != this->getDestType().getRank())
6727 RankedTensorType unpackedTypeAfterFold = sliceOp.getResultType();
6728 SmallVector<int64_t> outerShapeWithoutTranspose =
6730 SmallVector<bool> areOuterDimsTiled(outerShapeWithoutTranspose.size(),
false);
6731 for (
auto [pos, tileSize] :
6732 llvm::zip_equal(this->getInnerDimsPos(), this->getStaticInnerTiles())) {
6733 areOuterDimsTiled[pos] =
true;
6734 if (unpackedTypeAfterFold.isDynamicDim(pos))
6736 if (ShapedType::isDynamic(outerShapeWithoutTranspose[pos]))
6738 if (ShapedType::isDynamic(tileSize))
6740 int64_t paddingSize = outerShapeWithoutTranspose[pos] * tileSize -
6741 unpackedTypeAfterFold.getDimSize(pos);
6742 if (paddingSize >= tileSize)
6746 for (int64_t pos = 0, e = outerShapeWithoutTranspose.size(); pos < e; ++pos) {
6747 if (areOuterDimsTiled[pos])
6749 int64_t dim = outerShapeWithoutTranspose[pos];
6750 if (ShapedType::isDynamic(dim))
6752 if (dim != unpackedTypeAfterFold.getDimSize(pos))
6758bool UnPackOp::isLikeUnPad() {
6759 ShapedType packedTensorType = getSourceType();
6763::mlir::LogicalResult
6764UnPackOp::fold(FoldAdaptor adaptor,
6765 ::llvm::SmallVectorImpl<OpFoldResult> &results) {
6767 if (!hasPureTensorSemantics())
6770 if (OpFoldResult reshapedSource = reshapeConstantSource(
6771 llvm::dyn_cast_if_present<DenseElementsAttr>(adaptor.getSource()),
6772 cast<TensorType>(getResult().
getType()))) {
6773 results.push_back(reshapedSource);
6799 if (!op.hasPureTensorSemantics())
6808 Value sourceTensor = newOperands[0];
6812 rewriter, sourceTensor.
getType(), op.getMixedTiles());
6818 UnPackOp newOp = UnPackOp::create(rewriter, op.getLoc(), sourceTensor,
6819 newOperands[1], op.getInnerDimsPos(),
6820 newMixedTileSizes, op.getOuterDimsPerm());
6821 newOp->setDiscardableAttrs(op->getDiscardableAttrDictionary());
6824 Value oldResult = op.getResult();
6825 Value newResult = newOp.getResult();
6828 ? tensor::CastOp::create(rewriter, op->getLoc(),
6829 oldResult.
getType(), newResult)
6843 utils::IteratorType::reduction, utils::IteratorType::parallel,
6844 utils::IteratorType::parallel, utils::IteratorType::reduction};
6847SmallVector<AffineMap>
6848BatchReduceMatmulOp::getDefaultIndexingMaps(MLIRContext *context) {
6849 AffineExpr d0, d1, d2, d3;
6850 SmallVector<AffineMap> indexingMaps;
6852 indexingMaps.push_back(
AffineMap::get(4, 0, {d0, d1, d3}, context));
6853 indexingMaps.push_back(
AffineMap::get(4, 0, {d0, d3, d2}, context));
6855 return indexingMaps;
6858bool BatchReduceMatmulOp::isDefaultIndexingMaps(Attribute attr) {
6859 ArrayAttr maps = dyn_cast<ArrayAttr>(attr);
6862 if (maps.size() != 3)
6867 return (*positions)[0] == SmallVector<int64_t>{0, 1, 3} &&
6868 (*positions)[1] == SmallVector<int64_t>{0, 3, 2} &&
6869 (*positions)[2] == SmallVector<int64_t>{1, 2};
6871unsigned BatchReduceMatmulOp::getNumRegionArgs() {
return 3; }
6873std::string BatchReduceMatmulOp::getLibraryCallName() {
6879bool BatchReduceMatmulOp::hasUserDefinedMaps() {
6880 SmallVector<AffineMap, 3> defaultMaps =
6882 SmallVector<AffineMap, 3> explicitMaps = getIndexingMapsArray();
6883 return defaultMaps != explicitMaps;
6893bool BatchReduceMatmulOp::isValidLhsRhsBroadcastMap(AffineMap bcastMap,
6896 "Expected less than 3 result dim expr.");
6897 bool isValid =
false;
6898 enum Indices { batchPos, mPos, nPos, kPos };
6900 AffineExpr expr = bcastMap.
getResult(0);
6903 AffineExpr expr0 = bcastMap.
getResult(0);
6904 AffineExpr expr1 = bcastMap.
getResult(1);
6909 : ((expr0.isFunctionOfDim(batchPos) &&
6910 expr1.isFunctionOfDim(kPos)) ||
6911 (expr0.isFunctionOfDim(kPos) && expr1.isFunctionOfDim(nPos)));
6916void BatchReduceMatmulOp::regionBuilder(
6917 ImplicitLocOpBuilder &
b,
Block &block, ArrayRef<NamedAttribute> attrs,
6920 emitError() <<
"BatchReduceMatmulOp regionBuilder expects 3 args, got "
6925 "BatchReduceMatmulOp regionBuilder expects 3 args");
6926 RegionBuilderHelper helper(
b, block);
6927 SmallVector<Value> yields;
6931 helper.buildTypeFn(TypeFn::cast_signed, toType, block.
getArgument(0));
6933 helper.buildTypeFn(TypeFn::cast_signed, toType, block.
getArgument(1));
6935 helper.buildBinaryFn(BinaryFn::mul, castValA, castValB,
emitError);
6936 if (!castValA || !castValB || !mulVal)
6939 helper.buildBinaryFn(BinaryFn::add, block.
getArgument(2), mulVal);
6942 yields.push_back(addVal);
6943 helper.yieldOutputs(yields);
6946ParseResult BatchReduceMatmulOp::parse(OpAsmParser &parser,
6947 OperationState &
result) {
6948 SmallVector<Attribute, 3> indexingMapsAttr;
6959 if (!isa<AffineMapAttr>(mapAttr)) {
6961 "expected affine map attribute");
6963 indexingMapsAttr.push_back(mapAttr);
6973 if (indexingMapsAttr.empty()) {
6974 indexingMapsAttr = llvm::map_to_vector(
6975 BatchReduceMatmulOp::getDefaultIndexingMaps(parser.
getContext()),
6976 [](AffineMap map) -> Attribute { return AffineMapAttr::get(map); });
6978 result.addAttribute(
"indexing_maps",
6980 return ::parseNamedStructuredOp(parser,
result,
6981 BatchReduceMatmulOp::getNumRegionArgs(),
6982 BatchReduceMatmulOp::getRegionBuilder());
6985void BatchReduceMatmulOp::print(OpAsmPrinter &p) {
6986 SmallVector<Attribute, 3> indexingMaps = llvm::map_to_vector(
6987 BatchReduceMatmulOp::getDefaultIndexingMaps(
getContext()),
6988 [](AffineMap map) -> Attribute {
return AffineMapAttr::get(map); });
6990 if (!llvm::equal(getIndexingMaps(), indexingMaps)) {
6991 p <<
" indexing_maps = [";
6992 llvm::interleaveComma(getIndexingMaps(), p,
6997 SmallVector<StringRef, 3> elidedAttrs = {
6998 "operandSegmentSizes",
"linalg.memoized_indexing_maps",
"indexing_maps"};
7004LogicalResult BatchReduceMatmulOp::verify() {
7007 if (!hasUserDefinedMaps())
7010 for (
unsigned opIndex = 0; opIndex < 3; opIndex++) {
7016LogicalResult BatchReduceMatmulOp::fold(FoldAdaptor,
7017 SmallVectorImpl<OpFoldResult> &) {
7020void BatchReduceMatmulOp::getEffects(
7021 SmallVectorImpl<SideEffects::EffectInstance<MemoryEffects::Effect>>
7023 if (hasPureTensorSemantics())
7036SmallVector<utils::IteratorType> ScaledContractOp::getIteratorTypesArray() {
7037 AffineMap outAffineMap = getIndexingMapsArray().pop_back_val();
7039 SmallVector<bool> dimsInOutput(outAffineMap.
getNumDims(),
false);
7041 auto dimExpr = dyn_cast<AffineDimExpr>(
result);
7042 assert(dimExpr &&
"affine_map is a projected permutation");
7043 dimsInOutput[dimExpr.getPosition()] =
true;
7046 SmallVector<utils::IteratorType> iteratorTypes;
7047 for (
auto dimOccursInOutput : dimsInOutput)
7048 iteratorTypes.push_back(dimOccursInOutput ? utils::IteratorType::parallel
7049 : utils::IteratorType::reduction);
7051 return iteratorTypes;
7054unsigned ScaledContractOp::getNumRegionArgs() {
return 5; }
7057void ScaledContractOp::regionBuilder(
7058 ImplicitLocOpBuilder &
b,
Block &block, ArrayRef<NamedAttribute> attrs,
7061 emitError() <<
"ScaledContractOp regionBuilder expects 5 args, got "
7066 "ScaledContractOp regionBuilder expects 5 args");
7067 RegionBuilderHelper helper(
b, block);
7069 TypeFn castSignedness = TypeFn::cast_signed;
7070 auto castIter = llvm::find_if(attrs, [&](
const NamedAttribute &attr) {
7071 return attr.
getName() ==
"cast";
7073 if (castIter != attrs.end()) {
7074 if (
auto attr = llvm::dyn_cast<TypeFnAttr>(castIter->getValue()))
7084 auto buildScaledValue = [&](Value data, Value scale) -> Value {
7085 auto dataFloatTy = dyn_cast<FloatType>(data.
getType());
7086 auto outFloatTy = dyn_cast<FloatType>(outType);
7087 if (dataFloatTy && dyn_cast<FloatType>(scale.getType()) && outFloatTy) {
7088 unsigned dataWidth = dataFloatTy.getWidth();
7089 unsigned outWidth = outFloatTy.getWidth();
7090 if (dataWidth < outWidth)
7091 return arith::ScalingExtFOp::create(
b, outType, data, scale,
7094 Value dataAtOutType = helper.buildTypeFn(castSignedness, outType, data);
7095 Value scaleAtOutType = helper.buildTypeFn(castSignedness, outType, scale);
7096 return helper.buildBinaryFn(BinaryFn::mul, dataAtOutType, scaleAtOutType,
7108 Value productAtOutType =
7109 helper.buildBinaryFn(BinaryFn::mul, scaledLhs, scaledRhs,
emitError);
7110 if (!productAtOutType)
7116 helper.yieldOutputs({
result});
7119ParseResult ScaledContractOp::parse(OpAsmParser &parser,
7120 OperationState &
result) {
7122 if (
failed(indexingMapsAttr) || *indexingMapsAttr ==
nullptr)
7124 "expected 'indexing_maps' attribute");
7125 result.addAttribute(
"indexing_maps", *indexingMapsAttr);
7131void ScaledContractOp::print(OpAsmPrinter &p) {
7132 p <<
" indexing_maps = " << llvm::interleaved_array(getIndexingMaps());
7134 p, getOperation(), getInputs(), getOutputs(),
7135 {
"indexing_maps",
"operandSegmentSizes"});
7138LogicalResult ScaledContractOp::verify() {
7139 int iterationSpaceDims = -1;
7144 SmallVector<size_t> inOccurrences;
7145 SmallVector<size_t> outOccurrences;
7148 SmallVector<AffineMap, 5> maps = getIndexingMapsArray();
7150 return emitOpError(
"expected 5 indexing maps and operands");
7152 SmallVector<Type, 5> types = llvm::to_vector(getOperandTypes());
7154 auto outputFloatType = dyn_cast<FloatType>(outputElementType);
7155 if (!outputFloatType)
7156 return emitOpError(
"expected output element type to be floating-point");
7158 for (Type inputType : ArrayRef<Type>(types).take_front(4)) {
7162 "expected input element types to be integer or floating-point");
7164 return emitOpError(
"expected input element type bitwidth to be no "
7165 "greater than output element type bitwidth");
7168 for (
auto &&[affineMap, operandType, isInput] :
7169 llvm::zip(SmallVector<AffineMap>{maps[0], maps[2], maps[4]},
7170 SmallVector<Type>{types[0], types[2], types[4]},
7171 SmallVector<bool>{
true,
true,
false})) {
7173 affineMap, operandType, isInput, iterationSpaceDims, inOccurrences,
7174 outOccurrences, [&]() {
return emitError(); })))
7179 inOccurrences, outOccurrences,
7184 auto checkScaleAffineMapAndType = [&](AffineMap affineMap,
7185 Type operandType) -> LogicalResult {
7190 return emitError(
"scale affine_map must not contain symbols");
7193 "scale affine_map must not have more results than inputs");
7195 SmallVector<bool, 8> seen(affineMap.
getNumInputs(),
false);
7199 AffineDimExpr dim =
nullptr;
7200 if (isa<AffineDimExpr>(expr)) {
7202 dim = dyn_cast<AffineDimExpr>(expr);
7203 }
else if (
auto binExpr = dyn_cast<AffineBinaryOpExpr>(expr)) {
7210 "only block scale with floordiv is supported for now");
7211 auto scaleDim = dyn_cast<AffineDimExpr>(binExpr.getLHS());
7213 return emitError(
"block scale LHS must be dim");
7214 auto scaleFactor = dyn_cast<AffineConstantExpr>(binExpr.getRHS());
7216 return emitError(
"block scale RHS must be constant");
7217 if (scaleFactor.getValue() <= 0)
7218 return emitError(
"block scale factor must be positive");
7221 return emitError(
"unsupported scaling variant");
7225 return emitError(
"invalid scale affine_map result expression");
7228 "scale affine_map must not have duplicate result dimensions");
7233 if (
auto shapedType = dyn_cast<ShapedType>(operandType)) {
7236 "scale ranks of shaped operand and results of corresponding "
7237 "affine_map differ");
7240 "scale affine_map specifies shaped access while operand has "
7248 for (
auto &&[affineMap, operandType] :
7249 llvm::zip(SmallVector<AffineMap>{maps[1], maps[3]},
7250 SmallVector<Type>{types[1], types[3]})) {
7251 if (
failed(checkScaleAffineMapAndType(affineMap, operandType)))
7256 for (
auto &&[inputMap, inputType, scaleMap, scaleType] :
7257 llvm::zip(SmallVector<AffineMap>{maps[0], maps[2]},
7258 SmallVector<Type>{types[0], types[2]},
7259 SmallVector<AffineMap>{maps[1], maps[3]},
7260 SmallVector<Type>{types[1], types[3]})) {
7261 if (inputMap.getNumResults() < scaleMap.getNumResults())
7262 return emitError(
"scale must have at most the same rank as input");
7263 if (scaleMap.getNumResults() == 0)
7266 auto inputShape = dyn_cast<ShapedType>(inputType).getShape();
7267 auto scaleShape = dyn_cast<ShapedType>(scaleType).getShape();
7274 for (
auto [scaleIdx, scaleExpr] : llvm::enumerate(scaleMap.getResults())) {
7275 AffineDimExpr scaleDimExpr =
nullptr;
7276 std::optional<int64_t> scaleFactor;
7277 if (
auto dimExpr = dyn_cast<AffineDimExpr>(scaleExpr)) {
7279 scaleDimExpr = dimExpr;
7280 }
else if (
auto scaleBinExpr = dyn_cast<AffineBinaryOpExpr>(scaleExpr)) {
7283 "only floordiv is supported for now");
7284 auto scaleDim = dyn_cast<AffineDimExpr>(scaleBinExpr.getLHS());
7285 assert(scaleDim &&
"block scale LHS is a dim expression");
7286 scaleDimExpr = scaleDim;
7288 dyn_cast<AffineConstantExpr>(scaleBinExpr.getRHS()).getValue();
7290 llvm_unreachable(
"unknown scale expression");
7292 assert(scaleDimExpr &&
"failed to find scale dim expression");
7294 std::optional<unsigned> inputIdx =
7295 inputMap.getResultPosition(scaleDimExpr);
7298 "scale map must contain corresponding input dimensions only");
7302 if (scaleFactor && inputShape[*inputIdx] != ShapedType::kDynamic &&
7303 scaleShape[scaleIdx] != ShapedType::kDynamic &&
7304 llvm::divideCeilSigned(inputShape[*inputIdx], *scaleFactor) !=
7305 static_cast<int64_t
>(scaleShape[scaleIdx])) {
7306 return emitError() <<
"Invalid scale shape at dim " << *inputIdx
7308 << llvm::divideCeilSigned(inputShape[*inputIdx],
7310 <<
" but got " << scaleShape[scaleIdx];
7318LogicalResult ScaledContractOp::fold(FoldAdaptor,
7319 SmallVectorImpl<OpFoldResult> &) {
7323void ScaledContractOp::getEffects(
7324 SmallVectorImpl<SideEffects::EffectInstance<MemoryEffects::Effect>>
7326 if (hasPureTensorSemantics())
7342void LinalgDialect::getCanonicalizationPatterns(
7351 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 ArityGroupAndKind getArityGroupAndKind(ElementwiseKind kind)
static PackOrUnPackTransposeResult commonPermutationOfPackAndUnPackOp(OpTy packOrUnPackOp, ArrayRef< int64_t > innerPermutation, ArrayRef< int64_t > outerPermutation)
OpFoldResult createFoldedDimOp(OpBuilder &b, Location loc, Value val, int64_t dim)
Create one memref::DimOp or tensor::DimOp depending on the type of val.
static SmallVector< OpFoldResult > getNewMixedTileSizes(PatternRewriter &rewriter, Type newPackedTy, ArrayRef< OpFoldResult > mixedTiles)
static bool areTilesAndTiledDimsAllConstant(OpTy op)
Returns true if the tiles and the tiled dims are constant.
std::string generateLibraryCallName(Operation *op)
Returns the name mangled library call name to disambiguate between different overloads at the C level...
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.
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={})
Patterns must specify the root operation name they match against, and can also specify the benefit of...
This represents an operation in an abstracted form, suitable for use with the builder APIs.
void addOperands(ValueRange newOperands)
void addAttributes(ArrayRef< NamedAttribute > newAttributes)
Add an array of named attributes.
void addAttribute(StringRef name, Attribute attr)
Add an attribute with the specified name.
void addTypes(ArrayRef< Type > newTypes)
Region * addRegion()
Create a region that should be attached to the operation.
Folds a tensor.cast op into a consuming PackOp op if the tensor.cast has source that is more static t...
LogicalResult matchAndRewrite(PackOp op, PatternRewriter &rewriter) const override
Folds a tensor.cast op into a consuming UnPackOp op if the tensor.cast has source that is more static...
LogicalResult matchAndRewrite(UnPackOp op, PatternRewriter &rewriter) const override