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)) {
602 if (op.getNanMode() == tosa::NanPropagationMode::IGNORE)
606 const auto minClamp =
607 llvm::cast<mlir::FloatAttr>(op.getMinValAttr()).getValue();
608 const auto maxClamp =
609 llvm::cast<mlir::FloatAttr>(op.getMaxValAttr()).getValue();
610 const bool isMin = minClamp.isNegInfinity();
611 const bool isMax = maxClamp.isInfinity();
613 if (isMin && isMax) {
621 const bool isBoolean = inputElementType.isInteger(1);
622 if (inputElementType.isUnsignedInteger() || isBoolean) {
623 const int64_t minClamp = llvm::cast<mlir::IntegerAttr>(op.getMinValAttr())
626 const int64_t maxClamp = llvm::cast<mlir::IntegerAttr>(op.getMaxValAttr())
630 const unsigned bitWidth = inputElementType.getIntOrFloatBitWidth();
631 const int64_t intMin = APInt::getMinValue(bitWidth).getZExtValue();
632 const int64_t intMax = APInt::getMaxValue(bitWidth).getZExtValue();
634 if (minClamp <= intMin && maxClamp >= intMax) {
641 if (llvm::isa<IntegerType>(inputElementType)) {
643 llvm::cast<mlir::IntegerAttr>(op.getMinValAttr()).getInt();
645 llvm::cast<mlir::IntegerAttr>(op.getMaxValAttr()).getInt();
647 const unsigned bitWidth = inputElementType.getIntOrFloatBitWidth();
648 const int64_t intMin = APInt::getSignedMinValue(bitWidth).getSExtValue();
649 const int64_t intMax = APInt::getSignedMaxValue(bitWidth).getSExtValue();
651 if (minClamp <= intMin && maxClamp >= intMax) {
683 template <
typename T>
697 Value input = op.getInput();
705 const auto opNanMode = op.getNanMode();
706 const auto clampNanMode = clampOp.getNanMode();
707 if (opNanMode == NanPropagationMode::IGNORE &&
708 clampNanMode == NanPropagationMode::PROPAGATE)
711 auto maxValAttr = op.getMaxValAttr();
712 auto minValAttr = op.getMinValAttr();
713 auto clampOpMaxValAttr = clampOp.getMaxValAttr();
714 auto clampOpMinValAttr = clampOp.getMinValAttr();
716 auto inputEType = llvm::cast<ShapedType>(input.
getType()).getElementType();
718 llvm::dyn_cast<mlir::quant::UniformQuantizedType>(inputEType)) {
723 if (mlir::isa<FloatType>(inputEType)) {
724 auto floatMaxValAttr = cast<mlir::FloatAttr>(maxValAttr);
725 auto floatMinValAttr = cast<mlir::FloatAttr>(minValAttr);
726 auto clampOpFloatMaxValAttr = cast<mlir::FloatAttr>(clampOpMaxValAttr);
727 auto clampOpFloatMinValAttr = cast<mlir::FloatAttr>(clampOpMinValAttr);
730 const auto opMinFloat = floatMinValAttr.getValue();
731 const auto opMaxFloat = floatMaxValAttr.getValue();
732 const auto clampOpMinFloat = clampOpFloatMinValAttr.getValue();
733 const auto clampOpMaxFloat = clampOpFloatMaxValAttr.getValue();
737 if (!opRangeFloatRange.
intersects(clampRangeFloatRange))
741 auto newMinVal = std::max(opMinFloat, clampOpMinFloat);
742 auto newMaxVal = std::min(opMaxFloat, clampOpMaxFloat);
743 newMinValAttr = rewriter.
getFloatAttr(inputEType, newMinVal);
744 newMaxValAttr = rewriter.
getFloatAttr(inputEType, newMaxVal);
746 assert(mlir::isa<IntegerType>(inputEType));
747 auto intMaxValAttr = cast<mlir::IntegerAttr>(maxValAttr);
748 auto intMinValAttr = cast<mlir::IntegerAttr>(minValAttr);
749 auto clampOpIntMaxValAttr = cast<mlir::IntegerAttr>(clampOpMaxValAttr);
750 auto clampOpIntMinValAttr = cast<mlir::IntegerAttr>(clampOpMinValAttr);
752 if (inputEType.isUnsignedInteger()) {
754 const auto opMinInt = intMinValAttr.getUInt();
755 const auto opMaxInt = intMaxValAttr.getUInt();
756 const auto clampOpMinInt = clampOpIntMinValAttr.getUInt();
757 const auto clampOpMaxInt = clampOpIntMaxValAttr.getUInt();
761 if (!opRangeIntRange.
intersects(clampRangeIntRange))
765 auto newMinVal = std::max(opMinInt, clampOpMinInt);
766 auto newMaxVal = std::min(opMaxInt, clampOpMaxInt);
771 const auto opMinInt = intMinValAttr.getInt();
772 const auto opMaxInt = intMaxValAttr.getInt();
773 const auto clampOpMinInt = clampOpIntMinValAttr.getInt();
774 const auto clampOpMaxInt = clampOpIntMaxValAttr.getInt();
778 if (!opRangeIntRange.
intersects(clampRangeIntRange))
782 auto newMinVal = std::max(opMinInt, clampOpMinInt);
783 auto newMaxVal = std::min(opMaxInt, clampOpMaxInt);
789 auto newMode = (opNanMode != clampNanMode)
790 ? tosa::NanPropagationMode::IGNORE
794 NanPropagationModeAttr::get(rewriter.
getContext(), newMode);
797 op, op.getType(), clampOp.getInput(), newMinValAttr, newMaxValAttr,
803void ClampOp::getCanonicalizationPatterns(RewritePatternSet &results,
804 MLIRContext *context) {
805 results.
add<ClampIsNoOp>(context);
806 results.
add<ClampClampOptimization>(context);
814 Value sliceInput = sliceOp.getInput1();
818 sliceOp,
"slice input must be concat operation");
821 auto concatType = dyn_cast<RankedTensorType>(concatOp.getType());
822 if (!concatType || !concatType.hasStaticShape())
824 sliceOp,
"slice input must be a static ranked tensor");
825 int32_t axis = concatOp.getAxis();
832 sliceOp,
"start of slice must be a static ranked shape");
836 sliceOp,
"size of slice must be a static ranked shape");
846 std::optional<Value> replaceWithSlice;
847 for (
auto input : inputs) {
848 auto inputType = dyn_cast<RankedTensorType>(input.
getType());
849 if (!inputType || !inputType.hasStaticShape())
851 sliceOp,
"concat input must be a static ranked tensor");
853 if (sliceStarts[axis] >= 0 && (sliceStarts[axis] + sliceSizes[axis]) <=
854 inputType.getDimSize(axis)) {
860 tosa::SliceOp::create(rewriter, sliceOp.getLoc(), sliceOp.
getType(),
861 input, start_op, size_op)
865 sliceStarts[axis] -= inputType.getDimSize(axis);
868 if (!replaceWithSlice)
870 sliceOp,
"corresponding concat input not found for slice");
872 rewriter.
replaceOp(sliceOp, replaceWithSlice.value());
882 Value sliceInput = sliceOp.getInput1();
888 "slice input must be a pad operation");
891 if (!padOp->hasOneUse())
893 "pad shall have a single consumer");
896 auto inputTy = dyn_cast<RankedTensorType>(padOp.getInput1().getType());
897 auto padTy = dyn_cast<RankedTensorType>(padOp.getType());
898 if (!inputTy || !padTy || !inputTy.hasRank())
900 "slice input must be a ranked tensor");
907 "`padding` input specified on the tosa::PadOp must be constant.");
910 llvm::to_vector(paddingElems.getValues<
int64_t>());
916 sliceOp,
"start of slice must be a static ranked shape");
923 sliceOp,
"size of slice must be a static ranked shape");
928 const int64_t rank = inputTy.getRank();
929 if (llvm::any_of(llvm::seq<int64_t>(0, rank), [&](
int64_t i) {
930 const bool isDimDynamic = inputTy.isDynamicDim(i);
931 const bool isDimSliced =
934 return isDimDynamic && isDimSliced;
937 sliceOp,
"axis that are sliced shall be statically known.");
944 bool updated =
false;
946 for (
int64_t i = 0; i < rank; ++i) {
947 const int64_t padLo = padPaddings[i * 2];
948 const int64_t padHi = padPaddings[i * 2 + 1];
949 const int64_t sliceStart = sliceStarts[i];
950 const int64_t sliceSize = sliceSizes[i];
951 const int64_t sliceEnd = sliceStart + sliceSize;
954 if (inputTy.isDynamicDim(i)) {
955 newPadPaddings[i * 2] = padLo;
956 newPadPaddings[i * 2 + 1] = padHi;
957 newSliceStarts[i] = sliceStart;
962 const int64_t dimSize = inputTy.getShape()[i];
963 const int64_t dimTotal = padLo + dimSize + padHi;
966 if (sliceStart < 0 || sliceEnd > dimTotal)
970 const int64_t newSliceStart = std::max<int64_t>(sliceStart - padLo, 0);
971 newSliceStarts[i] = newSliceStart;
972 updated |= newSliceStart != sliceStart;
975 const int64_t newPadLo = std::max<int64_t>(padLo - sliceStart, 0);
977 std::max<int64_t>(sliceEnd - (padLo + dimSize), 0);
978 newPadPaddings[i * 2] = newPadLo;
979 newPadPaddings[i * 2 + 1] = newPadHi;
980 updated |= (newPadLo != padLo) || (newPadHi != padHi);
984 newPadPaddings[i * 2] + dimSize + newPadPaddings[i * 2 + 1];
990 sliceOp,
"terminate condition; nothing to rewrite");
996 RankedTensorType::get(newPadShape, inputTy.getElementType());
997 auto newPadOp = tosa::PadOp::create(rewriter, padOp.getLoc(), newPadTy,
998 padOp.getInput1(), newPaddingsOp,
999 padOp.getPadConst());
1005 newPadOp.getResult(), newStartOp,
1020 ShapedType resultType = cast<ShapedType>(sliceOp.getType());
1021 if (!resultType.hasRank())
1024 ElementsAttr sizeElems;
1027 sliceOp,
"size of slice must be a static ranked shape");
1031 llvm::to_vector(sizeElems.getValues<
int64_t>());
1033 bool replaceSliceSize{
false};
1037 for (
const auto &[
index, size] : llvm::enumerate(sliceSizes)) {
1039 sliceSizes[
index] = resultType.getDimSize(
index);
1040 replaceSliceSize =
true;
1044 if (!replaceSliceSize) {
1046 sliceOp,
"no dimension of size of slice is dynamic that resolves "
1047 "to static output shape");
1052 tosa::SliceOp::create(rewriter, sliceOp.getLoc(), sliceOp.
getType(),
1053 sliceOp.getInput1(), sliceOp.getStart(), size_op);
1055 rewriter.
replaceOp(sliceOp, newSliceOp.getResult());
1060void SliceOp::getCanonicalizationPatterns(RewritePatternSet &results,
1061 MLIRContext *context) {
1062 results.
add<ConcatSliceOptimization, PadSliceOptimization,
1063 SliceDynamicSizeCanonicalization>(context);
1071 const Value castInput = castOp.getInput();
1075 "input must be cast operation");
1077 const Value innerCastInput = innerCastOp.getInput();
1079 const ShapedType innerInputType =
1080 llvm::cast<ShapedType>(innerCastInput.
getType());
1081 const ShapedType innerOutputType =
1082 llvm::cast<ShapedType>(innerCastOp.getType());
1083 const ShapedType outerOutputType = llvm::cast<ShapedType>(castOp.getType());
1085 const Type innerInputElemType = innerInputType.getElementType();
1086 const Type innerOutputElemType = innerOutputType.getElementType();
1087 const Type outerOutputElemType = outerOutputType.getElementType();
1090 outerOutputElemType};
1092 if (llvm::any_of(types, [](
const Type type) {
1096 llvm::isa<Float8E4M3FNType, Float8E5M2Type, BFloat16Type,
1097 Float16Type, Float32Type>(type));
1100 castOp,
"only integer and f32, f16, bf16, f8E4M3FN, f8E5M2 types are "
1103 if (llvm::isa<Float8E5M2Type>(innerInputElemType) &&
1104 llvm::isa<Float8E4M3FNType>(outerOutputElemType)) {
1106 castOp,
"avoid introducing f8E5M2 -> f8E4M3FN casts which are not "
1110 if (llvm::isa<Float8E4M3FNType>(innerInputElemType) &&
1111 llvm::isa<Float8E5M2Type>(outerOutputElemType)) {
1113 castOp,
"avoid introducing f8E4M3FN -> f8E5M2 casts which are not "
1117 if (llvm::isa<Float8E5M2Type, Float8E4M3FNType>(innerInputElemType) &&
1120 castOp,
"avoid introducing fp8 -> integer casts which are not "
1125 llvm::isa<Float8E5M2Type, Float8E4M3FNType>(outerOutputElemType)) {
1127 castOp,
"avoid introducing integer -> fp8 casts which are not "
1131 if (llvm::isa<Float16Type>(innerInputElemType) &&
1132 llvm::isa<BFloat16Type>(outerOutputElemType)) {
1134 castOp,
"avoid introducing fp16 -> bf16 casts which are not "
1138 if (llvm::isa<BFloat16Type>(innerInputElemType) &&
1139 llvm::isa<Float16Type>(outerOutputElemType)) {
1141 castOp,
"avoid introducing bf16 -> fp16 casts which are not "
1145 const auto isIntegerOneOfWidth = [](
Type type,
size_t bitwidth1,
1150 if (isIntegerOneOfWidth(innerInputElemType, 8, 16) &&
1153 castOp,
"avoid introducing i8/i16 -> i64 casts which are not "
1157 if (isIntegerOneOfWidth(innerInputElemType, 1, 64) &&
1160 castOp,
"avoid introducing bool/i64 to float casts which are not "
1161 "supported in all versions of TOSA");
1165 isIntegerOneOfWidth(outerOutputElemType, 1, 64)) {
1167 castOp,
"avoid introducing float to bool/i64 casts which are not "
1168 "supported in all versions of TOSA");
1174 "inner cast operation is narrowing");
1178 if (!innerCastOp.getInputUnsigned() && castOp.getInputUnsigned()) {
1180 castOp,
"avoid rewriting cast(input_unsigned=false) -> "
1181 "cast(input_unsigned=true)");
1186 innerCastOp.getInputUnsigned());
1192 return semantics.nonFiniteBehavior !=
1193 llvm::fltNonfiniteBehavior::FiniteOnly;
1197 return semantics.nonFiniteBehavior == llvm::fltNonfiniteBehavior::IEEE754;
1201 const ShapedType outType)
const {
1203 if (inType.getElementType().isInteger() &&
1204 outType.getElementType().isInteger()) {
1206 const auto inTypeSignedness =
1207 cast<IntegerType>(inType.getElementType()).getSignedness();
1208 const auto outTypeSignedness =
1209 cast<IntegerType>(outType.getElementType()).getSignedness();
1211 return (inTypeSignedness != outTypeSignedness ||
1212 inType.getElementTypeBitWidth() >
1213 outType.getElementTypeBitWidth());
1216 if (inType.getElementType().isFloat() &&
1217 outType.getElementType().isFloat()) {
1219 FloatType inElemTy = cast<FloatType>(inType.getElementType());
1220 FloatType outElemTy = cast<FloatType>(outType.getElementType());
1221 llvm::fltSemantics inTypeSemantics = inElemTy.getFloatSemantics();
1222 llvm::fltSemantics outTypeSemantics = outElemTy.getFloatSemantics();
1228 [[maybe_unused]]
const auto isSupported = [](
Type elemType) {
1229 return llvm::isa<Float8E4M3FNType, Float8E5M2Type, BFloat16Type,
1230 Float16Type, Float32Type>(elemType);
1233 assert(isSupported(inElemTy) &&
1234 "unsupported input element type in isNarrowingCast");
1235 assert(isSupported(outElemTy) &&
1236 "unsupported output element type in isNarrowingCast");
1239 inTypeSemantics.maxExponent > outTypeSemantics.maxExponent ||
1240 inTypeSemantics.minExponent < outTypeSemantics.minExponent ||
1241 inTypeSemantics.precision > outTypeSemantics.precision ||
1258 const Value outerInput = castOp.getInput();
1259 auto innerCastOp = outerInput.
getDefiningOp<tosa::CastOp>();
1262 "input must be a cast operation");
1264 const Value innerInput = innerCastOp.getInput();
1265 const auto innerInputTy = llvm::cast<ShapedType>(innerInput.
getType());
1266 const auto innerOutputTy = llvm::cast<ShapedType>(innerCastOp.getType());
1267 const auto outerOutputTy = llvm::cast<ShapedType>(castOp.getType());
1269 if (!llvm::isa<tosa::BlockScaledType>(innerInputTy.getElementType()))
1271 castOp,
"inner cast input must have block scaled element type");
1273 if (innerInputTy != outerOutputTy)
1275 castOp,
"inner input type must match outer output type");
1277 const Type innerOutputElemType = innerOutputTy.getElementType();
1278 const bool isLosslessCast =
1279 isa<Float32Type, BFloat16Type>(innerOutputElemType);
1280 if (!isLosslessCast)
1282 castOp,
"avoid cancelling casts that should be lossy");
1290void CastOp::getCanonicalizationPatterns(RewritePatternSet &results,
1291 MLIRContext *context) {
1292 results.
add<NonNarrowingCastsOptimization,
1293 CancellingBlockScaledCastsOptimization>(context);
1302 const Value castToBlockScaledInput = castToBlockScaledOp.getInputData();
1303 auto castFromBlockScaledOp =
1304 castToBlockScaledInput.
getDefiningOp<tosa::CastFromBlockScaledOp>();
1305 if (!castFromBlockScaledOp)
1307 castToBlockScaledOp,
1308 "input must be cast_from_block_scaled operation");
1310 const Value innerData = castFromBlockScaledOp.getInputData();
1311 const Value innerScale = castFromBlockScaledOp.getInputScale();
1312 const auto innerDataTy = llvm::cast<ShapedType>(innerData.
getType());
1313 const auto innerScaleTy = llvm::cast<ShapedType>(innerScale.
getType());
1315 const Value outerData = castToBlockScaledOp.getOutputData();
1316 const Value outerScale = castToBlockScaledOp.getOutputScale();
1317 const auto outerDataTy = llvm::cast<ShapedType>(outerData.
getType());
1318 const auto outerScaleTy = llvm::cast<ShapedType>(outerScale.
getType());
1320 if (innerDataTy != outerDataTy || innerScaleTy != outerScaleTy) {
1322 castToBlockScaledOp,
1323 "inputs types to cast_from_block_scaled operation must match output "
1324 "types to cast_to_block_scaled");
1327 if (castFromBlockScaledOp.getBlockSize() !=
1328 castToBlockScaledOp.getBlockSize()) {
1330 castToBlockScaledOp,
"block sizes for cast_from_block_scaled and "
1331 "cast_to_block_scaled must match");
1334 rewriter.
replaceOp(castToBlockScaledOp, {innerData, innerScale});
1340void CastToBlockScaledOp::getCanonicalizationPatterns(
1341 RewritePatternSet &results, MLIRContext *context) {
1342 results.
add<CancellingCastToFromBlockScaledOptimization>(context);
1350 const FailureOr<int32_t> rowCount =
1352 if (failed(rowCount) || rowCount.value() != 1)
1356 op, op.getOutput().
getType(), op.getValues(), op.getIndices());
1361void RowGatherOp::getCanonicalizationPatterns(RewritePatternSet &results,
1362 MLIRContext *context) {
1363 results.
add<RowGatherToGather>(context);
1370template <
typename Folder>
1371static DenseElementsAttr
1373 bool foldDenseValues =
false) {
1377 if (!returnTy.hasRank() || !returnTy.hasStaticShape())
1380 const auto lETy = llvm::cast<ShapedType>(lhs.getType()).
getElementType();
1381 const auto rETy = llvm::cast<ShapedType>(rhs.getType()).getElementType();
1385 if (lhs.isSplat() && rhs.isSplat()) {
1386 if (isa<FloatType>(lETy)) {
1387 const APFloat l = lhs.getSplatValue<APFloat>();
1388 const APFloat r = rhs.getSplatValue<APFloat>();
1389 const auto maybeResult = Folder::fold(l, r);
1390 if (failed(maybeResult))
1395 if (
const auto lIntTy = llvm::dyn_cast<IntegerType>(lETy)) {
1396 const APInt l = lhs.getSplatValue<APInt>();
1397 const APInt r = rhs.getSplatValue<APInt>();
1398 const auto maybeResult = Folder::fold(l, r, lIntTy.isUnsigned());
1399 if (failed(maybeResult))
1405 if (foldDenseValues) {
1406 assert(lETy.isIntOrIndex() &&
1407 "Only integer types are currently supported.");
1410 llvm::zip(lhs.getValues<APInt>(), rhs.getValues<APInt>())) {
1411 const auto maybeResult = Folder::fold(l, r,
false);
1412 if (failed(maybeResult))
1414 resultValues.push_back(maybeResult.value());
1422template <
typename Folder>
1424 bool foldDenseValues =
false) {
1428 if (!returnTy.hasRank() || !returnTy.hasStaticShape())
1434 if (
const auto vIntTy = llvm::dyn_cast<IntegerType>(vETy)) {
1436 const auto maybeResult = Folder::fold(v, vIntTy.isUnsigned());
1437 if (failed(maybeResult))
1443 if (foldDenseValues) {
1447 for (
auto const &v : val.
getValues<APInt>()) {
1448 const auto maybeResult = Folder::fold(v,
false);
1449 if (failed(maybeResult))
1451 resultValues.push_back(maybeResult.value());
1466 assert(dense.isSplat());
1467 APInt a = dense.getSplatValue<APInt>();
1468 return a.getSExtValue();
1472 static FailureOr<APInt>
fold(
const APInt &lhs,
const APInt &rhs,
1473 const bool isUnsigned) {
1476 isUnsigned ? lhs.uadd_ov(rhs, overflow) : lhs.sadd_ov(rhs, overflow);
1482 static FailureOr<APFloat>
fold(
const APFloat &lhs,
const APFloat &rhs) {
1488 static FailureOr<APInt>
fold(
const APInt &lhs,
const APInt &rhs,
1489 const bool isUnsigned) {
1492 isUnsigned ? lhs.usub_ov(rhs, overflow) : lhs.ssub_ov(rhs, overflow);
1498 static FailureOr<APFloat>
fold(
const APFloat &lhs,
const APFloat &rhs) {
1504 static FailureOr<APInt>
fold(
const APInt &lhs,
const APInt &rhs,
1505 const bool isUnsigned) {
1507 const unsigned originalWidth = lhs.getBitWidth();
1510 if (lhs.getBitWidth() != rhs.getBitWidth()) {
1515 if (lhs == 0 || rhs == 0)
1516 return APInt::getZero(originalWidth);
1518 bool overflow =
false;
1520 isUnsigned ? lhs.umul_ov(rhs, overflow) : lhs.smul_ov(rhs, overflow);
1525 return result.trunc(originalWidth);
1528 static FailureOr<APFloat>
fold(
const APFloat &lhs,
const APFloat &rhs) {
1534 return a.isNegative() !=
b.isNegative();
1539 static FailureOr<APInt>
fold(
const APInt &lhs,
const APInt &rhs,
1541 if (lhs.getBitWidth() != rhs.getBitWidth())
1549 APInt::udivrem(lhs, rhs, q, r);
1550 if (!r.isZero() && Ceil) {
1557 bool overflow{
false};
1558 APInt
const q = lhs.sdiv_ov(rhs, overflow);
1561 APInt
const r = lhs.srem(rhs);
1563 if (Ceil && !r.isZero() && !
signsDiffer(lhs, rhs)) {
1571 static FailureOr<APFloat>
fold(
const APFloat &lhs,
const APFloat &rhs) {
1577 static FailureOr<APInt>
fold(
const APInt &lhs,
const APInt &rhs,
1579 if (lhs.getBitWidth() != rhs.getBitWidth())
1581 if (lhs.isNegative() || (!rhs.isStrictlyPositive()))
1585 return lhs.urem(rhs);
1588 return lhs.srem(rhs);
1591 static FailureOr<APFloat>
fold(
const APFloat &lhs,
const APFloat &rhs) {
1593 auto const r = t.mod(rhs);
1594 if (llvm::APFloatBase::opStatus::opOK == r) {
1602 static FailureOr<APInt>
fold(
const APInt &lhs,
const APInt &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) {
1610 return lhs >= rhs ? lhs : rhs;
1615 static FailureOr<APInt>
fold(
const APInt &lhs,
const APInt &rhs,
1617 if (lhs.getBitWidth() != rhs.getBitWidth())
1619 return lhs.getSExtValue() <= rhs.getSExtValue() ? lhs : rhs;
1622 static FailureOr<APFloat>
fold(
const APFloat &lhs,
const APFloat &rhs) {
1623 return lhs <= rhs ? lhs : rhs;
1628 static FailureOr<APInt>
fold(
const APInt &value,
bool isUnsigned) {
1629 auto const numBits = value.getBitWidth();
1631 auto const zextv = value.getZExtValue();
1632 if (zextv >= numBits)
1634 return APInt::getOneBitSet(numBits, zextv);
1636 auto const sextv = value.getSExtValue();
1637 if (sextv < 0 || sextv >= numBits || (value.isNegative()))
1639 return APInt::getOneBitSet(numBits, sextv);
1647 static FailureOr<APInt>
fold(
const APInt &lhs,
const APInt &rhs,
1649 assert(!isUnsigned &&
1650 "unsigned values are not supported for shape div folders");
1651 if (lhs.isNegative() || !rhs.isStrictlyPositive())
1656 static FailureOr<APFloat>
fold(
const APFloat &lhs,
const APFloat &rhs) {
1662 static FailureOr<APInt>
fold(
const APInt &value,
bool isUnsigned) {
1663 if (!value.isStrictlyPositive())
1665 return APInt(value.getBitWidth(), value.ceilLogBase2());
1670 static FailureOr<APInt>
fold(
const APInt &value,
bool isUnsigned) {
1671 if (!value.isStrictlyPositive())
1673 return APInt(value.getBitWidth(), value.logBase2());
1678 static FailureOr<APInt>
fold(
const APInt &lhs,
const APInt &rhs,
1679 const bool isUnsigned) {
1680 return isUnsigned ? APInt(1, lhs.ugt(rhs)) : APInt(1, lhs.sgt(rhs));
1683 static FailureOr<APInt>
fold(
const APFloat &lhs,
const APFloat &rhs) {
1684 return APInt(1, lhs > rhs);
1689 static FailureOr<APInt>
fold(
const APInt &lhs,
const APInt &rhs,
1690 const bool isUnsigned) {
1691 return isUnsigned ? APInt(1, lhs.uge(rhs)) : APInt(1, lhs.sge(rhs));
1694 static FailureOr<APInt>
fold(
const APFloat &lhs,
const APFloat &rhs) {
1695 return APInt(1, lhs >= rhs);
1700 static FailureOr<APInt>
fold(
const APInt &lhs,
const APInt &rhs,
1701 const bool isUnsigned) {
1702 return APInt(1, lhs == rhs);
1705 static FailureOr<APInt>
fold(
const APFloat &lhs,
const APFloat &rhs) {
1706 return APInt(1, lhs == rhs);
1711 if (llvm::isa<FloatType>(elemType))
1713 if (llvm::isa<IntegerType>(elemType))
1719 if (llvm::isa<FloatType>(elemType))
1720 return val && val.
isSplat() &&
1722 if (llvm::isa<IntegerType>(elemType)) {
1723 const int64_t shifted = 1LL << shift;
1724 return val && val.
isSplat() &&
1730OpFoldResult AddOp::fold(FoldAdaptor adaptor) {
1731 auto lhsTy = llvm::dyn_cast<RankedTensorType>(getInput1().
getType());
1732 auto rhsTy = llvm::dyn_cast<RankedTensorType>(getInput2().
getType());
1733 auto resultTy = llvm::dyn_cast<RankedTensorType>(
getType());
1734 if (!lhsTy || !rhsTy || !resultTy)
1738 if (!lhsTy.getElementType().isIntOrIndexOrFloat() ||
1739 !rhsTy.getElementType().isIntOrIndexOrFloat())
1742 auto resultETy = resultTy.getElementType();
1744 llvm::dyn_cast_if_present<DenseElementsAttr>(adaptor.getInput1());
1746 llvm::dyn_cast_if_present<DenseElementsAttr>(adaptor.getInput2());
1749 lhsTy.getShape(), rhsTy.getShape());
1750 if (isBroadcastable && lhsTy == resultTy &&
isSplatZero(resultETy, rhsAttr))
1752 if (isBroadcastable && rhsTy == resultTy &&
isSplatZero(resultETy, lhsAttr))
1755 if (!lhsAttr || !rhsAttr)
1761OpFoldResult ArgMaxOp::fold(FoldAdaptor adaptor) {
1762 auto inputTy = llvm::dyn_cast<RankedTensorType>(getInput().
getType());
1763 auto outputTy = llvm::dyn_cast<RankedTensorType>(
getType());
1764 if (!inputTy || !outputTy || !inputTy.hasStaticShape() ||
1765 !outputTy.hasStaticShape())
1769 if (inputTy.getDimSize(getAxis()) == 1 && outputElementTy.
isInteger()) {
1770 const auto outputElemIntTy = cast<IntegerType>(outputElementTy);
1771 const APInt zero = APInt::getZero(outputElemIntTy.getWidth());
1778OpFoldResult ArgMinOp::fold(FoldAdaptor adaptor) {
1779 auto inputTy = llvm::dyn_cast<RankedTensorType>(getInput().
getType());
1780 auto outputTy = llvm::dyn_cast<RankedTensorType>(
getType());
1781 if (!inputTy || !outputTy || !inputTy.hasStaticShape() ||
1782 !outputTy.hasStaticShape())
1786 if (inputTy.getDimSize(getAxis()) == 1 && outputElementTy.
isInteger()) {
1787 const auto outputElemIntTy = cast<IntegerType>(outputElementTy);
1788 const APInt zero = APInt::getZero(outputElemIntTy.getWidth());
1795OpFoldResult IntDivOp::fold(FoldAdaptor adaptor) {
1796 auto lhsTy = llvm::dyn_cast<RankedTensorType>(getInput1().
getType());
1797 auto rhsTy = llvm::dyn_cast<RankedTensorType>(getInput2().
getType());
1798 auto resultTy = llvm::dyn_cast<RankedTensorType>(
getType());
1799 if (!lhsTy || !rhsTy || !resultTy)
1801 if (lhsTy.getElementType() != rhsTy.getElementType())
1806 auto resultETy = resultTy.getElementType();
1808 llvm::dyn_cast_if_present<DenseElementsAttr>(adaptor.getInput1());
1810 llvm::dyn_cast_if_present<DenseElementsAttr>(adaptor.getInput2());
1811 if (lhsAttr && lhsAttr.isSplat() && rhsAttr && rhsAttr.isSplat()) {
1812 if (llvm::isa<IntegerType>(resultETy) && resultTy.hasStaticShape() &&
1813 lhsAttr.getSplatValue<APInt>().isZero() &&
1814 !rhsAttr.getSplatValue<APInt>().isZero()) {
1815 return lhsAttr.resizeSplat(resultTy);
1819 if (rhsAttr && rhsAttr.isSplat()) {
1821 lhsTy.getShape(), rhsTy.getShape());
1822 if (isBroadcastable && lhsTy == resultTy &&
1823 llvm::isa<IntegerType>(resultETy) &&
1824 rhsAttr.getSplatValue<APInt>().isOne())
1828 if (rhsAttr && lhsAttr && rhsAttr.isSplat() && lhsAttr.isSplat() &&
1829 llvm::isa<IntegerType>(resultETy) && resultTy.hasStaticShape()) {
1830 APInt l = lhsAttr.getSplatValue<APInt>();
1831 APInt r = rhsAttr.getSplatValue<APInt>();
1833 auto intTy = dyn_cast<mlir::IntegerType>(resultETy);
1835 DivFoldAdaptor<
false>::fold(l, r, intTy.isUnsigned());
1848std::optional<APInt> mulInt(APInt
lhs, APInt
rhs, int32_t shift,
1849 unsigned bitwidth) {
1850 bool overflow =
false;
1851 APInt
result =
lhs.sext(64).smul_ov(
rhs.sext(64), overflow);
1854 return std::nullopt;
1857 auto round = APInt(64, 1) << (shift - 1);
1859 result.ashrInPlace(shift);
1862 if (!(
result.getSExtValue() >= INT32_MIN &&
1863 result.getSExtValue() <= INT32_MAX)) {
1865 return std::nullopt;
1869 return result.trunc(bitwidth);
1872DenseElementsAttr mulBinaryFolder(DenseElementsAttr
lhs, DenseElementsAttr
rhs,
1873 RankedTensorType ty, int32_t shift) {
1875 if (!ty.hasStaticShape())
1878 if (llvm::isa<IntegerType>(ty.getElementType())) {
1879 APInt l =
lhs.getSplatValue<APInt>();
1880 APInt r =
rhs.getSplatValue<APInt>();
1886 auto bitwidth = ty.getElementType().getIntOrFloatBitWidth();
1887 const std::optional<APInt>
result = mulInt(l, r, shift, bitwidth);
1893 if (llvm::isa<FloatType>(ty.getElementType())) {
1894 APFloat l =
lhs.getSplatValue<APFloat>();
1895 APFloat r =
rhs.getSplatValue<APFloat>();
1905OpFoldResult MulOp::fold(FoldAdaptor adaptor) {
1906 auto lhs = getInput1();
1907 auto rhs = getInput2();
1908 auto lhsTy = llvm::dyn_cast<RankedTensorType>(
lhs.getType());
1909 auto rhsTy = llvm::dyn_cast<RankedTensorType>(
rhs.getType());
1910 auto resultTy = llvm::dyn_cast<RankedTensorType>(
getType());
1911 if (!lhsTy || !rhsTy || !resultTy)
1914 auto resultETy = resultTy.getElementType();
1916 llvm::dyn_cast_if_present<DenseElementsAttr>(adaptor.getInput1());
1918 llvm::dyn_cast_if_present<DenseElementsAttr>(adaptor.getInput2());
1923 if (resultETy.isInteger(32)) {
1924 ElementsAttr shiftElem;
1925 if (getShift().getImpl()) {
1929 shift = shiftElem.getValues<IntegerAttr>()[0].getInt();
1930 if (shift < 0 || shift > 63)
1935 if (rhsTy == resultTy &&
isSplatZero(resultETy, lhsAttr) &&
1936 resultTy.hasStaticShape())
1938 return lhsAttr.resizeSplat(resultTy);
1939 if (lhsTy == resultTy &&
isSplatZero(resultETy, rhsAttr) &&
1940 resultTy.hasStaticShape())
1941 return rhsAttr.resizeSplat(resultTy);
1944 lhsTy.getShape(), rhsTy.getShape());
1945 if (isBroadcastable && rhsTy == resultTy &&
1948 if (isBroadcastable && lhsTy == resultTy &&
1952 return mulBinaryFolder(lhsAttr, rhsAttr, resultTy, shift);
1955OpFoldResult SubOp::fold(FoldAdaptor adaptor) {
1956 auto lhsTy = llvm::dyn_cast<RankedTensorType>(getInput1().
getType());
1957 auto rhsTy = llvm::dyn_cast<RankedTensorType>(getInput2().
getType());
1958 auto resultTy = llvm::dyn_cast<RankedTensorType>(
getType());
1959 if (!lhsTy || !rhsTy || !resultTy)
1963 if (!lhsTy.getElementType().isIntOrIndexOrFloat() ||
1964 !rhsTy.getElementType().isIntOrIndexOrFloat())
1967 auto resultETy = resultTy.getElementType();
1969 llvm::dyn_cast_if_present<DenseElementsAttr>(adaptor.getInput1());
1971 llvm::dyn_cast_if_present<DenseElementsAttr>(adaptor.getInput2());
1974 lhsTy.getShape(), rhsTy.getShape());
1975 if (isBroadcastable && lhsTy == resultTy &&
isSplatZero(resultETy, rhsAttr))
1978 if (!lhsAttr || !rhsAttr)
1984OpFoldResult GreaterOp::fold(FoldAdaptor adaptor) {
1985 auto resultTy = llvm::cast<ShapedType>(
getType());
1987 llvm::dyn_cast_if_present<DenseElementsAttr>(adaptor.getInput1());
1989 llvm::dyn_cast_if_present<DenseElementsAttr>(adaptor.getInput2());
1991 if (!lhsAttr || !rhsAttr)
1997OpFoldResult GreaterEqualOp::fold(FoldAdaptor adaptor) {
1998 auto resultTy = llvm::cast<ShapedType>(
getType());
2000 llvm::dyn_cast_if_present<DenseElementsAttr>(adaptor.getInput1());
2002 llvm::dyn_cast_if_present<DenseElementsAttr>(adaptor.getInput2());
2004 if (!lhsAttr || !rhsAttr)
2010OpFoldResult EqualOp::fold(FoldAdaptor adaptor) {
2011 auto resultTy = llvm::cast<ShapedType>(
getType());
2013 llvm::dyn_cast_if_present<DenseElementsAttr>(adaptor.getInput1());
2015 llvm::dyn_cast_if_present<DenseElementsAttr>(adaptor.getInput2());
2016 Value
lhs = getInput1();
2017 Value
rhs = getInput2();
2018 auto lhsTy = llvm::cast<ShapedType>(
lhs.getType());
2022 if (llvm::isa<IntegerType>(lhsTy.getElementType()) && resultTy.hasRank() &&
2023 resultTy.hasStaticShape() &&
lhs ==
rhs) {
2027 if (!lhsAttr || !rhsAttr)
2033OpFoldResult CastOp::fold(FoldAdaptor adaptor) {
2037 auto operand = llvm::dyn_cast_if_present<ElementsAttr>(adaptor.getInput());
2041 auto inTy = llvm::cast<ShapedType>(getInput().
getType());
2042 auto outTy = llvm::cast<ShapedType>(
getType());
2043 if (!outTy.hasRank() || !outTy.hasStaticShape())
2045 auto inETy = inTy.getElementType();
2046 auto outETy = outTy.getElementType();
2048 if (operand.isSplat()) {
2049 if (llvm::isa<FloatType>(inETy) && llvm::isa<FloatType>(outETy)) {
2051 auto splatVal = operand.getSplatValue<APFloat>();
2052 auto &semantics = llvm::cast<FloatType>(outETy).getFloatSemantics();
2053 splatVal.convert(semantics, llvm::RoundingMode::NearestTiesToEven,
2058 if (llvm::isa<IntegerType>(inETy) && llvm::isa<FloatType>(outETy)) {
2059 const bool unsign = llvm::cast<IntegerType>(inETy).isUnsignedInteger() ||
2060 adaptor.getInputUnsigned();
2062 splatVal.convertFromAPInt(operand.getSplatValue<APInt>(), !unsign,
2063 llvm::RoundingMode::NearestTiesToEven);
2067 if (llvm::isa<FloatType>(inETy) && llvm::isa<IntegerType>(outETy)) {
2068 auto floatVal = operand.getSplatValue<APFloat>();
2069 if (outETy.isInteger(1))
2071 auto unsign = llvm::cast<IntegerType>(outETy).isUnsignedInteger();
2072 auto intVal = APSInt(
2073 llvm::cast<IntegerType>(outETy).getIntOrFloatBitWidth(), unsign);
2075 floatVal.convertToInteger(intVal, llvm::RoundingMode::NearestTiesToEven,
2080 if (llvm::isa<IntegerType>(inETy) && llvm::isa<IntegerType>(outETy)) {
2081 const auto inIntType = llvm::cast<IntegerType>(inETy);
2082 const bool unsignIn =
2083 inIntType.isUnsignedInteger() || adaptor.getInputUnsigned();
2085 inETy.getIntOrFloatBitWidth() > outETy.getIntOrFloatBitWidth();
2086 auto intVal = operand.getSplatValue<APInt>();
2087 auto bitwidth = outETy.getIntOrFloatBitWidth();
2090 if (outETy.isInteger(1)) {
2091 intVal = APInt(bitwidth, intVal.isZero() ? 0 : 1);
2093 intVal = intVal.trunc(bitwidth);
2094 }
else if (unsignIn || inIntType.isInteger(1)) {
2095 intVal = intVal.zext(bitwidth);
2097 intVal = intVal.sext(bitwidth);
2107OpFoldResult ConstOp::fold(FoldAdaptor adaptor) {
return getValuesAttr(); }
2109OpFoldResult ConstShapeOp::fold(FoldAdaptor adaptor) {
return getValuesAttr(); }
2111#define REDUCE_FOLDER(OP) \
2112 OpFoldResult OP::fold(FoldAdaptor adaptor) { \
2113 ShapedType inputTy = llvm::cast<ShapedType>(getInput().getType()); \
2114 if (!inputTy.hasRank()) \
2116 if (inputTy != getType()) \
2118 if (inputTy.getRank() == 0 || inputTy.getDimSize(getAxis()) == 1) \
2119 return getInput(); \
2132 auto inputTy = llvm::dyn_cast<RankedTensorType>(getInput1().
getType());
2133 auto outputTy = llvm::dyn_cast<RankedTensorType>(
getType());
2135 if (!inputTy || !outputTy)
2141 if (inputTy == outputTy && inputTy.getNumDynamicDims() < 2)
2145 if (
auto reshapeOp = llvm::dyn_cast_if_present<tosa::ReshapeOp>(
2146 getInput1().getDefiningOp())) {
2147 getInput1Mutable().assign(reshapeOp.getInput1());
2152 if (!inputTy.getElementType().isIntOrIndexOrFloat())
2156 if (!outputTy.hasStaticShape())
2160 if (
auto operand = llvm::dyn_cast_if_present<DenseResourceElementsAttr>(
2161 adaptor.getInput1()))
2162 return DenseResourceElementsAttr::get(outputTy, operand.getRawHandle());
2166 llvm::dyn_cast_if_present<DenseElementsAttr>(adaptor.getInput1())) {
2168 if (operand.isSplat())
2173 if (!getInput1().hasOneUse())
2180 return operand.reshape(
2181 llvm::cast<ShapedType>(operand.getType()).clone(shapeVec));
2187OpFoldResult PadOp::fold(FoldAdaptor adaptor) {
2189 if (adaptor.getPadding() && getInput1().
getType() ==
getType()) {
2190 auto densePad = llvm::dyn_cast<DenseElementsAttr>(adaptor.getPadding());
2191 if (densePad && densePad.isSplat() &&
2192 densePad.getSplatValue<APInt>().isZero()) {
2202OpFoldResult ResizeOp::fold(FoldAdaptor adaptor) {
2204 llvm::dyn_cast_if_present<DenseElementsAttr>(adaptor.getScale());
2206 llvm::dyn_cast_if_present<DenseElementsAttr>(adaptor.getOffset());
2208 llvm::dyn_cast_if_present<DenseElementsAttr>(adaptor.getBorder());
2209 if (!scaleAttr || !offsetAttr || !borderAttr) {
2216 if (scale.size() != 4 || offset.size() != 2 || border.size() != 2) {
2221 if (scale[0] != scale[1] || scale[2] != scale[3]) {
2226 if (offset[0] != 0 || offset[1] != 0) {
2231 if (border[0] != 0 || border[1] != 0) {
2235 return foldToInputIfTypeMatches(
getType(), getInput());
2238OpFoldResult ReverseOp::fold(FoldAdaptor adaptor) {
2239 auto operand = getInput1();
2240 auto operandTy = llvm::cast<ShapedType>(operand.getType());
2241 auto axis = getAxis();
2244 const bool isSplatInput =
2245 !isa<BlockScaledType>(operandTy.getElementType()) &&
2246 llvm::isa_and_nonnull<SplatElementsAttr>(adaptor.getInput1());
2247 if (!operandTy.hasRank() ||
2248 (!isSplatInput && operandTy.getDimSize(axis) != 1))
2250 return foldToInputIfTypeMatches(
getType(), operand);
2253OpFoldResult SliceOp::fold(FoldAdaptor adaptor) {
2254 auto inputTy = llvm::dyn_cast<RankedTensorType>(getInput1().
getType());
2255 auto outputTy = llvm::dyn_cast<RankedTensorType>(
getType());
2257 if (!inputTy || !outputTy)
2260 if (inputTy == outputTy && inputTy.hasStaticShape())
2265 DenseElementsAttr startElems;
2271 llvm::all_of(startElems.
getValues<APInt>(),
2272 [](
const APInt &val) { return val.isZero(); });
2277 DenseElementsAttr sizeElems;
2281 auto inputShape = inputTy.getShape();
2282 auto sizeValues = sizeElems.
getValues<APInt>();
2284 bool sizeMatchesInput =
true;
2285 for (
const auto &[i, sizeVal] : llvm::enumerate(sizeValues)) {
2286 int64_t size = sizeVal.getSExtValue();
2288 if (inputTy.isDynamicDim(i)) {
2292 sizeMatchesInput =
false;
2299 sizeMatchesInput =
false;
2305 if (sizeMatchesInput)
2306 return foldToInputIfTypeMatches(
getType(), getInput1());
2310 if (!adaptor.getInput1())
2314 if (!inputTy.getElementType().isIntOrIndexOrFloat() ||
2315 !outputTy.getElementType().isIntOrIndexOrFloat())
2318 auto operand = llvm::cast<ElementsAttr>(adaptor.getInput1());
2319 if (operand.isSplat() && outputTy.hasStaticShape()) {
2323 if (inputTy.hasStaticShape() && outputTy.hasStaticShape() &&
2324 outputTy.getNumElements() == 1) {
2325 llvm::SmallVector<uint64_t>
indices =
2326 llvm::to_vector(startElems.
getValues<uint64_t>());
2327 if (
auto values = operand.tryGetValues<Attribute>())
2334OpFoldResult tosa::SelectOp::fold(FoldAdaptor adaptor) {
2335 const Value pred = getPred();
2336 const Value onTrue = getOnTrue();
2337 const Value onFalse = getOnFalse();
2339 const auto predTy = llvm::dyn_cast<RankedTensorType>(pred.
getType());
2340 const auto onTrueTy = llvm::dyn_cast<RankedTensorType>(onTrue.
getType());
2341 const auto onFalseTy = llvm::dyn_cast<RankedTensorType>(onFalse.
getType());
2342 if (!predTy || !onTrueTy || !onFalseTy)
2345 const Type resultTy =
getType();
2347 const ArrayRef<int64_t> predShape = predTy.getShape();
2348 const ArrayRef<int64_t> onTrueShape = onTrueTy.getShape();
2350 if (onTrue == onFalse && onTrueTy == resultTy &&
2355 llvm::dyn_cast_if_present<DenseIntElementsAttr>(adaptor.getInput1());
2358 if (!predicate.isSplat())
2361 const bool predicateValue = predicate.getSplatValue<APInt>().getBoolValue();
2363 SmallVector<SmallVector<int64_t>, 3> shapes;
2364 shapes.emplace_back(predShape);
2365 shapes.emplace_back(onTrueShape);
2366 shapes.emplace_back(onFalseTy.getShape());
2367 const bool isBroadcastable =
2370 if (predicateValue ==
true && onTrueTy == resultTy && isBroadcastable)
2372 if (predicateValue ==
false && onFalseTy == resultTy && isBroadcastable)
2378 const auto inputType =
2379 dyn_cast<RankedTensorType>(tileOp.getInput1().getType());
2380 const auto outputType = dyn_cast<RankedTensorType>(tileOp.getType());
2381 if (!inputType || !outputType)
2385 if (failed(tileOp.getConstantMultiples(multiples)))
2388 for (
const auto [
index, multiple] : llvm::enumerate(multiples)) {
2391 if (outputType.isDynamicDim(
index))
2393 if (inputType.getDimSize(
index) != 1)
2406 Value tileOutput = tileOp.getOutput();
2409 "tile output must have one use");
2412 const bool isBinaryElementwise =
2415 if (!isBinaryElementwise && !isa<tosa::MulOp>(user))
2417 tileOp,
"consumer must be binary broadcastable");
2421 tileOp,
"tile must only expand statically-known singleton dims");
2425 Value otherOperand = lhsOperand == tileOutput ? rhsOperand : lhsOperand;
2426 Value tileInput = tileOp.getInput1();
2428 const ShapedType newOtherType = cast<ShapedType>(otherOperand.
getType());
2429 const ShapedType newTileType = cast<ShapedType>(tileInput.
getType());
2432 newOtherType.getShape(), newTileType.getShape(), broadcastedShape);
2434 const ShapedType outputType = cast<ShapedType>(user->
getResultTypes()[0]);
2435 if (!llvm::equal(broadcastedShape, outputType.getShape()))
2437 tileOp,
"tile output must be broadcastable to consumer operands");
2441 mapper.
map(tileOutput, tileOp.getInput1());
2448void TileOp::getCanonicalizationPatterns(RewritePatternSet &results,
2449 MLIRContext *context) {
2450 results.
add<RemoveBroadcastTileFromBinaryElementwise>(context);
2453OpFoldResult TileOp::fold(FoldAdaptor adaptor) {
2455 if (
auto multiples = llvm::dyn_cast_if_present<DenseElementsAttr>(
2456 adaptor.getMultiples())) {
2457 if (multiples.isSplat() &&
2458 multiples.getSplatValue<APInt>().getSExtValue() == 1)
2460 if (
auto int_array_attr =
2461 llvm::dyn_cast<DenseIntElementsAttr>(multiples)) {
2462 if (llvm::all_of(int_array_attr.getValues<APInt>(),
2463 [](APInt v) { return v.getSExtValue() == 1; }))
2471OpFoldResult TransposeOp::fold(FoldAdaptor adaptor) {
2472 auto resultTy = llvm::cast<ShapedType>(
getType());
2476 llvm::dyn_cast_if_present<DenseElementsAttr>(adaptor.getInput1())) {
2477 if (input.isSplat() && resultTy.hasRank() && resultTy.hasStaticShape() &&
2478 input.
getType().getElementType() == resultTy.getElementType())
2479 return input.reshape(resultTy);
2483 const llvm::ArrayRef<int32_t> perms = getPerms();
2485 if (!llvm::equal(llvm::seq<int32_t>(0, perms.size()), perms))
2488 return foldToInputIfTypeMatches(
getType(), getInput1());
2491OpFoldResult tosa::NegateOp::fold(FoldAdaptor adaptor) {
2494 auto definingOp = getInput1().getDefiningOp<tosa::NegateOp>();
2500 if (FailureOr<int64_t> maybeIZp = getInput1ZeroPoint();
2501 failed(maybeIZp) || *maybeIZp != 0) {
2505 if (FailureOr<int64_t> maybeOZp = getOutputZeroPoint();
2506 failed(maybeOZp) || *maybeOZp != 0) {
2510 if (FailureOr<int64_t> maybeIZp = definingOp.getInput1ZeroPoint();
2511 failed(maybeIZp) || *maybeIZp != 0) {
2515 if (FailureOr<int64_t> maybeOZp = definingOp.getOutputZeroPoint();
2516 failed(maybeOZp) || *maybeOZp != 0) {
2521 return foldToInputIfTypeMatches(
getType(), definingOp.getInput1());
2524OpFoldResult tosa::AbsOp::fold(FoldAdaptor adaptor) {
2525 auto input = getInput1();
2528 return foldToInputIfTypeMatches(
getType(), input);
2533OpFoldResult tosa::ReciprocalOp::fold(FoldAdaptor adaptor) {
2534 auto input = adaptor.getInput1();
2536 auto inputAttr = llvm::dyn_cast_if_present<DenseElementsAttr>(input);
2538 if (!inputAttr || !inputAttr.isSplat())
2541 auto shapeType = llvm::cast<ShapedType>(
getType());
2542 if (!shapeType.hasRank() || !shapeType.hasStaticShape())
2544 if (
auto floatType = llvm::dyn_cast<FloatType>(inputAttr.getElementType())) {
2545 auto floatVal = inputAttr.getSplatValue<APFloat>();
2547 ReciprocalOp::calcOneElement(floatVal));
2553template <
typename Op,
typename OpFoldAdaptor>
2555 auto input1ConstShape =
2556 dyn_cast<tosa::ConstShapeOp>(op->getInput().getDefiningOp());
2557 if (!input1ConstShape)
2560 const auto input1Attr = cast<DenseElementsAttr>(input1ConstShape.getValues());
2566template <
typename Op,
typename OpFoldAdaptor>
2568 auto input1ConstShape =
2569 dyn_cast<tosa::ConstShapeOp>(op->getInput1().getDefiningOp());
2570 auto input2ConstShape =
2571 dyn_cast<tosa::ConstShapeOp>(op->getInput2().getDefiningOp());
2572 if (!input1ConstShape || !input2ConstShape)
2575 const auto input1Attr = cast<DenseElementsAttr>(input1ConstShape.getValues());
2576 const auto input2Attr = cast<DenseElementsAttr>(input2ConstShape.getValues());
2579 input1Attr.getType(),
2583OpFoldResult tosa::DimOp::fold(FoldAdaptor adaptor) {
2584 const auto inputTy = llvm::dyn_cast<ShapedType>(getInput1().
getType());
2585 if (!inputTy || !inputTy.hasRank())
2587 const int32_t axis = getAxis();
2588 const int64_t dimSize = inputTy.getDimSize(axis);
2589 if (ShapedType::isDynamic(dimSize))
2593 const auto resultAttrTy =
2594 RankedTensorType::get(1, builder.getIndexType());
2599 auto const inputs = op->getInput();
2605 concatDims.reserve( 64);
2606 for (
auto const &v : inputs) {
2607 auto vConstShape = dyn_cast<tosa::ConstShapeOp>(v.getDefiningOp());
2611 const auto vAttr = cast<DenseElementsAttr>(vConstShape.getValues());
2614 auto const vAttrVals = vAttr.getValues<APInt>();
2615 for (
auto const &v : vAttrVals) {
2616 concatDims.push_back(v);
2620 auto *ctx = op->getContext();
2621 assert(ctx !=
nullptr &&
"ctx is nullptr");
2622 auto const rankedTy = RankedTensorType::get(
2623 {
static_cast<int64_t>(concatDims.size())}, IndexType::get(ctx));
2629 auto const input1 = op->getInput();
2630 auto const input2 = op->getStart();
2631 auto const input3 = op->getSize();
2633 auto input1ConstShape = dyn_cast<tosa::ConstShapeOp>(input1.getDefiningOp());
2635 if (!input1ConstShape)
2638 auto const input1Attr = cast<DenseElementsAttr>(input1ConstShape.getValues());
2642 auto const input1Vals = input1Attr.getValues<APInt>();
2643 auto const totalInput1 = input1Vals.size();
2648 if (failed(start) || failed(size))
2651 auto const startV =
static_cast<int32_t
>(start.value());
2652 auto const sizeV =
static_cast<int32_t
>(size.value());
2654 if ((sizeV <= 0) || (startV < 0) ||
2655 (
static_cast<size_t>(startV + sizeV) > totalInput1))
2659 sliceOfInput.reserve(totalInput1);
2661 for (
auto i = startV; i < (startV + sizeV); i++) {
2662 sliceOfInput.push_back(input1Vals[i]);
2665 auto *ctx = op->getContext();
2666 assert(ctx !=
nullptr &&
"ctx is nullptr");
2668 auto const rankedTy = RankedTensorType::get(
2669 {
static_cast<int64_t>(sliceOfInput.size())}, IndexType::get(ctx));
2674OpFoldResult tosa::AddShapeOp::fold(FoldAdaptor adaptor) {
2678OpFoldResult tosa::SubShapeOp::fold(FoldAdaptor adaptor) {
2682OpFoldResult tosa::MulShapeOp::fold(FoldAdaptor adaptor) {
2686OpFoldResult tosa::DivCeilShapeOp::fold(FoldAdaptor adaptor) {
2687 return binaryFold<DivCeilShapeOp, ShapeDivFoldAdaptor<
true>>(
this);
2690OpFoldResult tosa::DivFloorShapeOp::fold(FoldAdaptor adaptor) {
2691 return binaryFold<DivFloorShapeOp, ShapeDivFoldAdaptor<
false>>(
this);
2694OpFoldResult tosa::ModShapeOp::fold(FoldAdaptor adaptor) {
2698OpFoldResult tosa::MaxShapeOp::fold(FoldAdaptor adaptor) {
2702OpFoldResult tosa::MinShapeOp::fold(FoldAdaptor adaptor) {
2706OpFoldResult tosa::Exp2ShapeOp::fold(FoldAdaptor adaptor) {
2710OpFoldResult tosa::Log2CeilShapeOp::fold(FoldAdaptor adaptor) {
2714OpFoldResult tosa::Log2FloorShapeOp::fold(FoldAdaptor adaptor) {
2718OpFoldResult tosa::ConcatShapeOp::fold(FoldAdaptor adaptor) {
2722OpFoldResult 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.
static const llvm::fltSemantics * getFloatSemantics(TruncfSrcElemTypes etype)
Float semantics the element type attributes of xevm.truncf and xevm.extf stand for.
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)
LogicalResult matchAndRewrite(tosa::AvgPool2dAdaptiveOp op, PatternRewriter &rewriter) const override
LogicalResult matchAndRewrite(tosa::AvgPool2dOp op, PatternRewriter &rewriter) const override
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
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
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)
LogicalResult matchAndRewrite(tosa::MaxPool2dAdaptiveOp op, PatternRewriter &rewriter) const override
LogicalResult matchAndRewrite(tosa::MaxPool2dOp op, PatternRewriter &rewriter) const override
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 is a wrapper around RewritePattern that allows for matching and rewriting against an...
OpRewritePattern Base
Type alias to allow derived classes to inherit constructors with using Base::Base;.
OpRewritePattern(MLIRContext *context, PatternBenefit benefit=1, ArrayRef< StringRef > generatedNames={})
int32_t MAX_TENSOR_LIST_SIZE