29#include "llvm/ADT/APFloat.h"
30#include "llvm/ADT/APInt.h"
56 (padConstAttr.
size() != 1)) {
61 if (
auto padConstFpAttr = mlir::dyn_cast<DenseFPElementsAttr>(padConstAttr)) {
62 float padConstVal = (*padConstFpAttr.begin()).convertToFloat();
63 return padConstVal == 0.0f;
67 if (
auto padConstIntAttr =
68 mlir::dyn_cast<DenseIntElementsAttr>(padConstAttr)) {
77 int64_t padConstVal = (*padConstIntAttr.begin()).getSExtValue();
78 return zpVal == padConstVal;
86template <
typename OpTy>
87struct PoolPadFoldAdaptor;
90struct PoolPadFoldAdaptor<
tosa::MaxPool2dOp> {
91 using OpTy = tosa::MaxPool2dOp;
92 static bool checkKernelCompliance(OpTy op,
const ArrayRef<int64_t> newPad) {
93 const llvm::ArrayRef<int64_t> kernel = op.getKernel();
94 if (newPad[2] >= kernel[1] || newPad[3] >= kernel[1] ||
95 newPad[0] >= kernel[0] || newPad[1] >= kernel[0])
99 static bool checkPadConstCompliance(OpTy, Value padConst) {
101 DenseElementsAttr padConstAttr;
103 padConstAttr.
size() != 1) {
108 if (
auto padConstFpAttr =
109 mlir::dyn_cast<DenseFPElementsAttr>(padConstAttr)) {
110 const APFloat padConstVal = *padConstFpAttr.begin();
111 const APFloat lowestVal =
112 APFloat::getLargest(padConstVal.getSemantics(),
true);
113 return padConstVal == lowestVal;
115 if (
auto padConstIntAttr =
116 mlir::dyn_cast<DenseIntElementsAttr>(padConstAttr)) {
117 const APInt padConstVal = *padConstIntAttr.begin();
118 const unsigned int bitWidth = padConstVal.getBitWidth();
119 const APInt lowestVal =
120 padConstIntAttr.getElementType().isUnsignedInteger()
121 ? APInt::getZero(bitWidth)
122 : APInt::getSignedMinValue(bitWidth);
123 return padConstVal == lowestVal;
129 static void replaceOpWithNewPad(PatternRewriter &rewriter, OpTy op,
130 Value padInput, ArrayRef<int64_t> newPad) {
132 op, op.getType(), padInput, op.getKernel(), op.getStride(),
137template <
typename OpTy>
138struct ConvPadFoldAdaptor {
139 static bool checkKernelCompliance(OpTy,
const ArrayRef<int64_t>) {
142 static bool checkPadConstCompliance(OpTy op, Value padConst) {
145 static void replaceOpWithNewPad(PatternRewriter &rewriter, OpTy op,
146 Value padInput, ArrayRef<int64_t> newPad) {
148 op, op.getResult().
getType(), padInput, op.getWeight(), op.getBias(),
149 op.getInputZp(), op.getWeightZp(), newPad, op.getStrideAttr(),
150 op.getDilationAttr(), op.getAccType(), op.getLocalBound());
158template <
typename OpTy,
typename AdaptorTy>
160 using OpRewritePattern<OpTy>::OpRewritePattern;
162 LogicalResult matchAndRewrite(OpTy tensorOp,
163 PatternRewriter &rewriter)
const override {
165 auto padOp = tensorOp.getInput().template getDefiningOp<tosa::PadOp>();
168 "Producer must be a tosa::PadOp.");
171 const std::vector<int64_t> &tensorOpPad = tensorOp.getPad().vec();
172 if (tensorOpPad.size() != 4)
174 tensorOp,
"Tensor operation padding shall have 4 elements.");
177 DenseIntElementsAttr padOpPadding;
181 "The `padding` input specified on the tosa::PadOp must be constant.");
185 if (padOpPadding.size() != 8)
187 "Pad padding should have 8 elements.");
188 int64_t padNBefore = (*(padOpPadding.
begin() + 0)).getLimitedValue();
189 int64_t padNAfter = (*(padOpPadding.
begin() + 1)).getLimitedValue();
190 int64_t padHBefore = (*(padOpPadding.
begin() + 2)).getLimitedValue();
191 int64_t padHAfter = (*(padOpPadding.
begin() + 3)).getLimitedValue();
192 int64_t padWBefore = (*(padOpPadding.
begin() + 4)).getLimitedValue();
193 int64_t padWAfter = (*(padOpPadding.
begin() + 5)).getLimitedValue();
194 int64_t padCBefore = (*(padOpPadding.
begin() + 6)).getLimitedValue();
195 int64_t padCAfter = (*(padOpPadding.
begin() + 7)).getLimitedValue();
197 if (padNBefore != 0 || padNAfter != 0 || padCBefore != 0 || padCAfter != 0)
199 tensorOp,
"Folding padding in N or C dimensions is not supported.");
203 SmallVector<int64_t> foldedPad(tensorOpPad.size());
204 foldedPad[0] = padHBefore + tensorOpPad[0];
205 foldedPad[1] = padHAfter + tensorOpPad[1];
206 foldedPad[2] = padWBefore + tensorOpPad[2];
207 foldedPad[3] = padWAfter + tensorOpPad[3];
210 if (!AdaptorTy::checkKernelCompliance(tensorOp, foldedPad)) {
212 tensorOp,
"Padding size not aligned with kernel restrictions.");
216 if (!AdaptorTy::checkPadConstCompliance(tensorOp, padOp.getPadConst())) {
219 "Padding constant is not aligned with operator zero-point.");
223 if (llvm::any_of(foldedPad, [](int64_t padVal) {
return padVal > 8192; })) {
225 tensorOp,
"Padding size more than the 8K level limit.");
229 AdaptorTy::replaceOpWithNewPad(rewriter, tensorOp, padOp.getInput1(),
240 FoldPadToTensorOp<tosa::Conv2DOp, ConvPadFoldAdaptor<tosa::Conv2DOp>>>(
246 results.
add<FoldPadToTensorOp<tosa::DepthwiseConv2DOp,
247 ConvPadFoldAdaptor<tosa::DepthwiseConv2DOp>>>(
264 op,
"expected constant kernel, stride, and pad operands");
267 rewriter, op.getLoc(), op.
getType(), op.getInput(), op.getInputZp(),
282 if (op.getInput().getType() != op.getOutput().getType())
284 op,
"expected input and output types to match");
286 const auto inputType = llvm::cast<ShapedType>(op.getInput().getType());
287 if (!llvm::isa<FloatType>(inputType.getElementType()))
289 "expected floating-point input type");
297 "expected input and output zero points to be statically verifiable");
299 if (!llvm::all_of(op.getKernel(), [](
int64_t val) { return val == 1; }))
302 if (!llvm::all_of(op.getStride(), [](
int64_t val) { return val == 1; }))
305 if (!llvm::all_of(op.getPad(), [](
int64_t val) { return val == 0; }))
318void AvgPool2dAdaptiveOp::getCanonicalizationPatterns(
328 Value input = op.getInput();
329 Value output = op.getOutput();
330 ShapedType inputType = llvm::cast<ShapedType>(input.
getType());
331 ShapedType outputType = llvm::cast<ShapedType>(output.
getType());
334 llvm::all_of(op.getKernel(), [](
int64_t val) { return val == 1; }) &&
335 llvm::all_of(op.getStride(), [](
int64_t val) { return val == 1; }) &&
336 llvm::all_of(op.getPad(), [](
int64_t val) { return val == 0; }) &&
337 op.getNanMode() == tosa::NanPropagationMode::PROPAGATE) {
342 if (!inputType.hasStaticShape() || !outputType.hasStaticShape()) {
348 if (outputShape[1] != 1 || outputShape[2] != 1) {
353 if (inputShape[1] != 1 || inputShape[2] != 1) {
365 FoldPadToTensorOp<tosa::MaxPool2dOp,
366 PoolPadFoldAdaptor<tosa::MaxPool2dOp>>>(
383 op,
"expected constant kernel, stride, and pad operands");
386 rewriter, op.getLoc(), op.
getType(), op.getInput(),
395void MaxPool2dAdaptiveOp::getCanonicalizationPatterns(
409 if (op.getInput1().size() != 1)
411 if (op.getInput1().front().getType() != op.getType()) {
414 op.getInput1().front())
419 rewriter.
replaceOp(op, op.getInput1().front());
434 concatOperands.reserve(2 * op.getNumOperands());
436 int32_t maxNumOperands = 0;
442 bool foundRewritableConcat =
false;
443 for (
Value operand : op.getOperands()) {
444 concatOperands.emplace_back(operand);
446 auto producer = operand.getDefiningOp<tosa::ConcatOp>();
451 if (op.getAxis() != producer.getAxis())
455 foundRewritableConcat =
true;
456 concatOperands.pop_back();
457 llvm::append_range(concatOperands, producer->getOperands());
460 if (!foundRewritableConcat)
462 "No rewritable concat operand found.");
464 if (maxNumOperands > 0 &&
465 concatOperands.size() >
static_cast<size_t>(maxNumOperands))
467 op,
"Rewriting would exceed the maximum number of operands for the "
468 "target environment level.");
471 op, op.getType(), concatOperands, op.getAxisAttr());
481LogicalResult SelectOp::canonicalize(SelectOp op,
PatternRewriter &rewriter) {
482 auto notOp = op.getInput1().getDefiningOp<tosa::LogicalNotOp>();
486 op.getOperation()->setOperands(
487 {notOp.getInput1(), op.getOnFalse(), op.getOnTrue()});
499 auto innerTranspose =
500 transposeOp.getInput1().getDefiningOp<tosa::TransposeOp>();
503 "input must be transpose operation");
507 innerTranspose.getPerms();
509 if (transposePerms.size() != innerTransposePerms.size())
512 "transpose and inner transpose perms sizes must be equal");
513 if (transposePerms.empty())
515 transposeOp,
"transpose perms sizes must be positive");
519 for (
int i = 0, s = transposePerms.size(); i < s; ++i)
520 perms[i] = innerTransposePerms[transposePerms[i]];
523 transposeOp, transposeOp.getResult().
getType(),
536 if (op.getInput1().getDefiningOp<tosa::TransposeOp>())
538 op,
"Src is from transpose, can compose transposes");
542 if (isa_and_nonnull<tosa::TransposeOp>(subop))
544 op,
"Dest is used by transpose, can compose transposes");
547 auto input = op.getInput1();
548 auto inputTy = llvm::cast<ShapedType>(input.
getType());
549 if (!inputTy.hasRank())
553 for (
int i = 0; i < inputTy.getRank(); ++i)
554 if (inputTy.isDynamicDim(i))
563 nonZeroPerms.reserve(permValues.size());
564 for (
auto idx : permValues) {
565 auto sz = inputTy.getDimSize(idx);
567 nonZeroPerms.push_back(idx);
570 for (
int i = 1, s = nonZeroPerms.size(); i < s; ++i)
571 if (nonZeroPerms[i - 1] > nonZeroPerms[i])
573 "Transpose changes memory layout.");
576 newShape.reserve(inputTy.getRank());
577 for (
int i = 0, s = inputTy.getRank(); i < s; ++i)
578 newShape.push_back(inputTy.getDimSize(permValues[i]));
581 op, op.getType(), op.getInput1(),
589 results.
add<ConsolidateTransposeOptimization, TransposeIsReshape>(context);
597 Value input = op.getInput();
598 auto inputType = llvm::cast<ShapedType>(op.getInput().getType());
599 auto inputElementType = inputType.getElementType();
601 if (isa<FloatType>(inputElementType)) {
603 const auto minClamp =
604 llvm::cast<mlir::FloatAttr>(op.getMinValAttr()).getValue();
605 const auto maxClamp =
606 llvm::cast<mlir::FloatAttr>(op.getMaxValAttr()).getValue();
607 const bool isMin = minClamp.isNegInfinity();
608 const bool isMax = maxClamp.isInfinity();
610 if (isMin && isMax) {
618 const bool isBoolean = inputElementType.isInteger(1);
619 if (inputElementType.isUnsignedInteger() || isBoolean) {
620 const int64_t minClamp = llvm::cast<mlir::IntegerAttr>(op.getMinValAttr())
623 const int64_t maxClamp = llvm::cast<mlir::IntegerAttr>(op.getMaxValAttr())
627 const unsigned bitWidth = inputElementType.getIntOrFloatBitWidth();
628 const int64_t intMin = APInt::getMinValue(bitWidth).getZExtValue();
629 const int64_t intMax = APInt::getMaxValue(bitWidth).getZExtValue();
631 if (minClamp <= intMin && maxClamp >= intMax) {
638 if (llvm::isa<IntegerType>(inputElementType)) {
640 llvm::cast<mlir::IntegerAttr>(op.getMinValAttr()).getInt();
642 llvm::cast<mlir::IntegerAttr>(op.getMaxValAttr()).getInt();
644 const unsigned bitWidth = inputElementType.getIntOrFloatBitWidth();
645 const int64_t intMin = APInt::getSignedMinValue(bitWidth).getSExtValue();
646 const int64_t intMax = APInt::getSignedMaxValue(bitWidth).getSExtValue();
648 if (minClamp <= intMin && maxClamp >= intMax) {
680 template <
typename T>
694 Value input = op.getInput();
702 const auto opNanMode = op.getNanMode();
703 const auto clampNanMode = clampOp.getNanMode();
704 if (opNanMode == NanPropagationMode::IGNORE &&
705 clampNanMode == NanPropagationMode::PROPAGATE)
708 auto maxValAttr = op.getMaxValAttr();
709 auto minValAttr = op.getMinValAttr();
710 auto clampOpMaxValAttr = clampOp.getMaxValAttr();
711 auto clampOpMinValAttr = clampOp.getMinValAttr();
713 auto inputEType = llvm::cast<ShapedType>(input.
getType()).getElementType();
715 llvm::dyn_cast<mlir::quant::UniformQuantizedType>(inputEType)) {
720 if (mlir::isa<FloatType>(inputEType)) {
721 auto floatMaxValAttr = cast<mlir::FloatAttr>(maxValAttr);
722 auto floatMinValAttr = cast<mlir::FloatAttr>(minValAttr);
723 auto clampOpFloatMaxValAttr = cast<mlir::FloatAttr>(clampOpMaxValAttr);
724 auto clampOpFloatMinValAttr = cast<mlir::FloatAttr>(clampOpMinValAttr);
727 const auto opMinFloat = floatMinValAttr.getValue();
728 const auto opMaxFloat = floatMaxValAttr.getValue();
729 const auto clampOpMinFloat = clampOpFloatMinValAttr.getValue();
730 const auto clampOpMaxFloat = clampOpFloatMaxValAttr.getValue();
734 if (!opRangeFloatRange.
intersects(clampRangeFloatRange))
738 auto newMinVal = std::max(opMinFloat, clampOpMinFloat);
739 auto newMaxVal = std::min(opMaxFloat, clampOpMaxFloat);
740 newMinValAttr = rewriter.
getFloatAttr(inputEType, newMinVal);
741 newMaxValAttr = rewriter.
getFloatAttr(inputEType, newMaxVal);
743 assert(mlir::isa<IntegerType>(inputEType));
744 auto intMaxValAttr = cast<mlir::IntegerAttr>(maxValAttr);
745 auto intMinValAttr = cast<mlir::IntegerAttr>(minValAttr);
746 auto clampOpIntMaxValAttr = cast<mlir::IntegerAttr>(clampOpMaxValAttr);
747 auto clampOpIntMinValAttr = cast<mlir::IntegerAttr>(clampOpMinValAttr);
749 if (inputEType.isUnsignedInteger()) {
751 const auto opMinInt = intMinValAttr.getUInt();
752 const auto opMaxInt = intMaxValAttr.getUInt();
753 const auto clampOpMinInt = clampOpIntMinValAttr.getUInt();
754 const auto clampOpMaxInt = clampOpIntMaxValAttr.getUInt();
758 if (!opRangeIntRange.
intersects(clampRangeIntRange))
762 auto newMinVal = std::max(opMinInt, clampOpMinInt);
763 auto newMaxVal = std::min(opMaxInt, clampOpMaxInt);
768 const auto opMinInt = intMinValAttr.getInt();
769 const auto opMaxInt = intMaxValAttr.getInt();
770 const auto clampOpMinInt = clampOpIntMinValAttr.getInt();
771 const auto clampOpMaxInt = clampOpIntMaxValAttr.getInt();
775 if (!opRangeIntRange.
intersects(clampRangeIntRange))
779 auto newMinVal = std::max(opMinInt, clampOpMinInt);
780 auto newMaxVal = std::min(opMaxInt, clampOpMaxInt);
786 auto newMode = (opNanMode != clampNanMode)
787 ? tosa::NanPropagationMode::IGNORE
791 NanPropagationModeAttr::get(rewriter.
getContext(), newMode);
794 op, op.getType(), clampOp.getInput(), newMinValAttr, newMaxValAttr,
800void ClampOp::getCanonicalizationPatterns(RewritePatternSet &results,
801 MLIRContext *context) {
802 results.
add<ClampIsNoOp>(context);
803 results.
add<ClampClampOptimization>(context);
811 Value sliceInput = sliceOp.getInput1();
815 sliceOp,
"slice input must be concat operation");
818 auto concatType = dyn_cast<RankedTensorType>(concatOp.getType());
819 if (!concatType || !concatType.hasStaticShape())
821 sliceOp,
"slice input must be a static ranked tensor");
822 int32_t axis = concatOp.getAxis();
829 sliceOp,
"start of slice must be a static ranked shape");
833 sliceOp,
"size of slice must be a static ranked shape");
843 std::optional<Value> replaceWithSlice;
844 for (
auto input : inputs) {
845 auto inputType = dyn_cast<RankedTensorType>(input.
getType());
846 if (!inputType || !inputType.hasStaticShape())
848 sliceOp,
"concat input must be a static ranked tensor");
850 if (sliceStarts[axis] >= 0 && (sliceStarts[axis] + sliceSizes[axis]) <=
851 inputType.getDimSize(axis)) {
857 tosa::SliceOp::create(rewriter, sliceOp.getLoc(), sliceOp.
getType(),
858 input, start_op, size_op)
862 sliceStarts[axis] -= inputType.getDimSize(axis);
865 if (!replaceWithSlice)
867 sliceOp,
"corresponding concat input not found for slice");
869 rewriter.
replaceOp(sliceOp, replaceWithSlice.value());
879 Value sliceInput = sliceOp.getInput1();
885 "slice input must be a pad operation");
888 if (!padOp->hasOneUse())
890 "pad shall have a single consumer");
893 auto inputTy = dyn_cast<RankedTensorType>(padOp.getInput1().getType());
894 auto padTy = dyn_cast<RankedTensorType>(padOp.getType());
895 if (!inputTy || !padTy || !inputTy.hasRank())
897 "slice input must be a ranked tensor");
904 "`padding` input specified on the tosa::PadOp must be constant.");
907 llvm::to_vector(paddingElems.getValues<
int64_t>());
913 sliceOp,
"start of slice must be a static ranked shape");
920 sliceOp,
"size of slice must be a static ranked shape");
925 const int64_t rank = inputTy.getRank();
926 if (llvm::any_of(llvm::seq<int64_t>(0, rank), [&](
int64_t i) {
927 const bool isDimDynamic = inputTy.isDynamicDim(i);
928 const bool isDimSliced =
931 return isDimDynamic && isDimSliced;
934 sliceOp,
"axis that are sliced shall be statically known.");
941 bool updated =
false;
943 for (
int64_t i = 0; i < rank; ++i) {
944 const int64_t padLo = padPaddings[i * 2];
945 const int64_t padHi = padPaddings[i * 2 + 1];
946 const int64_t sliceStart = sliceStarts[i];
947 const int64_t sliceSize = sliceSizes[i];
948 const int64_t sliceEnd = sliceStart + sliceSize;
951 if (inputTy.isDynamicDim(i)) {
952 newPadPaddings[i * 2] = padLo;
953 newPadPaddings[i * 2 + 1] = padHi;
954 newSliceStarts[i] = sliceStart;
959 const int64_t dimSize = inputTy.getShape()[i];
960 const int64_t dimTotal = padLo + dimSize + padHi;
963 if (sliceStart < 0 || sliceEnd > dimTotal)
967 const int64_t newSliceStart = std::max<int64_t>(sliceStart - padLo, 0);
968 newSliceStarts[i] = newSliceStart;
969 updated |= newSliceStart != sliceStart;
972 const int64_t newPadLo = std::max<int64_t>(padLo - sliceStart, 0);
974 std::max<int64_t>(sliceEnd - (padLo + dimSize), 0);
975 newPadPaddings[i * 2] = newPadLo;
976 newPadPaddings[i * 2 + 1] = newPadHi;
977 updated |= (newPadLo != padLo) || (newPadHi != padHi);
981 newPadPaddings[i * 2] + dimSize + newPadPaddings[i * 2 + 1];
987 sliceOp,
"terminate condition; nothing to rewrite");
993 RankedTensorType::get(newPadShape, inputTy.getElementType());
994 auto newPadOp = tosa::PadOp::create(rewriter, padOp.getLoc(), newPadTy,
995 padOp.getInput1(), newPaddingsOp,
996 padOp.getPadConst());
1002 newPadOp.getResult(), newStartOp,
1017 ShapedType resultType = cast<ShapedType>(sliceOp.getType());
1018 if (!resultType.hasRank())
1021 ElementsAttr sizeElems;
1024 sliceOp,
"size of slice must be a static ranked shape");
1028 llvm::to_vector(sizeElems.getValues<
int64_t>());
1030 bool replaceSliceSize{
false};
1034 for (
const auto &[
index, size] : llvm::enumerate(sliceSizes)) {
1036 sliceSizes[
index] = resultType.getDimSize(
index);
1037 replaceSliceSize =
true;
1041 if (!replaceSliceSize) {
1043 sliceOp,
"no dimension of size of slice is dynamic that resolves "
1044 "to static output shape");
1049 tosa::SliceOp::create(rewriter, sliceOp.getLoc(), sliceOp.
getType(),
1050 sliceOp.getInput1(), sliceOp.getStart(), size_op);
1052 rewriter.
replaceOp(sliceOp, newSliceOp.getResult());
1057void SliceOp::getCanonicalizationPatterns(RewritePatternSet &results,
1058 MLIRContext *context) {
1059 results.
add<ConcatSliceOptimization, PadSliceOptimization,
1060 SliceDynamicSizeCanonicalization>(context);
1068 const Value castInput = castOp.getInput();
1072 "input must be cast operation");
1074 const Value innerCastInput = innerCastOp.getInput();
1076 const ShapedType innerInputType =
1077 llvm::cast<ShapedType>(innerCastInput.
getType());
1078 const ShapedType innerOutputType =
1079 llvm::cast<ShapedType>(innerCastOp.getType());
1080 const ShapedType outerOutputType = llvm::cast<ShapedType>(castOp.getType());
1082 const Type innerInputElemType = innerInputType.getElementType();
1083 const Type innerOutputElemType = innerOutputType.getElementType();
1084 const Type outerOutputElemType = outerOutputType.getElementType();
1087 outerOutputElemType};
1089 if (llvm::any_of(types, [](
const Type type) {
1093 llvm::isa<Float8E4M3FNType, Float8E5M2Type, BFloat16Type,
1094 Float16Type, Float32Type>(type));
1097 castOp,
"only integer and f32, f16, bf16, f8E4M3FN, f8E5M2 types are "
1100 if (llvm::isa<Float8E5M2Type>(innerInputElemType) &&
1101 llvm::isa<Float8E4M3FNType>(outerOutputElemType)) {
1103 castOp,
"avoid introducing f8E5M2 -> f8E4M3FN casts which are not "
1107 if (llvm::isa<Float8E4M3FNType>(innerInputElemType) &&
1108 llvm::isa<Float8E5M2Type>(outerOutputElemType)) {
1110 castOp,
"avoid introducing f8E4M3FN -> f8E5M2 casts which are not "
1114 if (llvm::isa<Float8E5M2Type, Float8E4M3FNType>(innerInputElemType) &&
1117 castOp,
"avoid introducing fp8 -> integer casts which are not "
1122 llvm::isa<Float8E5M2Type, Float8E4M3FNType>(outerOutputElemType)) {
1124 castOp,
"avoid introducing integer -> fp8 casts which are not "
1128 if (llvm::isa<Float16Type>(innerInputElemType) &&
1129 llvm::isa<BFloat16Type>(outerOutputElemType)) {
1131 castOp,
"avoid introducing fp16 -> bf16 casts which are not "
1135 if (llvm::isa<BFloat16Type>(innerInputElemType) &&
1136 llvm::isa<Float16Type>(outerOutputElemType)) {
1138 castOp,
"avoid introducing bf16 -> fp16 casts which are not "
1142 const auto isIntegerOneOfWidth = [](
Type type,
size_t bitwidth1,
1147 if (isIntegerOneOfWidth(innerInputElemType, 8, 16) &&
1150 castOp,
"avoid introducing i8/i16 -> i64 casts which are not "
1154 if (isIntegerOneOfWidth(innerInputElemType, 1, 64) &&
1157 castOp,
"avoid introducing bool/i64 to float casts which are not "
1158 "supported in all versions of TOSA");
1162 isIntegerOneOfWidth(outerOutputElemType, 1, 64)) {
1164 castOp,
"avoid introducing float to bool/i64 casts which are not "
1165 "supported in all versions of TOSA");
1171 "inner cast operation is narrowing");
1180 return semantics.nonFiniteBehavior !=
1181 llvm::fltNonfiniteBehavior::FiniteOnly;
1185 return semantics.nonFiniteBehavior == llvm::fltNonfiniteBehavior::IEEE754;
1189 const ShapedType outType)
const {
1191 if (inType.getElementType().isInteger() &&
1192 outType.getElementType().isInteger()) {
1194 const auto inTypeSignedness =
1195 cast<IntegerType>(inType.getElementType()).getSignedness();
1196 const auto outTypeSignedness =
1197 cast<IntegerType>(outType.getElementType()).getSignedness();
1199 return (inTypeSignedness != outTypeSignedness ||
1200 inType.getElementTypeBitWidth() >
1201 outType.getElementTypeBitWidth());
1204 if (inType.getElementType().isFloat() &&
1205 outType.getElementType().isFloat()) {
1207 FloatType inElemTy = cast<FloatType>(inType.getElementType());
1208 FloatType outElemTy = cast<FloatType>(outType.getElementType());
1209 llvm::fltSemantics inTypeSemantics = inElemTy.getFloatSemantics();
1210 llvm::fltSemantics outTypeSemantics = outElemTy.getFloatSemantics();
1216 [[maybe_unused]]
const auto isSupported = [](
Type elemType) {
1217 return llvm::isa<Float8E4M3FNType, Float8E5M2Type, BFloat16Type,
1218 Float16Type, Float32Type>(elemType);
1221 assert(isSupported(inElemTy) &&
1222 "unsupported input element type in isNarrowingCast");
1223 assert(isSupported(outElemTy) &&
1224 "unsupported output element type in isNarrowingCast");
1227 inTypeSemantics.maxExponent > outTypeSemantics.maxExponent ||
1228 inTypeSemantics.minExponent < outTypeSemantics.minExponent ||
1229 inTypeSemantics.precision > outTypeSemantics.precision ||
1246 const Value outerInput = castOp.getInput();
1247 auto innerCastOp = outerInput.
getDefiningOp<tosa::CastOp>();
1250 "input must be a cast operation");
1252 const Value innerInput = innerCastOp.getInput();
1253 const auto innerInputTy = llvm::cast<ShapedType>(innerInput.
getType());
1254 const auto innerOutputTy = llvm::cast<ShapedType>(innerCastOp.getType());
1255 const auto outerOutputTy = llvm::cast<ShapedType>(castOp.getType());
1257 if (!llvm::isa<tosa::BlockScaledType>(innerInputTy.getElementType()))
1259 castOp,
"inner cast input must have block scaled element type");
1261 if (innerInputTy != outerOutputTy)
1263 castOp,
"inner input type must match outer output type");
1265 const Type innerOutputElemType = innerOutputTy.getElementType();
1266 const bool isLosslessCast = isa<Float32Type>(innerOutputElemType);
1267 if (!isLosslessCast)
1269 castOp,
"avoid cancelling casts that should be lossy");
1277void CastOp::getCanonicalizationPatterns(RewritePatternSet &results,
1278 MLIRContext *context) {
1279 results.
add<NonNarrowingCastsOptimization,
1280 CancellingBlockScaledCastsOptimization>(context);
1289 const Value castToBlockScaledInput = castToBlockScaledOp.getInputData();
1290 auto castFromBlockScaledOp =
1291 castToBlockScaledInput.
getDefiningOp<tosa::CastFromBlockScaledOp>();
1292 if (!castFromBlockScaledOp)
1294 castToBlockScaledOp,
1295 "input must be cast_from_block_scaled operation");
1297 const Value innerData = castFromBlockScaledOp.getInputData();
1298 const Value innerScale = castFromBlockScaledOp.getInputScale();
1299 const auto innerDataTy = llvm::cast<ShapedType>(innerData.
getType());
1300 const auto innerScaleTy = llvm::cast<ShapedType>(innerScale.
getType());
1302 const Value outerData = castToBlockScaledOp.getOutputData();
1303 const Value outerScale = castToBlockScaledOp.getOutputScale();
1304 const auto outerDataTy = llvm::cast<ShapedType>(outerData.
getType());
1305 const auto outerScaleTy = llvm::cast<ShapedType>(outerScale.
getType());
1307 if (innerDataTy != outerDataTy || innerScaleTy != outerScaleTy) {
1309 castToBlockScaledOp,
1310 "inputs types to cast_from_block_scaled operation must match output "
1311 "types to cast_to_block_scaled");
1314 if (castFromBlockScaledOp.getBlockSize() !=
1315 castToBlockScaledOp.getBlockSize()) {
1317 castToBlockScaledOp,
"block sizes for cast_from_block_scaled and "
1318 "cast_to_block_scaled must match");
1321 rewriter.
replaceOp(castToBlockScaledOp, {innerData, innerScale});
1327void CastToBlockScaledOp::getCanonicalizationPatterns(
1328 RewritePatternSet &results, MLIRContext *context) {
1329 results.
add<CancellingCastToFromBlockScaledOptimization>(context);
1337 const FailureOr<int32_t> rowCount =
1339 if (failed(rowCount) || rowCount.value() != 1)
1343 op, op.getOutput().
getType(), op.getValues(), op.getIndices());
1348void RowGatherOp::getCanonicalizationPatterns(RewritePatternSet &results,
1349 MLIRContext *context) {
1350 results.
add<RowGatherToGather>(context);
1357template <
typename Folder>
1358static DenseElementsAttr
1360 bool foldDenseValues =
false) {
1364 if (!returnTy.hasRank() || !returnTy.hasStaticShape())
1368 const auto rETy = llvm::cast<ShapedType>(
rhs.getType()).getElementType();
1372 if (
lhs.isSplat() &&
rhs.isSplat()) {
1373 if (isa<FloatType>(lETy)) {
1374 const APFloat l =
lhs.getSplatValue<APFloat>();
1375 const APFloat r =
rhs.getSplatValue<APFloat>();
1376 const auto maybeResult = Folder::fold(l, r);
1377 if (failed(maybeResult))
1382 if (
const auto lIntTy = llvm::dyn_cast<IntegerType>(lETy)) {
1383 const APInt l =
lhs.getSplatValue<APInt>();
1384 const APInt r =
rhs.getSplatValue<APInt>();
1385 const auto maybeResult = Folder::fold(l, r, lIntTy.isUnsigned());
1386 if (failed(maybeResult))
1392 if (foldDenseValues) {
1393 assert(lETy.isIntOrIndex() &&
1394 "Only integer types are currently supported.");
1397 llvm::zip(
lhs.getValues<APInt>(),
rhs.getValues<APInt>())) {
1398 const auto maybeResult = Folder::fold(l, r,
false);
1399 if (failed(maybeResult))
1401 resultValues.push_back(maybeResult.value());
1409template <
typename Folder>
1411 bool foldDenseValues =
false) {
1415 if (!returnTy.hasRank() || !returnTy.hasStaticShape())
1421 if (
const auto vIntTy = llvm::dyn_cast<IntegerType>(vETy)) {
1423 const auto maybeResult = Folder::fold(v, vIntTy.isUnsigned());
1424 if (failed(maybeResult))
1430 if (foldDenseValues) {
1434 for (
auto const &v : val.
getValues<APInt>()) {
1435 const auto maybeResult = Folder::fold(v,
false);
1436 if (failed(maybeResult))
1438 resultValues.push_back(maybeResult.value());
1453 assert(dense.isSplat());
1454 APInt a = dense.getSplatValue<APInt>();
1455 return a.getSExtValue();
1460 const bool isUnsigned) {
1463 isUnsigned ?
lhs.uadd_ov(
rhs, overflow) :
lhs.sadd_ov(
rhs, overflow);
1469 static FailureOr<APFloat>
fold(
const APFloat &
lhs,
const APFloat &
rhs) {
1476 const bool isUnsigned) {
1479 isUnsigned ?
lhs.usub_ov(
rhs, overflow) :
lhs.ssub_ov(
rhs, overflow);
1485 static FailureOr<APFloat>
fold(
const APFloat &
lhs,
const APFloat &
rhs) {
1492 const bool isUnsigned) {
1494 const unsigned originalWidth =
lhs.getBitWidth();
1497 if (
lhs.getBitWidth() !=
rhs.getBitWidth()) {
1502 if (
lhs == 0 ||
rhs == 0)
1503 return APInt::getZero(originalWidth);
1505 bool overflow =
false;
1507 isUnsigned ?
lhs.umul_ov(
rhs, overflow) :
lhs.smul_ov(
rhs, overflow);
1512 return result.trunc(originalWidth);
1515 static FailureOr<APFloat>
fold(
const APFloat &
lhs,
const APFloat &
rhs) {
1521 return a.isNegative() !=
b.isNegative();
1528 if (
lhs.getBitWidth() !=
rhs.getBitWidth())
1536 APInt::udivrem(
lhs,
rhs, q, r);
1537 if (!r.isZero() && Ceil) {
1544 bool overflow{
false};
1545 APInt
const q =
lhs.sdiv_ov(
rhs, overflow);
1548 APInt
const r =
lhs.srem(
rhs);
1558 static FailureOr<APFloat>
fold(
const APFloat &
lhs,
const APFloat &
rhs) {
1566 if (
lhs.getBitWidth() !=
rhs.getBitWidth())
1568 if (
lhs.isNegative() || (!
rhs.isStrictlyPositive()))
1578 static FailureOr<APFloat>
fold(
const APFloat &
lhs,
const APFloat &
rhs) {
1580 auto const r = t.mod(
rhs);
1581 if (llvm::APFloatBase::opStatus::opOK == r) {
1591 if (
lhs.getBitWidth() !=
rhs.getBitWidth())
1593 return lhs.getSExtValue() >=
rhs.getSExtValue() ?
lhs :
rhs;
1596 static FailureOr<APFloat>
fold(
const APFloat &
lhs,
const APFloat &
rhs) {
1604 if (
lhs.getBitWidth() !=
rhs.getBitWidth())
1606 return lhs.getSExtValue() <=
rhs.getSExtValue() ?
lhs :
rhs;
1609 static FailureOr<APFloat>
fold(
const APFloat &
lhs,
const APFloat &
rhs) {
1615 static FailureOr<APInt>
fold(
const APInt &value,
bool isUnsigned) {
1616 auto const numBits = value.getBitWidth();
1618 auto const zextv = value.getZExtValue();
1619 if (zextv >= numBits)
1621 return APInt::getOneBitSet(numBits, zextv);
1623 auto const sextv = value.getSExtValue();
1624 if (sextv < 0 || sextv >= numBits || (value.isNegative()))
1626 return APInt::getOneBitSet(numBits, sextv);
1636 assert(!isUnsigned &&
1637 "unsigned values are not supported for shape div folders");
1638 if (
lhs.isNegative() || !
rhs.isStrictlyPositive())
1643 static FailureOr<APFloat>
fold(
const APFloat &
lhs,
const APFloat &
rhs) {
1649 static FailureOr<APInt>
fold(
const APInt &value,
bool isUnsigned) {
1650 if (!value.isStrictlyPositive())
1652 return APInt(value.getBitWidth(), value.ceilLogBase2());
1657 static FailureOr<APInt>
fold(
const APInt &value,
bool isUnsigned) {
1658 if (!value.isStrictlyPositive())
1660 return APInt(value.getBitWidth(), value.logBase2());
1666 const bool isUnsigned) {
1667 return isUnsigned ? APInt(1,
lhs.ugt(
rhs)) : APInt(1,
lhs.sgt(
rhs));
1670 static FailureOr<APInt>
fold(
const APFloat &
lhs,
const APFloat &
rhs) {
1671 return APInt(1,
lhs >
rhs);
1677 const bool isUnsigned) {
1678 return isUnsigned ? APInt(1,
lhs.uge(
rhs)) : APInt(1,
lhs.sge(
rhs));
1681 static FailureOr<APInt>
fold(
const APFloat &
lhs,
const APFloat &
rhs) {
1682 return APInt(1,
lhs >=
rhs);
1688 const bool isUnsigned) {
1689 return APInt(1,
lhs ==
rhs);
1692 static FailureOr<APInt>
fold(
const APFloat &
lhs,
const APFloat &
rhs) {
1693 return APInt(1,
lhs ==
rhs);
1698 if (llvm::isa<FloatType>(elemType))
1700 if (llvm::isa<IntegerType>(elemType))
1706 if (llvm::isa<FloatType>(elemType))
1707 return val && val.
isSplat() &&
1709 if (llvm::isa<IntegerType>(elemType)) {
1710 const int64_t shifted = 1LL << shift;
1711 return val && val.
isSplat() &&
1717OpFoldResult AddOp::fold(FoldAdaptor adaptor) {
1718 auto lhsTy = llvm::dyn_cast<RankedTensorType>(getInput1().
getType());
1719 auto rhsTy = llvm::dyn_cast<RankedTensorType>(getInput2().
getType());
1720 auto resultTy = llvm::dyn_cast<RankedTensorType>(
getType());
1721 if (!lhsTy || !rhsTy || !resultTy)
1725 if (!lhsTy.getElementType().isIntOrIndexOrFloat() ||
1726 !rhsTy.getElementType().isIntOrIndexOrFloat())
1729 auto resultETy = resultTy.getElementType();
1731 llvm::dyn_cast_if_present<DenseElementsAttr>(adaptor.getInput1());
1733 llvm::dyn_cast_if_present<DenseElementsAttr>(adaptor.getInput2());
1736 lhsTy.getShape(), rhsTy.getShape());
1737 if (isBroadcastable && lhsTy == resultTy &&
isSplatZero(resultETy, rhsAttr))
1739 if (isBroadcastable && rhsTy == resultTy &&
isSplatZero(resultETy, lhsAttr))
1742 if (!lhsAttr || !rhsAttr)
1748OpFoldResult ArgMaxOp::fold(FoldAdaptor adaptor) {
1749 auto inputTy = llvm::dyn_cast<RankedTensorType>(getInput().
getType());
1750 auto outputTy = llvm::dyn_cast<RankedTensorType>(
getType());
1751 if (!inputTy || !outputTy || !inputTy.hasStaticShape() ||
1752 !outputTy.hasStaticShape())
1756 if (inputTy.getDimSize(getAxis()) == 1 && outputElementTy.
isInteger()) {
1757 const auto outputElemIntTy = cast<IntegerType>(outputElementTy);
1758 const APInt zero = APInt::getZero(outputElemIntTy.getWidth());
1765OpFoldResult IntDivOp::fold(FoldAdaptor adaptor) {
1766 auto lhsTy = llvm::dyn_cast<RankedTensorType>(getInput1().
getType());
1767 auto rhsTy = llvm::dyn_cast<RankedTensorType>(getInput2().
getType());
1768 auto resultTy = llvm::dyn_cast<RankedTensorType>(
getType());
1769 if (!lhsTy || !rhsTy || !resultTy)
1771 if (lhsTy.getElementType() != rhsTy.getElementType())
1776 auto resultETy = resultTy.getElementType();
1778 llvm::dyn_cast_if_present<DenseElementsAttr>(adaptor.getInput1());
1780 llvm::dyn_cast_if_present<DenseElementsAttr>(adaptor.getInput2());
1781 if (lhsAttr && lhsAttr.isSplat() && rhsAttr && rhsAttr.isSplat()) {
1782 if (llvm::isa<IntegerType>(resultETy) && resultTy.hasStaticShape() &&
1783 lhsAttr.getSplatValue<APInt>().isZero() &&
1784 !rhsAttr.getSplatValue<APInt>().isZero()) {
1785 return lhsAttr.resizeSplat(resultTy);
1789 if (rhsAttr && rhsAttr.isSplat()) {
1791 lhsTy.getShape(), rhsTy.getShape());
1792 if (isBroadcastable && lhsTy == resultTy &&
1793 llvm::isa<IntegerType>(resultETy) &&
1794 rhsAttr.getSplatValue<APInt>().isOne())
1798 if (rhsAttr && lhsAttr && rhsAttr.isSplat() && lhsAttr.isSplat() &&
1799 llvm::isa<IntegerType>(resultETy)) {
1800 APInt l = lhsAttr.getSplatValue<APInt>();
1801 APInt r = rhsAttr.getSplatValue<APInt>();
1803 auto intTy = dyn_cast<mlir::IntegerType>(resultETy);
1805 DivFoldAdaptor<
false>::fold(l, r, intTy.isUnsigned());
1818std::optional<APInt> mulInt(APInt
lhs, APInt
rhs, int32_t shift,
1819 unsigned bitwidth) {
1820 bool overflow =
false;
1821 APInt
result =
lhs.sext(64).smul_ov(
rhs.sext(64), overflow);
1824 return std::nullopt;
1827 auto round = APInt(64, 1) << (shift - 1);
1829 result.ashrInPlace(shift);
1832 if (!(
result.getSExtValue() >= INT32_MIN &&
1833 result.getSExtValue() <= INT32_MAX)) {
1835 return std::nullopt;
1839 return result.trunc(bitwidth);
1842DenseElementsAttr mulBinaryFolder(DenseElementsAttr
lhs, DenseElementsAttr
rhs,
1843 RankedTensorType ty, int32_t shift) {
1845 if (llvm::isa<IntegerType>(ty.getElementType())) {
1846 APInt l =
lhs.getSplatValue<APInt>();
1847 APInt r =
rhs.getSplatValue<APInt>();
1853 auto bitwidth = ty.getElementType().getIntOrFloatBitWidth();
1854 const std::optional<APInt>
result = mulInt(l, r, shift, bitwidth);
1860 if (llvm::isa<FloatType>(ty.getElementType())) {
1861 APFloat l =
lhs.getSplatValue<APFloat>();
1862 APFloat r =
rhs.getSplatValue<APFloat>();
1872OpFoldResult MulOp::fold(FoldAdaptor adaptor) {
1873 auto lhs = getInput1();
1874 auto rhs = getInput2();
1875 auto lhsTy = llvm::dyn_cast<RankedTensorType>(
lhs.getType());
1876 auto rhsTy = llvm::dyn_cast<RankedTensorType>(
rhs.getType());
1877 auto resultTy = llvm::dyn_cast<RankedTensorType>(
getType());
1878 if (!lhsTy || !rhsTy || !resultTy)
1881 auto resultETy = resultTy.getElementType();
1883 llvm::dyn_cast_if_present<DenseElementsAttr>(adaptor.getInput1());
1885 llvm::dyn_cast_if_present<DenseElementsAttr>(adaptor.getInput2());
1890 if (resultETy.isInteger(32)) {
1891 ElementsAttr shift_elem;
1892 if (getShift().getImpl()) {
1896 shift = shift_elem.getValues<IntegerAttr>()[0].getInt();
1900 if (rhsTy == resultTy &&
isSplatZero(resultETy, lhsAttr) &&
1901 resultTy.hasStaticShape())
1903 return lhsAttr.resizeSplat(resultTy);
1904 if (lhsTy == resultTy &&
isSplatZero(resultETy, rhsAttr) &&
1905 resultTy.hasStaticShape())
1906 return rhsAttr.resizeSplat(resultTy);
1909 lhsTy.getShape(), rhsTy.getShape());
1910 if (isBroadcastable && rhsTy == resultTy &&
1913 if (isBroadcastable && lhsTy == resultTy &&
1917 return mulBinaryFolder(lhsAttr, rhsAttr, resultTy, shift);
1920OpFoldResult SubOp::fold(FoldAdaptor adaptor) {
1921 auto lhsTy = llvm::dyn_cast<RankedTensorType>(getInput1().
getType());
1922 auto rhsTy = llvm::dyn_cast<RankedTensorType>(getInput2().
getType());
1923 auto resultTy = llvm::dyn_cast<RankedTensorType>(
getType());
1924 if (!lhsTy || !rhsTy || !resultTy)
1928 if (!lhsTy.getElementType().isIntOrIndexOrFloat() ||
1929 !rhsTy.getElementType().isIntOrIndexOrFloat())
1932 auto resultETy = resultTy.getElementType();
1934 llvm::dyn_cast_if_present<DenseElementsAttr>(adaptor.getInput1());
1936 llvm::dyn_cast_if_present<DenseElementsAttr>(adaptor.getInput2());
1939 lhsTy.getShape(), rhsTy.getShape());
1940 if (isBroadcastable && lhsTy == resultTy &&
isSplatZero(resultETy, rhsAttr))
1943 if (!lhsAttr || !rhsAttr)
1949OpFoldResult GreaterOp::fold(FoldAdaptor adaptor) {
1950 auto resultTy = llvm::cast<ShapedType>(
getType());
1952 llvm::dyn_cast_if_present<DenseElementsAttr>(adaptor.getInput1());
1954 llvm::dyn_cast_if_present<DenseElementsAttr>(adaptor.getInput2());
1956 if (!lhsAttr || !rhsAttr)
1962OpFoldResult GreaterEqualOp::fold(FoldAdaptor adaptor) {
1963 auto resultTy = llvm::cast<ShapedType>(
getType());
1965 llvm::dyn_cast_if_present<DenseElementsAttr>(adaptor.getInput1());
1967 llvm::dyn_cast_if_present<DenseElementsAttr>(adaptor.getInput2());
1969 if (!lhsAttr || !rhsAttr)
1975OpFoldResult EqualOp::fold(FoldAdaptor adaptor) {
1976 auto resultTy = llvm::cast<ShapedType>(
getType());
1978 llvm::dyn_cast_if_present<DenseElementsAttr>(adaptor.getInput1());
1980 llvm::dyn_cast_if_present<DenseElementsAttr>(adaptor.getInput2());
1981 Value
lhs = getInput1();
1982 Value
rhs = getInput2();
1983 auto lhsTy = llvm::cast<ShapedType>(
lhs.getType());
1987 if (llvm::isa<IntegerType>(lhsTy.getElementType()) && resultTy.hasRank() &&
1988 resultTy.hasStaticShape() &&
lhs ==
rhs) {
1992 if (!lhsAttr || !rhsAttr)
1998OpFoldResult CastOp::fold(FoldAdaptor adaptor) {
2002 auto operand = llvm::dyn_cast_if_present<ElementsAttr>(adaptor.getInput());
2006 auto inTy = llvm::cast<ShapedType>(getInput().
getType());
2007 auto outTy = llvm::cast<ShapedType>(
getType());
2008 if (!outTy.hasRank() || !outTy.hasStaticShape())
2010 auto inETy = inTy.getElementType();
2011 auto outETy = outTy.getElementType();
2013 if (operand.isSplat()) {
2014 if (llvm::isa<FloatType>(inETy) && llvm::isa<FloatType>(outETy)) {
2016 auto splatVal = operand.getSplatValue<APFloat>();
2017 auto &semantics = llvm::cast<FloatType>(outETy).getFloatSemantics();
2018 splatVal.convert(semantics, llvm::RoundingMode::NearestTiesToEven,
2023 if (llvm::isa<IntegerType>(inETy) && llvm::isa<FloatType>(outETy)) {
2024 auto unsign = llvm::cast<IntegerType>(inETy).isUnsignedInteger();
2025 APFloat splatVal(llvm::cast<FloatType>(outETy).getFloatSemantics());
2026 splatVal.convertFromAPInt(operand.getSplatValue<APInt>(), !unsign,
2027 llvm::RoundingMode::NearestTiesToEven);
2031 if (llvm::isa<FloatType>(inETy) && llvm::isa<IntegerType>(outETy)) {
2032 auto unsign = llvm::cast<IntegerType>(outETy).isUnsignedInteger();
2033 auto intVal = APSInt(
2034 llvm::cast<IntegerType>(outETy).getIntOrFloatBitWidth(), unsign);
2035 auto floatVal = operand.getSplatValue<APFloat>();
2037 floatVal.convertToInteger(intVal, llvm::RoundingMode::NearestTiesToEven,
2042 if (llvm::isa<IntegerType>(inETy) && llvm::isa<IntegerType>(outETy)) {
2043 const auto inIntType = llvm::cast<IntegerType>(inETy);
2044 auto unsignIn = inIntType.isUnsignedInteger();
2046 inETy.getIntOrFloatBitWidth() > outETy.getIntOrFloatBitWidth();
2047 auto intVal = operand.getSplatValue<APInt>();
2048 auto bitwidth = outETy.getIntOrFloatBitWidth();
2051 if (outETy.isInteger(1)) {
2052 intVal = APInt(bitwidth, intVal.isZero() ? 0 : 1);
2054 intVal = intVal.trunc(bitwidth);
2055 }
else if (unsignIn || inIntType.isInteger(1)) {
2056 intVal = intVal.zext(bitwidth);
2058 intVal = intVal.sext(bitwidth);
2068OpFoldResult ConstOp::fold(FoldAdaptor adaptor) {
return getValuesAttr(); }
2070OpFoldResult ConstShapeOp::fold(FoldAdaptor adaptor) {
return getValuesAttr(); }
2072#define REDUCE_FOLDER(OP) \
2073 OpFoldResult OP::fold(FoldAdaptor adaptor) { \
2074 ShapedType inputTy = llvm::cast<ShapedType>(getInput().getType()); \
2075 if (!inputTy.hasRank()) \
2077 if (inputTy != getType()) \
2079 if (inputTy.getRank() == 0 || inputTy.getDimSize(getAxis()) == 1) \
2080 return getInput(); \
2093 auto inputTy = llvm::dyn_cast<RankedTensorType>(getInput1().
getType());
2094 auto outputTy = llvm::dyn_cast<RankedTensorType>(
getType());
2096 if (!inputTy || !outputTy)
2102 if (inputTy == outputTy && inputTy.getNumDynamicDims() < 2)
2106 if (
auto reshapeOp = llvm::dyn_cast_if_present<tosa::ReshapeOp>(
2107 getInput1().getDefiningOp())) {
2108 getInput1Mutable().assign(reshapeOp.getInput1());
2113 if (!inputTy.getElementType().isIntOrIndexOrFloat())
2118 llvm::dyn_cast_if_present<DenseElementsAttr>(adaptor.getInput1())) {
2120 if (!outputTy.hasStaticShape())
2124 if (operand.isSplat())
2129 if (!getInput1().hasOneUse())
2136 return operand.reshape(
2137 llvm::cast<ShapedType>(operand.getType()).clone(shapeVec));
2143OpFoldResult PadOp::fold(FoldAdaptor adaptor) {
2145 if (adaptor.getPadding() && getInput1().
getType() ==
getType()) {
2146 auto densePad = llvm::dyn_cast<DenseElementsAttr>(adaptor.getPadding());
2147 if (densePad && densePad.isSplat() &&
2148 densePad.getSplatValue<APInt>().isZero()) {
2158OpFoldResult ResizeOp::fold(FoldAdaptor adaptor) {
2160 llvm::dyn_cast_if_present<DenseElementsAttr>(adaptor.getScale());
2162 llvm::dyn_cast_if_present<DenseElementsAttr>(adaptor.getOffset());
2164 llvm::dyn_cast_if_present<DenseElementsAttr>(adaptor.getBorder());
2165 if (!scaleAttr || !offsetAttr || !borderAttr) {
2172 if (scale.size() != 4 || offset.size() != 2 || border.size() != 2) {
2177 if (scale[0] != scale[1] || scale[2] != scale[3]) {
2182 if (offset[0] != 0 || offset[1] != 0) {
2187 if (border[0] != 0 || border[1] != 0) {
2191 return foldToInputIfTypeMatches(
getType(), getInput());
2194OpFoldResult ReverseOp::fold(FoldAdaptor adaptor) {
2195 auto operand = getInput1();
2196 auto operandTy = llvm::cast<ShapedType>(operand.getType());
2197 auto axis = getAxis();
2199 const bool isSplatInput =
2200 llvm::isa_and_nonnull<SplatElementsAttr>(adaptor.getInput1());
2201 if (!operandTy.hasRank() ||
2202 (!isSplatInput && operandTy.getDimSize(axis) != 1))
2204 return foldToInputIfTypeMatches(
getType(), operand);
2207OpFoldResult SliceOp::fold(FoldAdaptor adaptor) {
2208 auto inputTy = llvm::dyn_cast<RankedTensorType>(getInput1().
getType());
2209 auto outputTy = llvm::dyn_cast<RankedTensorType>(
getType());
2211 if (!inputTy || !outputTy)
2214 if (inputTy == outputTy && inputTy.hasStaticShape())
2219 DenseElementsAttr startElems;
2225 llvm::all_of(startElems.
getValues<APInt>(),
2226 [](
const APInt &val) { return val.isZero(); });
2231 DenseElementsAttr sizeElems;
2235 auto inputShape = inputTy.getShape();
2236 auto sizeValues = sizeElems.
getValues<APInt>();
2238 bool sizeMatchesInput =
true;
2239 for (
const auto &[i, sizeVal] : llvm::enumerate(sizeValues)) {
2240 int64_t size = sizeVal.getSExtValue();
2242 if (inputTy.isDynamicDim(i)) {
2246 sizeMatchesInput =
false;
2253 sizeMatchesInput =
false;
2259 if (sizeMatchesInput)
2264 if (!adaptor.getInput1())
2268 if (!inputTy.getElementType().isIntOrIndexOrFloat() ||
2269 !outputTy.getElementType().isIntOrIndexOrFloat())
2272 auto operand = llvm::cast<ElementsAttr>(adaptor.getInput1());
2273 if (operand.isSplat() && outputTy.hasStaticShape()) {
2277 if (inputTy.hasStaticShape() && outputTy.hasStaticShape() &&
2278 outputTy.getNumElements() == 1) {
2279 llvm::SmallVector<uint64_t>
indices =
2280 llvm::to_vector(startElems.
getValues<uint64_t>());
2281 if (
auto values = operand.tryGetValues<Attribute>())
2288OpFoldResult tosa::SelectOp::fold(FoldAdaptor adaptor) {
2289 const Value pred = getPred();
2290 const Value onTrue = getOnTrue();
2291 const Value onFalse = getOnFalse();
2293 const auto predTy = llvm::dyn_cast<RankedTensorType>(pred.
getType());
2294 const auto onTrueTy = llvm::dyn_cast<RankedTensorType>(onTrue.
getType());
2295 const auto onFalseTy = llvm::dyn_cast<RankedTensorType>(onFalse.
getType());
2296 if (!predTy || !onTrueTy || !onFalseTy)
2299 const Type resultTy =
getType();
2301 const ArrayRef<int64_t> predShape = predTy.getShape();
2302 const ArrayRef<int64_t> onTrueShape = onTrueTy.getShape();
2304 if (onTrue == onFalse && onTrueTy == resultTy &&
2309 llvm::dyn_cast_if_present<DenseIntElementsAttr>(adaptor.getInput1());
2312 if (!predicate.isSplat())
2315 const bool predicateValue = predicate.getSplatValue<APInt>().getBoolValue();
2317 SmallVector<SmallVector<int64_t>, 3> shapes;
2318 shapes.emplace_back(predShape);
2319 shapes.emplace_back(onTrueShape);
2320 shapes.emplace_back(onFalseTy.getShape());
2321 const bool isBroadcastable =
2324 if (predicateValue ==
true && onTrueTy == resultTy && isBroadcastable)
2326 if (predicateValue ==
false && onFalseTy == resultTy && isBroadcastable)
2332 const auto inputType =
2333 dyn_cast<RankedTensorType>(tileOp.getInput1().getType());
2334 const auto outputType = dyn_cast<RankedTensorType>(tileOp.getType());
2335 if (!inputType || !outputType)
2339 if (failed(tileOp.getConstantMultiples(multiples)))
2342 for (
const auto [
index, multiple] : llvm::enumerate(multiples)) {
2345 if (outputType.isDynamicDim(
index))
2347 if (inputType.getDimSize(
index) != 1)
2360 Value tileOutput = tileOp.getOutput();
2363 "tile output must have one use");
2366 const bool isBinaryElementwise =
2369 if (!isBinaryElementwise && !isa<tosa::MulOp>(user))
2371 tileOp,
"consumer must be binary broadcastable");
2375 tileOp,
"tile must only expand statically-known singleton dims");
2379 Value otherOperand = lhsOperand == tileOutput ? rhsOperand : lhsOperand;
2380 Value tileInput = tileOp.getInput1();
2382 const ShapedType newOtherType = cast<ShapedType>(otherOperand.
getType());
2383 const ShapedType newTileType = cast<ShapedType>(tileInput.
getType());
2386 newOtherType.getShape(), newTileType.getShape(), broadcastedShape);
2388 const ShapedType outputType = cast<ShapedType>(user->
getResultTypes()[0]);
2389 if (!llvm::equal(broadcastedShape, outputType.getShape()))
2391 tileOp,
"tile output must be broadcastable to consumer operands");
2395 mapper.
map(tileOutput, tileOp.getInput1());
2402void TileOp::getCanonicalizationPatterns(RewritePatternSet &results,
2403 MLIRContext *context) {
2404 results.
add<RemoveBroadcastTileFromBinaryElementwise>(context);
2407OpFoldResult TileOp::fold(FoldAdaptor adaptor) {
2409 if (
auto multiples = llvm::dyn_cast_if_present<DenseElementsAttr>(
2410 adaptor.getMultiples())) {
2411 if (multiples.isSplat() &&
2412 multiples.getSplatValue<APInt>().getSExtValue() == 1)
2414 if (
auto int_array_attr =
2415 llvm::dyn_cast<DenseIntElementsAttr>(multiples)) {
2416 if (llvm::all_of(int_array_attr.getValues<APInt>(),
2417 [](APInt v) { return v.getSExtValue() == 1; }))
2425OpFoldResult TransposeOp::fold(FoldAdaptor adaptor) {
2426 auto resultTy = llvm::cast<ShapedType>(
getType());
2430 llvm::dyn_cast_if_present<DenseElementsAttr>(adaptor.getInput1())) {
2431 if (input.isSplat() && resultTy.hasRank() && resultTy.hasStaticShape() &&
2432 input.
getType().getElementType() == resultTy.getElementType())
2433 return input.reshape(resultTy);
2437 const llvm::ArrayRef<int32_t> perms = getPerms();
2439 if (!llvm::equal(llvm::seq<int32_t>(0, perms.size()), perms))
2442 return foldToInputIfTypeMatches(
getType(), getInput1());
2445OpFoldResult tosa::NegateOp::fold(FoldAdaptor adaptor) {
2448 auto definingOp = getInput1().getDefiningOp<tosa::NegateOp>();
2454 if (FailureOr<int64_t> maybeIZp = getInput1ZeroPoint();
2455 failed(maybeIZp) || *maybeIZp != 0) {
2459 if (FailureOr<int64_t> maybeOZp = getOutputZeroPoint();
2460 failed(maybeOZp) || *maybeOZp != 0) {
2464 if (FailureOr<int64_t> maybeIZp = definingOp.getInput1ZeroPoint();
2465 failed(maybeIZp) || *maybeIZp != 0) {
2469 if (FailureOr<int64_t> maybeOZp = definingOp.getOutputZeroPoint();
2470 failed(maybeOZp) || *maybeOZp != 0) {
2475 return foldToInputIfTypeMatches(
getType(), definingOp.getInput1());
2478OpFoldResult tosa::AbsOp::fold(FoldAdaptor adaptor) {
2479 auto input = getInput1();
2482 return foldToInputIfTypeMatches(
getType(), input);
2487OpFoldResult tosa::ReciprocalOp::fold(FoldAdaptor adaptor) {
2488 auto input = adaptor.getInput1();
2490 auto inputAttr = llvm::dyn_cast_if_present<DenseElementsAttr>(input);
2492 if (!inputAttr || !inputAttr.isSplat())
2495 auto shapeType = llvm::cast<ShapedType>(
getType());
2496 if (!shapeType.hasRank() || !shapeType.hasStaticShape())
2498 if (
auto floatType = llvm::dyn_cast<FloatType>(inputAttr.getElementType())) {
2499 auto floatVal = inputAttr.getSplatValue<APFloat>();
2501 ReciprocalOp::calcOneElement(floatVal));
2507template <
typename Op,
typename OpFoldAdaptor>
2509 auto input1ConstShape =
2510 dyn_cast<tosa::ConstShapeOp>(op->getInput().getDefiningOp());
2511 if (!input1ConstShape)
2514 const auto input1Attr = cast<DenseElementsAttr>(input1ConstShape.getValues());
2520template <
typename Op,
typename OpFoldAdaptor>
2522 auto input1ConstShape =
2523 dyn_cast<tosa::ConstShapeOp>(op->getInput1().getDefiningOp());
2524 auto input2ConstShape =
2525 dyn_cast<tosa::ConstShapeOp>(op->getInput2().getDefiningOp());
2526 if (!input1ConstShape || !input2ConstShape)
2529 const auto input1Attr = cast<DenseElementsAttr>(input1ConstShape.getValues());
2530 const auto input2Attr = cast<DenseElementsAttr>(input2ConstShape.getValues());
2533 input1Attr.getType(),
2537OpFoldResult tosa::DimOp::fold(FoldAdaptor adaptor) {
2538 const auto inputTy = llvm::dyn_cast<ShapedType>(getInput1().
getType());
2539 if (!inputTy || !inputTy.hasRank())
2541 const int32_t axis = getAxis();
2542 const int64_t dimSize = inputTy.getDimSize(axis);
2543 if (ShapedType::isDynamic(dimSize))
2547 const auto resultAttrTy =
2548 RankedTensorType::get(1, builder.getIndexType());
2553 auto const inputs = op->getInput();
2559 concatDims.reserve( 64);
2560 for (
auto const &v : inputs) {
2561 auto vConstShape = dyn_cast<tosa::ConstShapeOp>(v.getDefiningOp());
2565 const auto vAttr = cast<DenseElementsAttr>(vConstShape.getValues());
2568 auto const vAttrVals = vAttr.getValues<APInt>();
2569 for (
auto const &v : vAttrVals) {
2570 concatDims.push_back(v);
2574 auto *ctx = op->getContext();
2575 assert(ctx !=
nullptr &&
"ctx is nullptr");
2576 auto const rankedTy = RankedTensorType::get(
2577 {
static_cast<int64_t>(concatDims.size())}, IndexType::get(ctx));
2583 auto const input1 = op->getInput();
2584 auto const input2 = op->getStart();
2585 auto const input3 = op->getSize();
2587 auto input1ConstShape = dyn_cast<tosa::ConstShapeOp>(input1.getDefiningOp());
2589 if (!input1ConstShape)
2592 auto const input1Attr = cast<DenseElementsAttr>(input1ConstShape.getValues());
2596 auto const input1Vals = input1Attr.getValues<APInt>();
2597 auto const totalInput1 = input1Vals.size();
2602 if (failed(start) || failed(size))
2605 auto const startV =
static_cast<int32_t
>(start.value());
2606 auto const sizeV =
static_cast<int32_t
>(size.value());
2608 if ((sizeV <= 0) || (startV < 0) ||
2609 (
static_cast<size_t>(startV + sizeV) > totalInput1))
2613 sliceOfInput.reserve(totalInput1);
2615 for (
auto i = startV; i < (startV + sizeV); i++) {
2616 sliceOfInput.push_back(input1Vals[i]);
2619 auto *ctx = op->getContext();
2620 assert(ctx !=
nullptr &&
"ctx is nullptr");
2622 auto const rankedTy = RankedTensorType::get(
2623 {
static_cast<int64_t>(sliceOfInput.size())}, IndexType::get(ctx));
2628OpFoldResult tosa::AddShapeOp::fold(FoldAdaptor adaptor) {
2632OpFoldResult tosa::SubShapeOp::fold(FoldAdaptor adaptor) {
2636OpFoldResult tosa::MulShapeOp::fold(FoldAdaptor adaptor) {
2640OpFoldResult tosa::DivCeilShapeOp::fold(FoldAdaptor adaptor) {
2641 return binaryFold<DivCeilShapeOp, ShapeDivFoldAdaptor<
true>>(
this);
2644OpFoldResult tosa::DivFloorShapeOp::fold(FoldAdaptor adaptor) {
2645 return binaryFold<DivFloorShapeOp, ShapeDivFoldAdaptor<
false>>(
this);
2648OpFoldResult tosa::ModShapeOp::fold(FoldAdaptor adaptor) {
2652OpFoldResult tosa::MaxShapeOp::fold(FoldAdaptor adaptor) {
2656OpFoldResult tosa::MinShapeOp::fold(FoldAdaptor adaptor) {
2660OpFoldResult tosa::Exp2ShapeOp::fold(FoldAdaptor adaptor) {
2664OpFoldResult tosa::Log2CeilShapeOp::fold(FoldAdaptor adaptor) {
2668OpFoldResult tosa::Log2FloorShapeOp::fold(FoldAdaptor adaptor) {
2672OpFoldResult tosa::ConcatShapeOp::fold(FoldAdaptor adaptor) {
2676OpFoldResult tosa::SliceShapeOp::fold(FoldAdaptor adaptor) {
static bool isSplatZero(Type elemType, DenseElementsAttr val)
Returns true if 'val' is a splat of zero, false otherwise.
*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`
#define REDUCE_FOLDER(OP)
OpFoldResult concatShapeFold(tosa::ConcatShapeOp *op)
static DenseElementsAttr binaryFolder(DenseElementsAttr lhs, DenseElementsAttr rhs, ShapedType returnTy, bool foldDenseValues=false)
static DenseElementsAttr unaryFolder(DenseElementsAttr val, ShapedType returnTy, bool foldDenseValues=false)
static LogicalResult verifyTileIsBroadcast(tosa::TileOp tileOp)
OpFoldResult sliceShapeFold(tosa::SliceShapeOp *op)
static FailureOr< int64_t > getSingleI64From1ElementTensor(Value v)
OpFoldResult binaryFold(Op *op)
static bool isSplatOne(Type elemType, DenseElementsAttr val, int64_t shift)
OpFoldResult unaryShapeFold(Op *op)
static bool checkMatchingPadConstAndZp(Value padConst, Value zp)
static bool signsDiffer(const APInt &a, const APInt &b)
static ArrayRef< int64_t > getShape(Type type)
Returns the shape of the given type.
Attributes are known-constant values of operations.
DenseI32ArrayAttr getDenseI32ArrayAttr(ArrayRef< int32_t > values)
IntegerAttr getIntegerAttr(Type type, int64_t value)
DenseI64ArrayAttr getDenseI64ArrayAttr(ArrayRef< int64_t > values)
FloatAttr getFloatAttr(Type type, double value)
Ty getType(Args &&...args)
Get or construct an instance of the type Ty with provided arguments.
MLIRContext * getContext() const
An attribute that represents a reference to a dense vector or tensor object.
auto getValues() const
Return the held element values as a range of the given type.
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.
int64_t size() const
Returns the number of elements held by this attribute.
bool isSplat() const
Returns true if this attribute corresponds to a splat, i.e.
Type getElementType() const
Return the element type of this DenseElementsAttr.
static DenseElementsAttr get(ShapedType type, ArrayRef< Attribute > values)
Constructs a dense elements attribute from an array of element values.
ShapedType getType() const
Return the type of this ElementsAttr, guaranteed to be a vector or tensor with static shape.
An attribute that represents a reference to a dense integer vector or tensor object.
iterator begin() const
Iterator access to the integer element values.
This is a utility class for mapping one set of IR entities to another.
void map(Value from, Value to)
Inserts a new mapping for 'from' to 'to'.
MLIRContext is the top-level object for a collection of MLIR operations.
Operation * clone(Operation &op, IRMapping &mapper)
Creates a deep copy of the specified operation, remapping any operands that use values outside of the...
void setInsertionPoint(Block *block, Block::iterator insertPoint)
Set the insertion point to the specified location.
This class represents a single result from folding an operation.
This class indicates that an op is tosa-elementwise (permits broadcasting, unlike Elementwise trait).
This provides public APIs that all operations should have.
This class implements the operand iterators for the Operation class.
Operation is the basic unit of execution within MLIR.
Value getOperand(unsigned idx)
bool hasTrait()
Returns true if the operation was registered with a particular trait, e.g.
unsigned getNumOperands()
result_type_range getResultTypes()
result_range getResults()
A special type of RewriterBase that coordinates the application of a rewrite pattern on the current I...
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...
std::enable_if_t<!std::is_convertible< CallbackT, Twine >::value, LogicalResult > notifyMatchFailure(Location loc, CallbackT &&reasonCallback)
Used to notify the listener that the IR failed to be rewritten because of a match failure,...
void modifyOpInPlace(Operation *root, CallableT &&callable)
This method is a utility wrapper around an in-place modification of an operation.
OpTy replaceOpWithNewOp(Operation *op, Args &&...args)
Replace the results of the given (original) op with a new op that is created without verification (re...
Instances of the Type class are uniqued, have an immutable identifier and an optional mutable compone...
bool isIntOrIndex() const
Return true if this is an integer (of any signedness) or an index type.
bool isInteger() const
Return true if this is an integer type (with the specified width).
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.
user_iterator user_begin() const
bool hasOneUse() const
Returns true if this value has exactly one use.
Operation * getDefiningOp() const
If this value is the result of an operation, return the operation that defines it.
bool staticallyKnownBroadcastable(ArrayRef< SmallVector< int64_t, 6 > > shapes)
Returns true if a broadcast between n shapes is guaranteed to be successful and not result in an erro...
bool getBroadcastedShape(ArrayRef< int64_t > shape1, ArrayRef< int64_t > shape2, SmallVectorImpl< int64_t > &resultShape)
Returns true and sets resultShape to the broadcasted shape from the two given shapes if they are broa...
DynamicAPInt round(const Fraction &f)
TosaLevel getTosaLevelFromEnum(const Level level)
constexpr int64_t kInferableDimSize
Represents a dimension in the shape of a tensor that can be inferred based on the other provided dime...
SmallVector< int64_t > convertFromIntAttr(const DenseElementsAttr &attr, const int rank)
TargetEnvAttr lookupTargetEnv(Operation *op)
FailureOr< T > getConstantScalarIntValue(Value val)
Value getTosaConstShape(ImplicitLocOpBuilder &builder, llvm::ArrayRef< int64_t > shape)
Type getStorageElementTypeFromQuantized(quant::QuantizedType quantizedType)
bool getConstShapeValues(Operation *op, llvm::SmallVector< int64_t > &result_shape)
Include the generated interface declarations.
bool matchPattern(Value value, const Pattern &pattern)
Entry point for matching a pattern over a Value.
Type getType(OpFoldResult ofr)
Returns the int type of the integer in ofr.
Type getElementTypeOrSelf(Type type)
Return the element type or return the type itself.
detail::constant_op_matcher m_Constant()
Matches a constant foldable operation.
static FailureOr< APInt > fold(const APInt &lhs, const APInt &rhs, const bool isUnsigned)
static FailureOr< APFloat > fold(const APFloat &lhs, const APFloat &rhs)
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...
LogicalResult matchAndRewrite(tosa::AvgPool2dAdaptiveOp op, PatternRewriter &rewriter) const override
LogicalResult matchAndRewrite(tosa::AvgPool2dOp op, PatternRewriter &rewriter) const override
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...
LogicalResult matchAndRewrite(tosa::CastOp castOp, PatternRewriter &rewriter) const override
LogicalResult matchAndRewrite(tosa::CastToBlockScaledOp castToBlockScaledOp, PatternRewriter &rewriter) const override
bool intersects(const ClampRange< T > &otherRange)
ClampRange(const T &start, const T &end)
LogicalResult matchAndRewrite(tosa::ClampOp op, PatternRewriter &rewriter) const override
LogicalResult matchAndRewrite(tosa::ClampOp op, PatternRewriter &rewriter) const override
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...
LogicalResult matchAndRewrite(tosa::ConcatOp op, PatternRewriter &rewriter) const override
LogicalResult matchAndRewrite(tosa::SliceOp sliceOp, PatternRewriter &rewriter) const override
LogicalResult matchAndRewrite(tosa::ConcatOp op, PatternRewriter &rewriter) const override
LogicalResult matchAndRewrite(tosa::TransposeOp transposeOp, PatternRewriter &rewriter) const override
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...
static FailureOr< APInt > fold(const APInt &lhs, const APInt &rhs, bool isUnsigned)
static FailureOr< APFloat > fold(const APFloat &lhs, const APFloat &rhs)
static FailureOr< APInt > fold(const APFloat &lhs, const APFloat &rhs)
static FailureOr< APInt > fold(const APInt &lhs, const APInt &rhs, const bool isUnsigned)
static FailureOr< APInt > fold(const APInt &value, bool isUnsigned)
static FailureOr< APInt > fold(const APFloat &lhs, const APFloat &rhs)
static FailureOr< APInt > fold(const APInt &lhs, const APInt &rhs, const bool isUnsigned)
static FailureOr< APInt > fold(const APInt &lhs, const APInt &rhs, const bool isUnsigned)
static FailureOr< APInt > fold(const APFloat &lhs, const APFloat &rhs)
static FailureOr< APInt > fold(const APInt &value, bool isUnsigned)
static FailureOr< APInt > fold(const APInt &value, bool isUnsigned)
static FailureOr< APFloat > fold(const APFloat &lhs, const APFloat &rhs)
static FailureOr< APInt > fold(const APInt &lhs, const APInt &rhs, bool isUnsigned)
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...
LogicalResult matchAndRewrite(tosa::MaxPool2dAdaptiveOp op, PatternRewriter &rewriter) const override
LogicalResult matchAndRewrite(tosa::MaxPool2dOp op, PatternRewriter &rewriter) const override
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...
static FailureOr< APInt > fold(const APInt &lhs, const APInt &rhs, bool isUnsigned)
static FailureOr< APFloat > fold(const APFloat &lhs, const APFloat &rhs)
static FailureOr< APInt > fold(const APInt &lhs, const APInt &rhs, bool isUnsigned)
static FailureOr< APFloat > fold(const APFloat &lhs, const APFloat &rhs)
static FailureOr< APInt > fold(const APInt &lhs, const APInt &rhs, const bool isUnsigned)
static FailureOr< APFloat > fold(const APFloat &lhs, const APFloat &rhs)
bool isNarrowingCast(const ShapedType inType, const ShapedType outType) const
LogicalResult matchAndRewrite(tosa::CastOp castOp, PatternRewriter &rewriter) const override
bool supportsInf(const llvm::fltSemantics &semantics) const
bool supportsNaN(const llvm::fltSemantics &semantics) const
LogicalResult matchAndRewrite(tosa::SliceOp sliceOp, PatternRewriter &rewriter) const override
LogicalResult matchAndRewrite(tosa::TileOp tileOp, PatternRewriter &rewriter) const override
LogicalResult matchAndRewrite(tosa::RowGatherOp op, PatternRewriter &rewriter) const override
static FailureOr< APFloat > fold(const APFloat &lhs, const APFloat &rhs)
static FailureOr< APInt > fold(const APInt &lhs, const APInt &rhs, bool isUnsigned)
LogicalResult matchAndRewrite(tosa::SliceOp sliceOp, PatternRewriter &rewriter) const override
static FailureOr< APFloat > fold(const APFloat &lhs, const APFloat &rhs)
static FailureOr< APInt > fold(const APInt &lhs, const APInt &rhs, const bool isUnsigned)
LogicalResult matchAndRewrite(tosa::TransposeOp op, PatternRewriter &rewriter) const override
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...
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...
int32_t MAX_TENSOR_LIST_SIZE