30#include "llvm/ADT/STLExtras.h"
31#include "llvm/ADT/Sequence.h"
32#include "llvm/ADT/SmallVectorExtras.h"
39template <
typename OpTy>
43 typename OpTy::Properties properties{};
44 OpTy::populateDefaultProperties(
47 return OpTy::create(builder, loc, resultTypes, operands, properties,
73template <
typename OpTy>
81 auto nanMode = op.getNanMode();
82 if (nanMode == NanPropagationMode::PROPAGATE)
86 Value lhsIsNaN = arith::CmpFOp::create(rewriter, op.getLoc(),
87 arith::CmpFPredicate::UNO, lhs, lhs);
88 Value rhsIsNaN = arith::CmpFOp::create(rewriter, op.getLoc(),
89 arith::CmpFPredicate::UNO, rhs, rhs);
91 arith::SelectOp::create(rewriter, op.getLoc(), lhsIsNaN, rhs,
result);
92 return arith::SelectOp::create(rewriter, op.getLoc(), rhsIsNaN, lhs,
98 ConversionPatternRewriter &rewriter) {
104 if (isa<tosa::AbsOp>(op) && isa<FloatType>(elementTy))
108 if (isa<tosa::AbsOp>(op) && isa<IntegerType>(elementTy)) {
109 auto zero = arith::ConstantOp::create(rewriter, loc,
110 rewriter.getZeroAttr(elementTy));
111 auto neg = arith::SubIOp::create(rewriter, loc, zero, args[0]);
112 return arith::MaxSIOp::create(rewriter, loc, args[0], neg);
116 if (isa<tosa::AddOp>(op) && isa<FloatType>(elementTy))
120 if (isa<tosa::AddOp>(op) && isa<IntegerType>(elementTy))
125 if (isa<tosa::SubOp>(op) && isa<FloatType>(elementTy))
129 if (isa<tosa::SubOp>(op) && isa<IntegerType>(elementTy))
134 if (isa<tosa::IntDivOp>(op) && isa<IntegerType>(elementTy))
139 if (isa<tosa::ReciprocalOp>(op) && isa<FloatType>(elementTy)) {
141 arith::ConstantOp::create(rewriter, loc, FloatAttr::get(elementTy, 1));
142 return arith::DivFOp::create(rewriter, loc, one, args[0]);
146 if (isa<tosa::MulOp>(op)) {
147 auto shiftVal = cast<tosa::MulOp>(op).getShift();
149 bool shiftIsConstant =
true;
152 shift = shiftElem.
getValues<IntegerAttr>()[0].getInt();
154 shiftIsConstant =
false;
156 if (isa<FloatType>(elementTy)) {
158 (
void)rewriter.notifyMatchFailure(op,
159 "Cannot have shift value for float");
162 return arith::MulFOp::create(rewriter, loc, args[0], args[1]);
165 if (isa<IntegerType>(elementTy)) {
169 if (shift > 0 || !shiftIsConstant) {
176 a = arith::ExtSIOp::create(rewriter, loc, rewriter.getI32Type(), a);
178 if (!
b.getType().isInteger(32))
179 b = arith::ExtSIOp::create(rewriter, loc, rewriter.getI32Type(),
b);
181 auto shiftAmount = shiftIsConstant ? shiftConst : args[2];
182 auto roundingAttr = RoundingModeAttr::get(rewriter.getContext(),
183 RoundingMode::SINGLE_ROUND);
185 tosa::ApplyScaleOp::create(rewriter, loc, rewriter.getI32Type(), a,
186 b, shiftAmount, roundingAttr);
192 int bWidth =
b.getType().getIntOrFloatBitWidth();
193 int cWidth = resultTypes[0].getIntOrFloatBitWidth();
196 a = arith::ExtSIOp::create(rewriter, loc, resultTypes[0], a);
198 b = arith::ExtSIOp::create(rewriter, loc, resultTypes[0],
b);
200 return arith::MulIOp::create(rewriter, loc, resultTypes, a,
b);
205 if (isa<tosa::NegateOp>(op)) {
206 auto negate = cast<tosa::NegateOp>(op);
209 FailureOr<int64_t> maybeInZp = negate.getInput1ZeroPoint();
210 FailureOr<int64_t> maybeOutZp = negate.getOutputZeroPoint();
211 bool hasInZp = !failed(maybeInZp);
212 bool hasOutZp = !failed(maybeOutZp);
218 if (isa<FloatType>(elementTy))
219 return arith::NegFOp::create(rewriter, loc, resultTypes, args[0]);
221 if (isa<IntegerType>(elementTy)) {
223 Type intermediateType;
226 int intermediateBitWidth = 64;
228 if (hasInZp && hasOutZp) {
233 const int64_t zpAdd = inZp + outZp;
234 const APInt maxValue =
235 APInt::getSignedMaxValue(inputBitWidth).sext(128) +
240 if (maxValue.sle(APInt::getSignedMaxValue(16).getSExtValue())) {
241 intermediateBitWidth = 16;
242 }
else if (maxValue.sle(APInt::getSignedMaxValue(32).getSExtValue())) {
243 intermediateBitWidth = 32;
246 intermediateType = rewriter.getIntegerType(intermediateBitWidth);
247 zpAddValue = arith::ConstantOp::create(
248 rewriter, loc, rewriter.getIntegerAttr(intermediateType, zpAdd));
250 intermediateType = rewriter.getIntegerType(intermediateBitWidth);
251 Value arg1 = args[1];
252 Value arg2 = args[2];
254 if (arg1.
getType() != intermediateType)
255 arg1 = arith::ExtSIOp::create(rewriter, loc, intermediateType, arg1);
256 if (arg2.
getType() != intermediateType)
257 arg2 = arith::ExtSIOp::create(rewriter, loc, intermediateType, arg2);
259 arith::AddIOp::create(rewriter, loc, intermediateType, arg1, arg2);
265 if (ext.
getType() != intermediateType)
266 ext = arith::ExtSIOp::create(rewriter, loc, intermediateType, ext);
267 auto sub = arith::SubIOp::create(rewriter, loc, zpAddValue, ext);
271 rewriter, loc, intermediateType,
272 APInt::getSignedMinValue(inputBitWidth).getSExtValue());
274 rewriter, loc, intermediateType,
275 APInt::getSignedMaxValue(inputBitWidth).getSExtValue());
279 if (
clamp.getType() == elementTy)
281 return arith::TruncIOp::create(rewriter, loc, elementTy,
clamp);
286 if (isa<tosa::BitwiseAndOp>(op) && isa<IntegerType>(elementTy))
287 return arith::AndIOp::create(rewriter, loc, resultTypes, args);
290 if (isa<tosa::BitwiseOrOp>(op) && isa<IntegerType>(elementTy))
291 return arith::OrIOp::create(rewriter, loc, resultTypes, args);
294 if (isa<tosa::BitwiseNotOp>(op) && isa<IntegerType>(elementTy)) {
295 auto allOnesAttr = rewriter.getIntegerAttr(
296 elementTy, APInt::getAllOnes(elementTy.getIntOrFloatBitWidth()));
297 auto allOnes = arith::ConstantOp::create(rewriter, loc, allOnesAttr);
298 return arith::XOrIOp::create(rewriter, loc, resultTypes, args[0], allOnes);
302 if (isa<tosa::BitwiseXorOp>(op) && isa<IntegerType>(elementTy))
303 return arith::XOrIOp::create(rewriter, loc, resultTypes, args);
306 if (isa<tosa::LogicalLeftShiftOp>(op) && isa<IntegerType>(elementTy))
311 if (isa<tosa::LogicalRightShiftOp>(op) && isa<IntegerType>(elementTy))
316 if (isa<tosa::ArithmeticRightShiftOp>(op) && isa<IntegerType>(elementTy)) {
318 rewriter, loc, resultTypes, args);
319 bool round = cast<tosa::ArithmeticRightShiftOp>(op).getRound();
324 Type i1Ty = IntegerType::get(rewriter.getContext(), 1);
325 auto one = arith::ConstantOp::create(rewriter, loc,
326 IntegerAttr::get(elementTy, 1));
327 auto zero = arith::ConstantOp::create(rewriter, loc,
328 IntegerAttr::get(elementTy, 0));
330 arith::ConstantOp::create(rewriter, loc, IntegerAttr::get(i1Ty, 0));
332 arith::ConstantOp::create(rewriter, loc, IntegerAttr::get(i1Ty, 1));
335 auto shiftValueGreaterThanZero = arith::CmpIOp::create(
336 rewriter, loc, arith::CmpIPredicate::sgt, args[1], zero);
340 arith::SubIOp::create(rewriter, loc, resultTypes, args[1], one);
342 arith::ShRSIOp::create(rewriter, loc, resultTypes, args[0], subtract)
345 rewriter, loc,
TypeRange{i1Ty}, shifted);
347 arith::AndIOp::create(rewriter, loc, i1Ty, truncated, i1one);
349 auto shouldRound = arith::SelectOp::create(
350 rewriter, loc, i1Ty, shiftValueGreaterThanZero, isInputOdd, i1zero);
352 arith::ExtUIOp::create(rewriter, loc, resultTypes, shouldRound);
353 return arith::AddIOp::create(rewriter, loc, resultTypes,
result, extended);
357 if (isa<tosa::ClzOp>(op) && isa<IntegerType>(elementTy)) {
358 return math::CountLeadingZerosOp::create(rewriter, loc, elementTy, args[0]);
362 if (isa<tosa::LogicalAndOp>(op) && elementTy.isInteger(1))
363 return arith::AndIOp::create(rewriter, loc, resultTypes, args);
366 if (isa<tosa::LogicalNotOp>(op) && elementTy.isInteger(1)) {
367 auto one = arith::ConstantOp::create(rewriter, loc,
368 rewriter.getIntegerAttr(elementTy, 1));
369 return arith::XOrIOp::create(rewriter, loc, resultTypes, args[0], one);
373 if (isa<tosa::LogicalOrOp>(op) && elementTy.isInteger(1))
374 return arith::OrIOp::create(rewriter, loc, resultTypes, args);
377 if (isa<tosa::LogicalXorOp>(op) && elementTy.isInteger(1))
378 return arith::XOrIOp::create(rewriter, loc, resultTypes, args);
381 if (isa<tosa::PowOp>(op) && isa<FloatType>(elementTy))
386 if (isa<tosa::RsqrtOp>(op) && isa<FloatType>(elementTy))
391 if (isa<tosa::LogOp>(op) && isa<FloatType>(elementTy))
396 if (isa<tosa::ExpOp>(op) && isa<FloatType>(elementTy))
401 if (isa<tosa::SinOp>(op) && isa<FloatType>(elementTy))
406 if (isa<tosa::CosOp>(op) && isa<FloatType>(elementTy))
411 if (isa<tosa::TanhOp>(op) && isa<FloatType>(elementTy))
416 if (isa<tosa::ErfOp>(op) && llvm::isa<FloatType>(elementTy))
421 if (isa<tosa::GreaterOp>(op) && isa<FloatType>(elementTy))
422 return arith::CmpFOp::create(rewriter, loc, arith::CmpFPredicate::OGT,
425 if (isa<tosa::GreaterOp>(op) && elementTy.isSignlessInteger())
426 return arith::CmpIOp::create(rewriter, loc, arith::CmpIPredicate::sgt,
430 if (isa<tosa::GreaterEqualOp>(op) && isa<FloatType>(elementTy))
431 return arith::CmpFOp::create(rewriter, loc, arith::CmpFPredicate::OGE,
434 if (isa<tosa::GreaterEqualOp>(op) && elementTy.isSignlessInteger())
435 return arith::CmpIOp::create(rewriter, loc, arith::CmpIPredicate::sge,
439 if (isa<tosa::EqualOp>(op) && isa<FloatType>(elementTy))
440 return arith::CmpFOp::create(rewriter, loc, arith::CmpFPredicate::OEQ,
443 if (isa<tosa::EqualOp>(op) && elementTy.isSignlessInteger())
444 return arith::CmpIOp::create(rewriter, loc, arith::CmpIPredicate::eq,
448 if (isa<tosa::SelectOp>(op)) {
450 if (isa<FloatType>(elementTy) || isa<IntegerType>(elementTy))
451 return arith::SelectOp::create(rewriter, loc, args[0], args[1], args[2]);
455 if (isa<tosa::MaximumOp>(op) && isa<FloatType>(elementTy)) {
456 auto max = arith::MaximumFOp::create(rewriter, loc, args[0], args[1]);
458 rewriter, args[0], args[1],
max);
461 if (isa<tosa::MaximumOp>(op) && elementTy.isSignlessInteger()) {
462 return arith::MaxSIOp::create(rewriter, loc, args[0], args[1]);
466 if (isa<tosa::MinimumOp>(op) && isa<FloatType>(elementTy)) {
467 auto min = arith::MinimumFOp::create(rewriter, loc, args[0], args[1]);
469 rewriter, args[0], args[1],
min);
472 if (isa<tosa::MinimumOp>(op) && elementTy.isSignlessInteger()) {
473 return arith::MinSIOp::create(rewriter, loc, args[0], args[1]);
477 if (isa<tosa::CeilOp>(op) && isa<FloatType>(elementTy))
482 if (isa<tosa::FloorOp>(op) && isa<FloatType>(elementTy))
487 if (isa<tosa::ClampOp>(op) && isa<FloatType>(elementTy)) {
488 bool losesInfo =
false;
489 auto clampOp = cast<tosa::ClampOp>(op);
490 APFloat minApf = cast<FloatAttr>(clampOp.getMinValAttr()).getValue();
491 APFloat maxApf = cast<FloatAttr>(clampOp.getMaxValAttr()).getValue();
493 APFloat::rmNearestTiesToEven, &losesInfo);
495 APFloat::rmNearestTiesToEven, &losesInfo);
496 auto min = arith::ConstantOp::create(
497 rewriter, loc, elementTy, rewriter.getFloatAttr(elementTy, minApf));
498 auto max = arith::ConstantOp::create(
499 rewriter, loc, elementTy, rewriter.getFloatAttr(elementTy, maxApf));
502 const auto nanMode = clampOp.getNanMode();
505 if (!isa<FloatType>(elementTy))
510 if (nanMode == NanPropagationMode::PROPAGATE)
524 Value isNaN = arith::CmpFOp::create(
525 rewriter, op->
getLoc(), arith::CmpFPredicate::UNO, args[0], args[0]);
528 return arith::SelectOp::create(rewriter, op->
getLoc(), isNaN,
min,
result);
531 if (isa<tosa::ClampOp>(op) && isa<IntegerType>(elementTy)) {
532 auto intTy = cast<IntegerType>(elementTy);
533 auto clampOp = cast<tosa::ClampOp>(op);
535 cast<IntegerAttr>(clampOp.getMinValAttr()).getValue().getSExtValue();
537 cast<IntegerAttr>(clampOp.getMaxValAttr()).getValue().getSExtValue();
539 int64_t minRepresentable = std::numeric_limits<int64_t>::min();
540 int64_t maxRepresentable = std::numeric_limits<int64_t>::max();
541 if (intTy.isUnsignedInteger()) {
542 minRepresentable = 0;
543 if (intTy.getIntOrFloatBitWidth() <= 63) {
545 (
int64_t)APInt::getMaxValue(intTy.getIntOrFloatBitWidth())
548 }
else if (intTy.getIntOrFloatBitWidth() <= 64) {
550 minRepresentable = APInt::getSignedMinValue(intTy.getIntOrFloatBitWidth())
552 maxRepresentable = APInt::getSignedMaxValue(intTy.getIntOrFloatBitWidth())
557 min = std::max(
min, minRepresentable);
558 max = std::max(
max, minRepresentable);
559 min = std::min(
min, maxRepresentable);
560 max = std::min(
max, maxRepresentable);
563 intTy.getIntOrFloatBitWidth());
565 intTy.getIntOrFloatBitWidth());
567 intTy.isUnsignedInteger());
571 if (isa<tosa::SigmoidOp>(op) && isa<FloatType>(elementTy)) {
573 arith::ConstantOp::create(rewriter, loc, FloatAttr::get(elementTy, 1));
574 auto negate = arith::NegFOp::create(rewriter, loc, resultTypes, args[0]);
575 auto exp = mlir::math::ExpOp::create(rewriter, loc, resultTypes, negate);
576 auto added = arith::AddFOp::create(rewriter, loc, exp, one);
577 return arith::DivFOp::create(rewriter, loc, one, added);
581 if (isa<tosa::CastOp>(op)) {
582 Type srcTy = elementTy;
583 Type dstTy = resultTypes.front();
585 (
void)rewriter.notifyMatchFailure(op,
"unsupported type");
593 bool inputUnsigned = cast<tosa::CastOp>(op).getInputUnsigned();
598 if (isa<FloatType>(srcTy) && isa<FloatType>(dstTy) && bitExtend)
602 if (isa<FloatType>(srcTy) && isa<FloatType>(dstTy) && !bitExtend)
607 if (srcTy.
isInteger(1) && arith::UIToFPOp::areCastCompatible(srcTy, dstTy))
611 if (srcTy.
isInteger(1) && isa<IntegerType>(dstTy) && bitExtend)
618 auto unrealizedCast =
619 UnrealizedConversionCastOp::create(
623 return arith::UIToFPOp::create(rewriter, loc, resultTypes[0],
628 if (inputUnsigned && arith::UIToFPOp::areCastCompatible(srcTy, dstTy))
633 if (arith::SIToFPOp::areCastCompatible(srcTy, dstTy))
634 return arith::SIToFPOp::create(rewriter, loc, resultTypes, args,
638 if (isa<FloatType>(srcTy) && dstTy.
isInteger(1)) {
639 Value zero = arith::ConstantOp::create(rewriter, loc,
640 rewriter.getFloatAttr(srcTy, 0.0));
641 return arith::CmpFOp::create(rewriter, loc, arith::CmpFPredicate::UNE,
645 if (arith::FPToSIOp::areCastCompatible(srcTy, dstTy)) {
646 auto rounded = math::RoundEvenOp::create(rewriter, loc, args[0]);
648 const auto &fltSemantics = cast<FloatType>(srcTy).getFloatSemantics();
652 APFloat::semanticsMaxExponent(fltSemantics)) {
655 auto conv = arith::FPToSIOp::create(rewriter, loc, dstTy, rounded);
656 auto posInf = arith::ConstantOp::create(
659 APFloat::getInf(fltSemantics)));
660 auto negInf = arith::ConstantOp::create(
662 rewriter.getFloatAttr(
664 APFloat::getInf(fltSemantics,
true)));
665 auto overflow = arith::CmpFOp::create(
666 rewriter, loc, arith::CmpFPredicate::UEQ, rounded, posInf);
667 auto underflow = arith::CmpFOp::create(
668 rewriter, loc, arith::CmpFPredicate::UEQ, rounded, negInf);
669 auto intMin = arith::ConstantOp::create(
671 rewriter.getIntegerAttr(
674 auto intMax = arith::ConstantOp::create(
676 rewriter.getIntegerAttr(
680 arith::SelectOp::create(rewriter, loc, overflow, intMax, conv);
681 return arith::SelectOp::create(rewriter, loc, underflow, intMin,
685 auto intMinFP = arith::ConstantOp::create(
687 rewriter.getFloatAttr(
693 if (cast<FloatType>(srcTy).getFPMantissaWidth() >=
699 auto intMaxFP = arith::ConstantOp::create(
701 rewriter.getFloatAttr(
708 return arith::FPToSIOp::create(rewriter, loc, dstTy, clamped);
715 auto intMaxPlusOneFP = arith::ConstantOp::create(
717 rewriter.getFloatAttr(
724 auto intMax = arith::ConstantOp::create(
726 rewriter.getIntegerAttr(
730 arith::MaximumFOp::create(rewriter, loc, rounded, intMinFP);
732 arith::FPToSIOp::create(rewriter, loc, dstTy, minClampedFP);
733 auto overflow = arith::CmpFOp::create(
734 rewriter, loc, arith::CmpFPredicate::UGE, rounded, intMaxPlusOneFP);
735 return arith::SelectOp::create(rewriter, loc, overflow, intMax,
741 if (isa<IntegerType>(srcTy) && dstTy.
isInteger(1)) {
744 return arith::CmpIOp::create(rewriter, loc, arith::CmpIPredicate::ne,
749 if (inputUnsigned && isa<IntegerType>(srcTy) && isa<IntegerType>(dstTy) &&
754 if (isa<IntegerType>(srcTy) && isa<IntegerType>(dstTy) && bitExtend)
755 return arith::ExtSIOp::create(rewriter, loc, resultTypes, args,
758 if (isa<IntegerType>(srcTy) && isa<IntegerType>(dstTy) && !bitExtend) {
759 return arith::TruncIOp::create(rewriter, loc, dstTy, args[0]);
763 (
void)rewriter.notifyMatchFailure(
764 op,
"unhandled op for linalg body calculation for elementwise op");
783 return tensor::DimOp::create(rewriter, loc,
tensor, indexValue).getResult();
789 auto shapedType = dyn_cast<ShapedType>(
tensor.getType());
790 assert(shapedType && shapedType.hasRank() &&
"expected a ranked shaped type");
791 assert(
index >= 0 &&
index < shapedType.getRank() &&
"index out of bounds");
792 if (shapedType.isDynamicDim(
index))
798 auto isRanked = [](
Value value) {
799 return isa<RankedTensorType>(value.getType());
801 return llvm::all_of(operation->
getOperands(), isRanked) &&
802 llvm::all_of(operation->
getResults(), isRanked);
815std::pair<OpFoldResult, Value>
822 for (
auto operand : operands) {
823 auto size = cast<RankedTensorType>(operand.getType()).getDimSize(dim);
824 if (ShapedType::isStatic(size) && size > 1)
829 auto operandsWithDynamicDim =
830 llvm::filter_to_vector(operands, [&](
Value operand) {
831 return cast<RankedTensorType>(operand.
getType()).isDynamicDim(dim);
835 if (operandsWithDynamicDim.empty())
842 getTensorDim(rewriter, loc, indexPool, operandsWithDynamicDim[0], dim);
843 if (operandsWithDynamicDim.size() == 1)
844 return {targetSize, operandsWithDynamicDim[0]};
847 for (
size_t i = 1; i < operandsWithDynamicDim.size(); i++) {
849 getTensorDim(rewriter, loc, indexPool, operandsWithDynamicDim[i], dim);
850 targetSize = arith::MaxUIOp::create(rewriter, loc, targetSize, nextSize);
852 return {targetSize,
nullptr};
860 assert(!operands.empty());
861 auto rank = cast<RankedTensorType>(operands.front().
getType()).getRank();
864 for (
auto dim : llvm::seq<int64_t>(0, rank)) {
865 auto [targetSize, masterOperand] =
867 targetShape.push_back(targetSize);
868 masterOperands.push_back(masterOperand);
870 return {targetShape, masterOperands};
876 Value masterOperand) {
878 auto rankedTensorType = cast<RankedTensorType>(operand.
getType());
879 if (!rankedTensorType.isDynamicDim(dim))
886 if (operand == masterOperand)
890 auto rank = rankedTensorType.getRank();
892 for (
auto index : llvm::seq<int64_t>(0, rank)) {
895 affineExprs.push_back(affineExpr);
897 auto broadcastAffineMap =
903 auto one =
createIndex(rewriter, loc, indexPool, 1);
904 auto runtimeSize =
getTensorDim(rewriter, loc, indexPool, operand, dim);
905 auto broadcastNecessary = arith::CmpIOp::create(
906 rewriter, loc, arith::CmpIPredicate::eq, runtimeSize, one);
916 for (
auto index : llvm::seq<int64_t>(0, rank)) {
917 auto size =
index == dim ? targetSize
920 outputTensorShape.push_back(size);
922 Value outputTensor = tensor::EmptyOp::create(
923 opBuilder, loc, outputTensorShape, rankedTensorType.getElementType());
927 linalg::GenericOp::create(
928 opBuilder, loc, outputTensor.
getType(), operand, outputTensor,
932 linalg::YieldOp::create(opBuilder, loc, blockArgs.front());
937 auto castResultTensor = rewriter.
createOrFold<tensor::CastOp>(
938 loc, operand.
getType(), resultTensor);
941 scf::YieldOp::create(opBuilder, loc, castResultTensor);
946 scf::YieldOp::create(opBuilder, loc, operand);
950 auto ifOp = scf::IfOp::create(rewriter, loc, broadcastNecessary,
951 emitThenRegion, emitElseRegion);
952 return ifOp.getResult(0);
959 int64_t rank = cast<RankedTensorType>(operand.
getType()).getRank();
960 assert((
int64_t)targetShape.size() == rank);
961 assert((
int64_t)masterOperands.size() == rank);
962 for (
auto index : llvm::seq<int64_t>(0, rank))
975 if (operands.size() == 1)
979 bool hasDynamic =
false;
980 for (
auto op : operands) {
981 const auto tType = dyn_cast<RankedTensorType>(op.getType());
982 if (tType && !tType.hasStaticShape()) {
991 return llvm::map_to_vector(operands, [&](
Value operand) {
993 targetShape, masterOperands);
1002 auto resultType = cast_or_null<RankedTensorType>(
1005 return rewriter.notifyMatchFailure(operation,
"failed to convert type");
1007 Value outputTensor = tensor::EmptyOp::create(rewriter, loc, targetShape,
1008 resultType.getElementType());
1013 auto rank = resultType.getRank();
1014 auto affineMaps = llvm::map_to_vector(operands, [&](
Value operand) {
1015 auto shape = cast<ShapedType>(operand.
getType()).getShape();
1017 for (
auto it : llvm::enumerate(
shape)) {
1021 bool requiresBroadcast =
1022 (it.value() == 1 && resultType.getDimSize(it.index()) != 1);
1023 auto affineExpr = requiresBroadcast
1024 ? rewriter.getAffineConstantExpr(0)
1025 : rewriter.getAffineDimExpr(it.index());
1026 affineExprs.push_back(affineExpr);
1028 return AffineMap::get(rank, 0, affineExprs, rewriter.getContext());
1032 for (
Value scalar : scalarOperands) {
1033 auto scalarRank = cast<ShapedType>(scalar.getType()).getRank();
1035 rewriter.getAffineConstantExpr(0));
1036 affineMaps.push_back(
AffineMap::get(rank, 0, zeros, rewriter.getContext()));
1038 affineMaps.push_back(rewriter.getMultiDimIdentityMap(rank));
1041 llvm::append_range(inputs, scalarOperands);
1044 bool encounteredError =
false;
1045 auto linalgOp = linalg::GenericOp::create(
1046 rewriter, loc, outputTensor.
getType(), inputs, outputTensor, affineMaps,
1051 {resultType.getElementType()}, rewriter);
1053 encounteredError =
true;
1056 linalg::YieldOp::create(opBuilder, loc, opResult);
1058 if (encounteredError)
1059 return rewriter.notifyMatchFailure(
1060 operation,
"unable to create linalg.generic body for elementwise op");
1063 auto castResult = rewriter.createOrFold<tensor::CastOp>(
1064 loc, resultType, linalgOp->getResult(0));
1065 rewriter.replaceOp(operation, castResult);
1073 if (isa<tosa::MulOp>(operation))
1074 return operands.take_front(2);
1075 if (isa<tosa::NegateOp>(operation))
1076 return operands.take_front(1);
1084 if (isa<tosa::MulOp>(operation)) {
1088 return operands.drop_front(2);
1090 if (
auto negate = dyn_cast<tosa::NegateOp>(operation)) {
1091 if (succeeded(negate.getInput1ZeroPoint()) &&
1092 succeeded(negate.getOutputZeroPoint()))
1094 return operands.drop_front(1);
1101 ConversionPatternRewriter &rewriter,
1105 assert(operation->
getNumResults() == 1 &&
"elementwise op expects 1 result");
1107 "elementwise op expects at least 1 operand");
1109 return rewriter.notifyMatchFailure(operation,
1110 "Unranked tensors not supported");
1114 auto loc = operation->
getLoc();
1117 auto [targetShape, masterOperands] =
1119 auto broadcastOperands =
1121 targetShape, masterOperands);
1123 scalarOperands, targetShape, converter);
1130 bool allowNonFinites) {
1131 if (isa<tosa::ReduceSumOp>(op) && isa<FloatType>(elementTy))
1134 if (isa<tosa::ReduceSumOp>(op) && isa<IntegerType>(elementTy))
1137 if (isa<tosa::ReduceProductOp>(op) && isa<FloatType>(elementTy))
1140 if (isa<tosa::ReduceProductOp>(op) && isa<IntegerType>(elementTy))
1143 if (isa<tosa::ReduceMinOp>(op) && isa<FloatType>(elementTy))
1147 false, allowNonFinites));
1149 if (isa<tosa::ReduceMinOp>(op) && isa<IntegerType>(elementTy))
1153 if (isa<tosa::ReduceMaxOp>(op) && isa<FloatType>(elementTy))
1157 true, allowNonFinites));
1159 if (isa<tosa::ReduceMaxOp>(op) && isa<IntegerType>(elementTy))
1163 if (isa<tosa::ReduceAllOp>(op) && elementTy.
isInteger(1))
1166 if (isa<tosa::ReduceAnyOp>(op) && elementTy.
isInteger(1))
1169 if (isa<tosa::ArgMaxOp>(op) && isa<FloatType>(elementTy))
1173 true, allowNonFinites));
1175 if (isa<tosa::ArgMaxOp>(op) && isa<IntegerType>(elementTy))
1179 if (isa<tosa::ArgMinOp>(op) && isa<FloatType>(elementTy))
1183 false, allowNonFinites));
1185 if (isa<tosa::ArgMinOp>(op) && isa<IntegerType>(elementTy))
1199 if (isa<tosa::ReduceSumOp>(op) && isa<FloatType>(elementTy)) {
1201 rewriter, loc,
TypeRange{elementTy}, args);
1204 if (isa<tosa::ReduceSumOp>(op) && isa<IntegerType>(elementTy)) {
1206 rewriter, loc,
TypeRange{elementTy}, args);
1209 if (isa<tosa::ReduceProductOp>(op) && isa<FloatType>(elementTy)) {
1211 rewriter, loc,
TypeRange{elementTy}, args);
1214 if (isa<tosa::ReduceProductOp>(op) && isa<IntegerType>(elementTy)) {
1216 rewriter, loc,
TypeRange{elementTy}, args);
1219 if (isa<tosa::ReduceMinOp>(op) && isa<FloatType>(elementTy)) {
1220 return arith::MinimumFOp::create(rewriter, loc, args[0], args[1]);
1223 if (isa<tosa::ReduceMinOp>(op) && isa<IntegerType>(elementTy)) {
1224 return arith::MinSIOp::create(rewriter, loc, args[0], args[1]);
1227 if (isa<tosa::ReduceMaxOp>(op) && isa<FloatType>(elementTy)) {
1228 return arith::MaximumFOp::create(rewriter, loc, args[0], args[1]);
1231 if (isa<tosa::ReduceMaxOp>(op) && isa<IntegerType>(elementTy)) {
1232 return arith::MaxSIOp::create(rewriter, loc, args[0], args[1]);
1235 if (isa<tosa::ReduceAllOp>(op) && elementTy.
isInteger(1))
1236 return arith::AndIOp::create(rewriter, loc, args);
1238 if (isa<tosa::ReduceAnyOp>(op) && elementTy.
isInteger(1))
1239 return arith::OrIOp::create(rewriter, loc, args);
1247template <
typename OpTy>
1250 bool allowNonFinites) {
1251 auto loc = op->getLoc();
1252 auto inputTy = dyn_cast<RankedTensorType>(op->getOperand(0).getType());
1253 auto resultTy = dyn_cast<RankedTensorType>(op->getResult(0).getType());
1254 if (!inputTy || !resultTy)
1257 auto elementTy = resultTy.getElementType();
1258 Value input = op->getOperand(0);
1261 bool widenAccTy = std::is_same_v<OpTy, tosa::ReduceSumOp> &&
1262 isa<FloatType>(elementTy) &&
1263 cast<FloatType>(elementTy).isBF16();
1268 for (
unsigned i = 0; i < inputTy.getRank(); i++) {
1270 reduceShape.push_back(inputTy.getDimSize(i));
1271 if (inputTy.isDynamicDim(i))
1272 dynDims.push_back(tensor::DimOp::create(rewriter, loc, input, i));
1277 inputs.push_back(input);
1281 tensor::EmptyOp::create(rewriter, loc, reduceShape, accTy, dynDims)
1284 auto fillValueAttr =
1288 op,
"No initial value found for reduction operation");
1290 auto fillValue = arith::ConstantOp::create(rewriter, loc, fillValueAttr);
1292 linalg::FillOp::create(rewriter, loc,
ValueRange{fillValue},
1295 outputs.push_back(filledTensor);
1297 bool isNanIgnoreMode =
false;
1298 if constexpr (std::is_same_v<OpTy, tosa::ReduceMinOp> ||
1299 std::is_same_v<OpTy, tosa::ReduceMaxOp>) {
1301 if (isa<FloatType>(elementTy) &&
1302 op.getNanMode() == NanPropagationMode::IGNORE) {
1303 isNanIgnoreMode =
true;
1309 auto trueValue = arith::ConstantOp::create(rewriter, loc, trueAttr);
1310 auto emptyBoolTensor =
1311 tensor::EmptyOp::create(rewriter, loc, reduceShape,
1312 trueValue.getType(), dynDims)
1314 auto allResultsNaNTensor =
1315 linalg::FillOp::create(rewriter, loc,
ValueRange{trueValue},
1327 inputs.push_back(input);
1328 outputs.push_back(allResultsNaNTensor);
1332 bool didEncounterError =
false;
1333 linalg::LinalgOp linalgOp = linalg::ReduceOp::create(
1334 rewriter, loc, inputs, outputs, axis,
1336 std::array<Value, 2> binaryArgs{
1337 blockArgs[0], isNanIgnoreMode ? blockArgs[2] : blockArgs[1]};
1340 if (binaryArgs[0].
getType() != accTy)
1341 binaryArgs[0] = arith::ExtFOp::create(
1342 nestedBuilder, nestedLoc,
TypeRange{accTy},
1343 ValueRange{binaryArgs[0]}, arith::ExtFOp::Properties{});
1348 didEncounterError =
true;
1351 if (isNanIgnoreMode) {
1352 auto inputValue = blockArgs[0];
1353 auto initialValue = blockArgs[2];
1354 auto oldAllResultsNanFlagValue = blockArgs[3];
1357 Value isNaN = arith::CmpFOp::create(nestedBuilder, op->getLoc(),
1358 arith::CmpFPredicate::UNO,
1359 inputValue, inputValue);
1361 auto selectOp = arith::SelectOp::create(nestedBuilder, op->getLoc(),
1362 isNaN, initialValue,
result);
1365 auto newAllResultsNanFlagValue = arith::AndIOp::create(
1366 nestedBuilder, op->getLoc(), oldAllResultsNanFlagValue, isNaN);
1367 resultsToYield.push_back(selectOp);
1368 resultsToYield.push_back(newAllResultsNanFlagValue);
1370 resultsToYield.push_back(
result);
1372 linalg::YieldOp::create(nestedBuilder, loc, resultsToYield);
1375 if (!didEncounterError)
1377 op,
"unable to create linalg.generic body for reduce op");
1379 if (isNanIgnoreMode) {
1389 auto nanValue = arith::ConstantOp::create(rewriter, loc, nanValueAttr);
1390 auto emptyNanTensor =
1391 tensor::EmptyOp::create(rewriter, loc, reduceShape, accTy, dynDims)
1393 auto nanFilledTensor =
1394 linalg::FillOp::create(rewriter, loc,
ValueRange{nanValue},
1400 auto finalEmptyTensor =
1401 tensor::EmptyOp::create(rewriter, loc, reduceShape, accTy, dynDims)
1407 ins.push_back(linalgOp->getOpResult(1));
1408 ins.push_back(nanFilledTensor);
1409 ins.push_back(linalgOp->getResult(0));
1410 outs.push_back(finalEmptyTensor);
1412 linalg::ElementwiseOp::create(rewriter, op->getLoc(), ins, outs,
1413 mlir::linalg::ElementwiseKind::select);
1414 linalgOp = linalgSelect;
1418 Value reducedRes = linalgOp->getResult(0);
1421 tensor::EmptyOp::create(rewriter, loc, reduceShape, elementTy, dynDims)
1424 const unsigned reducedRank =
1425 cast<ShapedType>(reducedRes.
getType()).getRank();
1428 linalg::GenericOp::create(
1434 Value truncf = arith::TruncFOp::create(nestedBuilder, nestedLoc,
1435 elementTy, args[0]);
1436 linalg::YieldOp::create(nestedBuilder, nestedLoc, truncf);
1442 uint64_t expandInputRank = cast<ShapedType>(reducedRes.
getType()).getRank();
1443 reassociationMap.resize(expandInputRank);
1445 for (uint64_t i = 0; i < expandInputRank; i++) {
1446 int32_t dimToPush = i > axis ? i + 1 : i;
1450 if (expandInputRank != 0) {
1451 int32_t expandedDim = axis < expandInputRank ? axis : expandInputRank - 1;
1452 reassociationMap[expandedDim].push_back(
1469 auto type = cast<RankedTensorType>(operand.
getType());
1471 cast<RankedTensorType>(other.
getType()).getRank() - type.getRank();
1472 if (missingDims <= 0)
1477 for (
int64_t dim = 0; dim < missingDims; ++dim) {
1479 dimensions.push_back(dim);
1482 Value empty = tensor::EmptyOp::create(
1483 rewriter, loc,
shape, type.getElementType(), type.getEncoding());
1484 return linalg::BroadcastOp::create(rewriter, loc, operand, empty, dimensions)
1496 auto type = cast<RankedTensorType>(operand.
getType());
1498 sliceShape.erase(sliceShape.begin() + dim);
1499 auto sliceType = RankedTensorType::get(sliceShape, type.getElementType(),
1500 type.getEncoding());
1502 rewriter, loc, operand, sliceType);
1507 Value empty = tensor::EmptyOp::create(
1508 rewriter, loc,
shape, type.getElementType(), type.getEncoding());
1509 return linalg::BroadcastOp::create(rewriter, loc,
slice, empty,
1516static std::pair<Value, Value>
1520 int64_t bRank = cast<RankedTensorType>(
b.getType()).getRank();
1521 int64_t firstSharedDim = std::max(aRank, bRank) - std::min(aRank, bRank);
1527 for (
int64_t dim = firstSharedDim; dim < batchRank; ++dim) {
1528 auto aType = cast<RankedTensorType>(a.
getType());
1529 auto bType = cast<RankedTensorType>(
b.getType());
1530 int64_t aSize = aType.getDimSize(dim);
1531 int64_t bSize = bType.getDimSize(dim);
1532 if (aSize == bSize && ShapedType::isStatic(aSize))
1537 auto [size, masterOperand] =
1558 RankedTensorType outputType,
1560 const int64_t outputRank = outputType.getRank();
1561 const int64_t batchRank = outputRank - 2;
1562 auto normalizedOperands =
1564 a = normalizedOperands.first;
1565 b = normalizedOperands.second;
1568 outputShape.reserve(outputRank);
1569 for (
int64_t batchDim = 0; batchDim < batchRank; ++batchDim)
1572 outputShape.push_back(
1575 for (
auto [dim, size] : llvm::enumerate(outputShape))
1576 if (outputType.isDynamicDim(dim))
1577 dynamicDims.push_back(
1580 Value zero = arith::ConstantOp::create(
1581 rewriter, loc, rewriter.
getZeroAttr(outputType.getElementType()));
1582 Value emptyTensor = tensor::EmptyOp::create(
1583 rewriter, loc, outputType.getShape(), outputType.getElementType(),
1585 Value zeroTensor = linalg::FillOp::create(rewriter, loc,
ValueRange{zero},
1589 return {a,
b, zeroTensor};
1596class QuantizedMatMulTConverter :
public OpConversionPattern<tosa::MatMulTOp> {
1598 using OpConversionPattern<tosa::MatMulTOp>::OpConversionPattern;
1600 matchAndRewrite(tosa::MatMulTOp op, OpAdaptor adaptor,
1601 ConversionPatternRewriter &rewriter)
const final {
1603 auto aType = dyn_cast<RankedTensorType>(adaptor.getA().getType());
1604 auto bType = dyn_cast<RankedTensorType>(adaptor.getB().getType());
1605 auto outputType = dyn_cast<RankedTensorType>(op.getType());
1606 if (!aType || !bType || !outputType)
1607 return rewriter.notifyMatchFailure(op,
"only supports ranked tensors");
1609 FailureOr<int64_t> maybeAZp = op.getAZeroPoint();
1610 FailureOr<int64_t> maybeBZp = op.getBZeroPoint();
1611 if (failed(maybeAZp))
1612 return rewriter.notifyMatchFailure(
1613 op,
"input A zero point cannot be statically determined");
1614 if (failed(maybeBZp))
1615 return rewriter.notifyMatchFailure(
1616 op,
"input B zero point cannot be statically determined");
1618 const int64_t aZpVal = *maybeAZp;
1619 const int64_t bZpVal = *maybeBZp;
1621 if (aZpVal == 0 && bZpVal == 0)
1622 return rewriter.notifyMatchFailure(
1623 op,
"requires at least one nonzero zero point");
1625 const int64_t outputRank = outputType.getRank();
1626 const int64_t batchRank = outputRank - 2;
1627 auto [a,
b, zeroTensor] =
1630 Type outputElementType = outputType.getElementType();
1632 int64_t loopRank = outputRank + 1;
1634 llvm::map_to_vector(llvm::seq<int64_t>(0, loopRank), [&](
int64_t dim) {
1635 return rewriter.getAffineDimExpr(dim);
1638 dimensions.begin() + batchRank);
1639 aMap.push_back(dimensions[batchRank]);
1640 aMap.push_back(dimensions[batchRank + 2]);
1642 dimensions.begin() + batchRank);
1643 bMap.push_back(dimensions[batchRank + 1]);
1644 bMap.push_back(dimensions[batchRank + 2]);
1646 dimensions.begin() + batchRank);
1647 outputMap.push_back(dimensions[batchRank]);
1648 outputMap.push_back(dimensions[batchRank + 1]);
1653 SmallVector<utils::IteratorType> iteratorTypes(
1654 loopRank, utils::IteratorType::parallel);
1655 iteratorTypes.back() = utils::IteratorType::reduction;
1657 auto generic = linalg::GenericOp::create(
1658 rewriter, loc, outputType,
ValueRange{a,
b}, zeroTensor, indexingMaps,
1660 [=](OpBuilder &builder, Location nestedLoc,
ValueRange args) {
1661 auto extendToOutputType = [&](Value value) {
1662 if (value.getType() == outputElementType)
1664 return arith::ExtSIOp::create(builder, nestedLoc, outputElementType,
1669 Value aValue = extendToOutputType(args[0]);
1670 Value bValue = extendToOutputType(args[1]);
1671 Value aZp = arith::ConstantOp::create(
1673 builder.getIntegerAttr(outputElementType, aZpVal));
1674 Value bZp = arith::ConstantOp::create(
1676 builder.getIntegerAttr(outputElementType, bZpVal));
1678 arith::SubIOp::create(builder, nestedLoc, aValue, aZp);
1680 arith::SubIOp::create(builder, nestedLoc, bValue, bZp);
1682 arith::MulIOp::create(builder, nestedLoc, adjustedA, adjustedB);
1684 arith::AddIOp::create(builder, nestedLoc, args[2],
product);
1685 linalg::YieldOp::create(builder, nestedLoc, sum);
1687 rewriter.replaceOp(op, generic.getResult(0));
1692template <
typename SrcOp>
1693class PointwiseConverter :
public OpConversionPattern<SrcOp> {
1695 using OpConversionPattern<SrcOp>::OpConversionPattern;
1696 using typename OpConversionPattern<SrcOp>::OpAdaptor;
1699 matchAndRewrite(SrcOp op, OpAdaptor operands,
1700 ConversionPatternRewriter &rewriter)
const final {
1702 op, operands.getOperands(), rewriter, *this->getTypeConverter());
1712 auto inputType = cast<RankedTensorType>(input.
getType());
1713 auto elemType = inputType.getElementType();
1714 auto collapsedType = RankedTensorType::get({}, elemType);
1716 return tensor::CollapseShapeOp::create(rewriter, loc, collapsedType, input,
1723 output.reserve(input.size());
1725 for (
auto v : llvm::map_range(
1726 input, [](int32_t val) {
return static_cast<int8_t
>(val); })) {
1727 output.push_back(v);
1739static void setupLinalgGenericOpInputAndIndexingMap(
1742 bool isConstant, tosa::RescaleOp op,
Value &constant,
int64_t &arg,
1743 bool isShift =
false) {
1745 auto loc = op.getLoc();
1746 auto inputTy = cast<ShapedType>(op.getInput().getType());
1747 unsigned rank = inputTy.getRank();
1753 if (values.size() == 1) {
1754 IntegerAttr intAttr = isShift
1757 constant = arith::ConstantOp::create(rewriter, loc, intAttr);
1761 auto tensorType = RankedTensorType::get(
1762 {
static_cast<int64_t>(values.size())}, elementType);
1768 genericInputs.push_back(
1769 arith::ConstantOp::create(rewriter, loc, EltAttr));
1777 auto operand = isShift ? op.getShift() : op.getMultiplier();
1778 auto tensorType = dyn_cast<RankedTensorType>(operand.getType());
1779 if (tensorType && tensorType.hasStaticShape() &&
1780 tensorType.getShape()[0] == 1) {
1785 genericInputs.push_back(collapse1xNTensorToN(rewriter, operand, loc));
1786 indexingMaps.push_back(broadcastMap);
1788 genericInputs.push_back(operand);
1794 arg = indexingMaps.size() - 1;
1799 FailureOr<int64_t> maybeZp,
Location loc,
1801 bool isOutputZp =
false) {
1804 const uint32_t attrBitwidth =
1805 isOutputZp ? 32 : (bitwidth > 32 ? bitwidth : 32);
1812 result = blockArgs[zpArg];
1813 auto zpTy =
result.getType();
1814 if (zpTy.getIntOrFloatBitWidth() < attrBitwidth) {
1817 if (zpTy.isUnsignedInteger()) {
1819 UnrealizedConversionCastOp::create(
1824 if (zpTy.isUnsignedInteger()) {
1825 return arith::ExtUIOp::create(builder, loc, extendType,
result);
1827 return arith::ExtSIOp::create(builder, loc, extendType,
result);
1831 return arith::ConstantOp::create(builder, loc,
1832 IntegerAttr::get(extendType, *maybeZp));
1839 using OpRewritePattern<tosa::RescaleOp>::OpRewritePattern;
1841 LogicalResult matchAndRewrite(tosa::RescaleOp op,
1842 PatternRewriter &rewriter)
const final {
1843 auto loc = op.getLoc();
1844 auto input = op.getInput();
1845 auto inputTy = cast<ShapedType>(op.getInput().getType());
1846 auto outputTy = cast<ShapedType>(op.getOutput().getType());
1847 unsigned rank = inputTy.getRank();
1850 if (op.getRoundingMode() == RoundingMode::INEXACT_ROUND)
1852 op,
"tosa.rescale with rounding mode = 'INEXACT_ROUND' is not "
1853 "currently supported");
1854 if (op.getRoundingMode() == RoundingMode::DOUBLE_ROUND && !op.getScale32())
1856 op,
"tosa.rescale requires scale32 for double_round to be true");
1858 if (!isa<IntegerType>(inputTy.getElementType()))
1862 auto outputElemTy = dyn_cast<IntegerType>(outputTy.getElementType());
1863 if (!outputElemTy || outputElemTy.getWidth() > 32)
1865 op,
"only support integer output types up to 32 bits");
1871 FailureOr<int64_t> maybeIZp = op.getInputZeroPoint();
1872 if (succeeded(maybeIZp) && op.verifyInputZeroPoint(*maybeIZp).failed())
1874 FailureOr<int64_t> maybeOZp = op.getOutputZeroPoint();
1875 if (succeeded(maybeOZp) && op.verifyOutputZeroPoint(*maybeOZp).failed())
1878 SmallVector<Value> dynDims;
1879 for (
int i = 0; i < outputTy.getRank(); i++) {
1880 if (outputTy.isDynamicDim(i)) {
1881 dynDims.push_back(tensor::DimOp::create(rewriter, loc, input, i));
1885 DenseElementsAttr shiftElems;
1886 bool isShiftConstant =
false;
1888 isShiftConstant =
true;
1890 DenseElementsAttr multiplierElems;
1891 bool isMultiplierConstant =
false;
1893 isMultiplierConstant =
true;
1895 llvm::SmallVector<int32_t> shiftValues;
1896 llvm::SmallVector<int32_t> multiplierValues;
1899 if (isMultiplierConstant && isShiftConstant) {
1901 shiftValues = llvm::map_to_vector(
1902 shiftElems.
getValues<IntegerAttr>(), [](IntegerAttr attr) -> int32_t {
1903 return static_cast<int32_t>(attr.getInt());
1906 llvm::map_to_vector(multiplierElems.
getValues<IntegerAttr>(),
1907 [](IntegerAttr attr) -> int32_t {
1908 return static_cast<int32_t>(attr.getInt());
1912 for (
int i = 0, s = multiplierValues.size(); i < s; i++) {
1913 if (shiftValues[i] > 63) {
1915 multiplierValues[i] = 0;
1920 doubleRound = op.getRoundingMode() == RoundingMode::DOUBLE_ROUND &&
1921 llvm::any_of(shiftValues, [](int32_t v) {
return v > 31; });
1923 doubleRound = op.getRoundingMode() == RoundingMode::DOUBLE_ROUND;
1925 RoundingMode roundingMode =
1926 doubleRound ? RoundingMode::DOUBLE_ROUND : RoundingMode::SINGLE_ROUND;
1928 SmallVector<AffineMap> indexingMaps = {
1930 SmallVector<Value, 4> genericInputs = {input};
1934 Value multiplierConstant;
1935 int64_t multiplierArg = 0;
1936 setupLinalgGenericOpInputAndIndexingMap(
1937 rewriter, multiplierValues, genericInputs, indexingMaps,
1938 isMultiplierConstant, op, multiplierConstant, multiplierArg);
1942 Value shiftConstant;
1943 int64_t shiftArg = 0;
1944 setupLinalgGenericOpInputAndIndexingMap(
1945 rewriter, shiftValues, genericInputs, indexingMaps, isShiftConstant, op,
1946 shiftConstant, shiftArg,
true);
1960 genericInputs.push_back(
1961 collapse1xNTensorToN(rewriter, op->getOperand(3), loc));
1962 indexingMaps.push_back(broadcastMap);
1963 iZpArg = indexingMaps.size() - 1;
1967 genericInputs.push_back(
1968 collapse1xNTensorToN(rewriter, op->getOperand(4), loc));
1969 indexingMaps.push_back(broadcastMap);
1970 oZpArg = indexingMaps.size() - 1;
1977 Value emptyTensor = tensor::EmptyOp::create(
1978 rewriter, loc, outputTy.getShape(), outputTy.getElementType(),
1979 ArrayRef<Value>({dynDims}));
1981 auto linalgOp = linalg::GenericOp::create(
1982 rewriter, loc, outputTy, genericInputs,
ValueRange{emptyTensor},
1984 [&](OpBuilder &nestedBuilder, Location nestedLoc,
1986 Value value = blockArgs[0];
1987 Type valueTy = value.
getType();
1989 FailureOr<int64_t> maybeIZp = op.getInputZeroPoint();
1990 auto inputZp = getExtendZp(nestedBuilder, valueTy, maybeIZp,
1991 nestedLoc, blockArgs, iZpArg);
1993 FailureOr<int64_t> maybeOZp = op.getOutputZeroPoint();
1994 auto outputZp = getExtendZp(nestedBuilder, valueTy, maybeOZp,
1995 nestedLoc, blockArgs, oZpArg,
true);
1997 IntegerType outIntType =
1998 cast<IntegerType>(blockArgs.back().
getType());
1999 unsigned outBitWidth = outIntType.getWidth();
2000 assert(outBitWidth <= 32 &&
"Unexpected output zeropoint bitwidth");
2002 Value multiplier = multiplierConstant ? multiplierConstant
2003 : blockArgs[multiplierArg];
2004 Value shift = shiftConstant ? shiftConstant : blockArgs[shiftArg];
2007 value = UnrealizedConversionCastOp::create(
2008 nestedBuilder, nestedLoc,
2009 nestedBuilder.getIntegerType(
2015 if (op.getInputUnsigned()) {
2016 value = arith::ExtUIOp::create(nestedBuilder, nestedLoc,
2017 nestedBuilder.getI32Type(), value);
2019 value = arith::ExtSIOp::create(nestedBuilder, nestedLoc,
2020 nestedBuilder.getI32Type(), value);
2025 arith::SubIOp::create(nestedBuilder, nestedLoc, value, inputZp);
2027 value = tosa::ApplyScaleOp::create(nestedBuilder, loc,
2028 nestedBuilder.getI32Type(), value,
2029 multiplier, shift, roundingMode);
2033 arith::AddIOp::create(nestedBuilder, nestedLoc, value, outputZp);
2036 int32_t intMin = APInt::getSignedMinValue(outBitWidth).getSExtValue();
2037 int32_t intMax = APInt::getSignedMaxValue(outBitWidth).getSExtValue();
2040 if (op.getOutputUnsigned()) {
2042 intMax = APInt::getMaxValue(outBitWidth).getZExtValue();
2045 auto intMinVal = arith::ConstantOp::create(
2046 nestedBuilder, loc, nestedBuilder.getI32IntegerAttr(intMin));
2047 auto intMaxVal = arith::ConstantOp::create(
2048 nestedBuilder, loc, nestedBuilder.getI32IntegerAttr(intMax));
2051 nestedBuilder,
false);
2053 if (outIntType.getWidth() < 32) {
2054 value = arith::TruncIOp::create(
2055 nestedBuilder, nestedLoc,
2059 if (outIntType.isUnsignedInteger()) {
2060 value = UnrealizedConversionCastOp::create(nestedBuilder, nestedLoc,
2064 linalg::YieldOp::create(nestedBuilder, loc, value);
2067 rewriter.
replaceOp(op, linalgOp->getResults());
2077 using OpRewritePattern<tosa::ResizeOp>::OpRewritePattern;
2079 LogicalResult matchAndRewrite(tosa::ResizeOp op,
2080 PatternRewriter &rewriter)
const final {
2081 Location loc = op.getLoc();
2082 ImplicitLocOpBuilder builder(loc, rewriter);
2083 auto input = op.getInput();
2084 auto inputTy = cast<RankedTensorType>(input.getType());
2085 auto resultTy = cast<RankedTensorType>(op.getType());
2086 const bool isBilinear = op.getMode() == ResizeMode::BILINEAR;
2088 auto inputH = inputTy.getDimSize(1);
2089 auto inputW = inputTy.getDimSize(2);
2090 auto outputH = resultTy.getDimSize(1);
2091 auto outputW = resultTy.getDimSize(2);
2093 if (inputH != 1 || inputW != 1 || outputH != 1 || outputW != 1)
2095 op,
"tosa.resize is not a pure 1x1->1x1 image operation");
2097 if (op.getMode() != ResizeMode::NEAREST_NEIGHBOR &&
2098 op.getMode() != ResizeMode::BILINEAR)
2100 op,
"tosa.resize mode should be NEAREST_NEIGHBOR or BILINEAR");
2102 if (inputTy == resultTy) {
2107 SmallVector<int64_t> scale;
2113 SmallVector<ReassociationExprs, 4> reassociationMap(2);
2120 RankedTensorType::get({inputTy.getDimSize(0), inputTy.getDimSize(3)},
2121 inputTy.getElementType());
2122 Value collapse = tensor::CollapseShapeOp::create(builder, collapseTy, input,
2126 llvm::SmallVector<Value> outputDynSize;
2127 if (inputTy.isDynamicDim(0))
2128 outputDynSize.push_back(tensor::DimOp::create(builder, input, 0));
2129 if (inputTy.isDynamicDim(3))
2130 outputDynSize.push_back(tensor::DimOp::create(builder, input, 3));
2133 auto genericTy = collapseTy.clone(resultTy.getElementType());
2135 tensor::EmptyOp::create(builder, genericTy.getShape(),
2136 resultTy.getElementType(), outputDynSize);
2138 SmallVector<utils::IteratorType> iterators(genericTy.getRank(),
2139 utils::IteratorType::parallel);
2141 auto generic = linalg::GenericOp::create(
2143 ArrayRef<AffineMap>{genericMap, genericMap}, iterators,
2144 [=](OpBuilder &
b, Location loc,
ValueRange args) {
2145 Value value = args[0];
2147 if (inputTy.getElementType() != resultTy.getElementType()) {
2148 value = arith::ExtSIOp::create(
b, loc, resultTy.getElementType(),
2151 if (isBilinear && scale[0] != 0) {
2152 Value scaleY = arith::ConstantOp::create(
2153 b, loc,
b.getI32IntegerAttr(scale[0]));
2154 value = arith::MulIOp::create(
b, loc, value, scaleY);
2157 if (isBilinear && scale[2] != 0) {
2158 Value scaleX = arith::ConstantOp::create(
2159 b, loc,
b.getI32IntegerAttr(scale[2]));
2160 value = arith::MulIOp::create(
b, loc, value, scaleX);
2164 linalg::YieldOp::create(
b, loc, value);
2168 op, resultTy,
generic.getResults()[0], reassociationMap);
2180 LogicalResult matchAndRewrite(tosa::ResizeOp op,
2184 auto input = op.getInput();
2185 auto inputTy = dyn_cast<RankedTensorType>(input.getType());
2186 auto resultTy = dyn_cast<RankedTensorType>(op.getType());
2188 if (!inputTy || !resultTy)
2190 "requires ranked input/output types");
2192 auto batch = inputTy.getDimSize(0);
2193 auto channels = inputTy.getDimSize(3);
2194 auto inputH = inputTy.getDimSize(1);
2195 auto inputW = inputTy.getDimSize(2);
2196 auto outputH = resultTy.getDimSize(1);
2197 auto outputW = resultTy.getDimSize(2);
2199 if ((inputH != 1 || outputH == 1) && (inputW != 1 || outputW == 1))
2201 op,
"tosa.resize has no broadcasting behavior");
2206 resizeShape.push_back(batch);
2207 resizeShape.push_back(inputH == 1 ? 1 : outputH);
2208 resizeShape.push_back(inputW == 1 ? 1 : outputW);
2209 resizeShape.push_back(channels);
2211 auto resizeTy = resultTy.clone(resizeShape);
2213 tosa::ResizeOp::create(builder, resizeTy, input, op.getScale(),
2214 op.getOffset(), op.getBorder(), op.getMode());
2221 reassociationMap.push_back({});
2224 reassociationMap.push_back({});
2229 collapseShape.push_back(outputH);
2231 collapseShape.push_back(outputW);
2232 collapseShape.push_back(channels);
2234 auto collapseTy = resultTy.clone(collapseShape);
2235 Value collapse = tensor::CollapseShapeOp::create(builder, collapseTy,
2236 resize, reassociationMap);
2240 if (inputTy.isDynamicDim(0))
2241 outputDynSize.push_back(tensor::DimOp::create(builder, input, 0));
2242 if (inputTy.isDynamicDim(3))
2243 outputDynSize.push_back(tensor::DimOp::create(builder, input, 3));
2246 utils::IteratorType::parallel);
2247 Value empty = tensor::EmptyOp::create(
2248 builder, resultTy.getShape(), resultTy.getElementType(), outputDynSize);
2265 Value value = args[0];
2266 linalg::YieldOp::create(
b, loc, value);
2275 using OpRewritePattern<tosa::ResizeOp>::OpRewritePattern;
2277 LogicalResult matchAndRewrite(tosa::ResizeOp op,
2278 PatternRewriter &rewriter)
const final {
2279 Location loc = op.getLoc();
2280 ImplicitLocOpBuilder
b(loc, rewriter);
2281 auto input = op.getInput();
2282 auto inputTy = cast<ShapedType>(input.getType());
2283 auto resultTy = cast<ShapedType>(op.getType());
2284 auto resultETy = resultTy.getElementType();
2286 bool floatingPointMode = isa<FloatType>(resultETy);
2287 auto floatTy = resultETy;
2289 auto imageH = inputTy.getShape()[1];
2290 auto imageW = inputTy.getShape()[2];
2292 auto dynamicDimsOr =
2294 if (!dynamicDimsOr.has_value())
2296 op,
"unable to get dynamic dimensions of tosa.resize");
2298 if (op.getMode() != ResizeMode::NEAREST_NEIGHBOR &&
2299 op.getMode() != ResizeMode::BILINEAR)
2301 op,
"tosa.resize mode should be NEAREST_NEIGHBOR or BILINEAR");
2303 SmallVector<AffineMap, 2> affineMaps = {
2305 auto emptyTensor = tensor::EmptyOp::create(
b, resultTy.getShape(),
2306 resultETy, *dynamicDimsOr);
2307 auto genericOp = linalg::GenericOp::create(
2310 Value resize = genericOp.getResult(0);
2313 OpBuilder::InsertionGuard regionGuard(
b);
2314 b.createBlock(&genericOp.getRegion(), genericOp.getRegion().end(),
2316 Value batch = linalg::IndexOp::create(
b, 0);
2317 Value y = linalg::IndexOp::create(
b, 1);
2318 Value x = linalg::IndexOp::create(
b, 2);
2319 Value channel = linalg::IndexOp::create(
b, 3);
2322 arith::ConstantOp::create(
b,
b.getZeroAttr(
b.getI32Type()));
2323 Value zeroFp = arith::ConstantOp::create(
b,
b.getZeroAttr(floatTy));
2325 arith::ConstantOp::create(
b,
b.getI32IntegerAttr(imageH - 1));
2327 arith::ConstantOp::create(
b,
b.getI32IntegerAttr(imageW - 1));
2329 Value inY = arith::IndexCastOp::create(
b,
b.getI32Type(), y);
2330 Value inX = arith::IndexCastOp::create(
b,
b.getI32Type(), x);
2332 SmallVector<int64_t> scale, offset, border;
2337 op,
"tosa.resize scale/offset/border should have compile time "
2338 "constant values.");
2341 Value yScaleN, yScaleD, xScaleN, xScaleD;
2342 yScaleN = arith::ConstantOp::create(
b,
b.getI32IntegerAttr(scale[0]));
2343 yScaleD = arith::ConstantOp::create(
b,
b.getI32IntegerAttr(scale[1]));
2344 xScaleN = arith::ConstantOp::create(
b,
b.getI32IntegerAttr(scale[2]));
2345 xScaleD = arith::ConstantOp::create(
b,
b.getI32IntegerAttr(scale[3]));
2347 Value yOffset, xOffset, yBorder, xBorder;
2348 yOffset = arith::ConstantOp::create(
b,
b.getI32IntegerAttr(offset[0]));
2349 xOffset = arith::ConstantOp::create(
b,
b.getI32IntegerAttr(offset[1]));
2350 yBorder = arith::ConstantOp::create(
b,
b.getI32IntegerAttr(border[0]));
2351 xBorder = arith::ConstantOp::create(
b,
b.getI32IntegerAttr(border[1]));
2354 auto getIndexAndDeltaFp = [&](Value &index, Value &delta, Value in,
2355 Value scaleN, Value scaleD, Value offset,
2356 int size, ImplicitLocOpBuilder &
b) {
2364 Value val = arith::MulIOp::create(
b, in, scaleD);
2365 val = arith::AddIOp::create(
b, val, offset);
2366 index = arith::FloorDivSIOp::create(
b, val, scaleN);
2369 Value scaledIndex = arith::MulIOp::create(
b, index, scaleN);
2370 Value r = arith::SubIOp::create(
b, val, scaledIndex);
2371 Value rFp = arith::SIToFPOp::create(
b, floatTy, r);
2374 Value scaleNfp = arith::UIToFPOp::create(
b, floatTy, scaleN);
2375 delta = arith::DivFOp::create(
b, rFp, scaleNfp);
2379 auto getIndexAndDeltaInt = [&](Value &index, Value &delta, Value in,
2380 Value scaleN, Value scaleD, Value offset,
2381 int size, ImplicitLocOpBuilder &
b) {
2390 Value val = arith::MulIOp::create(
b, in, scaleD);
2391 val = arith::AddIOp::create(
b, val, offset);
2392 index = arith::FloorDivSIOp::create(
b, val, scaleN);
2393 delta = arith::MulIOp::create(
b, index, scaleN);
2394 delta = arith::SubIOp::create(
b, val, delta);
2397 Value ix, iy, dx, dy;
2398 if (floatingPointMode) {
2399 getIndexAndDeltaFp(iy, dy, inY, yScaleN, yScaleD, yOffset, imageH,
b);
2400 getIndexAndDeltaFp(ix, dx, inX, xScaleN, xScaleD, xOffset, imageW,
b);
2402 getIndexAndDeltaInt(iy, dy, inY, yScaleN, yScaleD, yOffset, imageH,
b);
2403 getIndexAndDeltaInt(ix, dx, inX, xScaleN, xScaleD, xOffset, imageW,
b);
2406 if (op.getMode() == ResizeMode::NEAREST_NEIGHBOR) {
2407 auto one = arith::ConstantOp::create(
b,
b.getI32IntegerAttr(1));
2409 auto getNearestIndexAndClamp = [&](Value val, Value dval, Value scale,
2410 Value
max,
int size,
2411 ImplicitLocOpBuilder &
b) -> Value {
2417 if (floatingPointMode) {
2419 arith::ConstantOp::create(
b,
b.getFloatAttr(floatTy, 0.5f));
2420 pred = arith::CmpFOp::create(
b, arith::CmpFPredicate::OGE, dval, h);
2422 Value dvalDouble = arith::ShLIOp::create(
b, dval, one);
2423 pred = arith::CmpIOp::create(
b, arith::CmpIPredicate::sge,
2427 auto offset = arith::SelectOp::create(
b, pred, one, zeroI32);
2428 val = arith::AddIOp::create(
b, val, offset);
2430 return arith::IndexCastOp::create(
b,
b.getIndexType(), val);
2433 iy = getNearestIndexAndClamp(iy, dy, yScaleN, hMax, imageH,
b);
2434 ix = getNearestIndexAndClamp(ix, dx, xScaleN, wMax, imageW,
b);
2436 Value
result = tensor::ExtractOp::create(
2439 linalg::YieldOp::create(
b,
result);
2442 assert(op.getMode() == ResizeMode::BILINEAR);
2444 auto oneVal = arith::ConstantOp::create(
b,
b.getI32IntegerAttr(1));
2446 auto getClampedIdxs = [&](Value &val0, Value &val1,
int size, Value in,
2447 Value
max, ImplicitLocOpBuilder &
b) {
2449 val1 = arith::AddIOp::create(
b, val0, oneVal);
2454 val0 = arith::IndexCastOp::create(
b,
b.getIndexType(), val0);
2455 val1 = arith::IndexCastOp::create(
b,
b.getIndexType(), val1);
2463 Value x0, x1, y0, y1;
2464 getClampedIdxs(y0, y1, imageH, iy, hMax,
b);
2465 getClampedIdxs(x0, x1, imageW, ix, wMax,
b);
2467 Value y0x0 = tensor::ExtractOp::create(
2469 Value y0x1 = tensor::ExtractOp::create(
2471 Value y1x0 = tensor::ExtractOp::create(
2473 Value y1x1 = tensor::ExtractOp::create(
2476 if (floatingPointMode) {
2478 arith::ConstantOp::create(
b,
b.getFloatAttr(floatTy, 1.0f));
2479 auto interpolate = [&](Value val0, Value val1, Value delta,
2481 ImplicitLocOpBuilder &
b) -> Value {
2484 Value oneMinusDelta = arith::SubFOp::create(
b, oneVal, delta);
2485 Value mul0 = arith::MulFOp::create(
b, val0, oneMinusDelta);
2486 Value mul1 = arith::MulFOp::create(
b, val1, delta);
2487 return arith::AddFOp::create(
b, mul0, mul1);
2493 Value topAcc = interpolate(y0x0, y0x1, dx, imageW,
b);
2498 Value bottomAcc = interpolate(y1x0, y1x1, dx, imageW,
b);
2502 Value
result = interpolate(topAcc, bottomAcc, dy, imageH,
b);
2503 linalg::YieldOp::create(
b,
result);
2506 y0x0 = arith::ExtSIOp::create(
b, resultETy, y0x0);
2507 y0x1 = arith::ExtSIOp::create(
b, resultETy, y0x1);
2508 y1x0 = arith::ExtSIOp::create(
b, resultETy, y1x0);
2509 y1x1 = arith::ExtSIOp::create(
b, resultETy, y1x1);
2512 if (resultETy.getIntOrFloatBitWidth() > deltaBitwidth) {
2513 dx = arith::ExtSIOp::create(
b, resultETy, dx);
2514 dy = arith::ExtSIOp::create(
b, resultETy, dy);
2517 Value yScaleNExt = yScaleN;
2518 Value xScaleNExt = xScaleN;
2520 const int64_t scaleBitwidth =
2522 if (resultETy.getIntOrFloatBitWidth() > scaleBitwidth) {
2523 yScaleNExt = arith::ExtSIOp::create(
b, resultETy, yScaleN);
2524 xScaleNExt = arith::ExtSIOp::create(
b, resultETy, xScaleN);
2527 auto interpolate = [](Value val0, Value val1, Value weight1,
2528 Value scale,
int inputSize,
2529 ImplicitLocOpBuilder &
b) -> Value {
2531 return arith::MulIOp::create(
b, val0, scale);
2532 Value weight0 = arith::SubIOp::create(
b, scale, weight1);
2533 Value mul0 = arith::MulIOp::create(
b, val0, weight0);
2534 Value mul1 = arith::MulIOp::create(
b, val1, weight1);
2535 return arith::AddIOp::create(
b, mul0, mul1);
2538 Value topAcc = interpolate(y0x0, y0x1, dx, xScaleNExt, imageW,
b);
2539 Value bottomAcc = interpolate(y1x0, y1x1, dx, xScaleNExt, imageW,
b);
2541 interpolate(topAcc, bottomAcc, dy, yScaleNExt, imageH,
b);
2542 linalg::YieldOp::create(
b,
result);
2555template <
typename SrcOp>
2558 using OpRewritePattern<SrcOp>::OpRewritePattern;
2560 LogicalResult matchAndRewrite(SrcOp op,
2561 PatternRewriter &rewriter)
const final {
2562 rewriter.
replaceOp(op, op.getOperation()->getOperands());
2567template <
typename SrcOp>
2570 ReduceConverter(MLIRContext *context,
bool allowNonFinites)
2571 : OpRewritePattern<SrcOp>(context), allowNonFinites(allowNonFinites) {}
2573 LogicalResult matchAndRewrite(SrcOp reduceOp,
2574 PatternRewriter &rewriter)
const final {
2580 bool allowNonFinites;
2585 using OpRewritePattern<tosa::ReverseOp>::OpRewritePattern;
2587 LogicalResult matchAndRewrite(tosa::ReverseOp op,
2588 PatternRewriter &rewriter)
const final {
2589 auto loc = op.getLoc();
2590 Value input = op.getInput1();
2591 auto inputTy = cast<ShapedType>(input.
getType());
2592 auto resultTy = cast<ShapedType>(op.getType());
2593 auto axis = op.getAxis();
2595 SmallVector<Value> dynDims;
2596 for (
int i = 0; i < inputTy.getRank(); i++) {
2597 if (inputTy.isDynamicDim(i)) {
2598 dynDims.push_back(tensor::DimOp::create(rewriter, loc, input, i));
2602 Value axisDimSize = tensor::DimOp::create(rewriter, loc, input, axis);
2605 auto emptyTensor = tensor::EmptyOp::create(
2606 rewriter, loc, inputTy.getShape(),
2607 inputTy.getElementType(), ArrayRef<Value>({dynDims}))
2609 SmallVector<AffineMap, 2> affineMaps = {
2613 op, resultTy, ArrayRef<Value>({}),
ValueRange{emptyTensor}, affineMaps,
2615 [&](OpBuilder &nestedBuilder, Location nestedLoc,
ValueRange args) {
2616 llvm::SmallVector<Value>
indices;
2617 for (
unsigned int i = 0; i < inputTy.getRank(); i++) {
2619 linalg::IndexOp::create(rewriter, nestedLoc, i).getResult();
2623 arith::SubIOp::create(rewriter, nestedLoc, axisDimSize, one);
2624 index = arith::SubIOp::create(rewriter, nestedLoc, sizeMinusOne,
2631 auto extract = tensor::ExtractOp::create(nestedBuilder, nestedLoc,
2633 linalg::YieldOp::create(nestedBuilder, op.getLoc(),
2634 extract.getResult());
2644struct TileConverter :
public OpConversionPattern<tosa::TileOp> {
2645 using OpConversionPattern<tosa::TileOp>::OpConversionPattern;
2648 matchAndRewrite(tosa::TileOp op, OpAdaptor adaptor,
2649 ConversionPatternRewriter &rewriter)
const override {
2650 auto loc = op.getLoc();
2651 auto input = op.getInput1();
2652 auto inputTy = cast<ShapedType>(input.
getType());
2653 auto inputShape = inputTy.getShape();
2654 auto resultTy = cast<ShapedType>(op.getType());
2655 auto elementTy = inputTy.getElementType();
2656 int64_t rank = inputTy.getRank();
2658 SmallVector<int64_t> multiples;
2659 if (
failed(op.getConstantMultiples(multiples)))
2663 SmallVector<int64_t, 2> genericShape;
2664 for (
int i = 0; i < rank; i++) {
2665 int64_t dim = multiples[i];
2666 genericShape.push_back(dim == -1 ? ShapedType::kDynamic : dim);
2667 genericShape.push_back(inputShape[i]);
2670 SmallVector<Value> dynDims;
2671 for (
int i = 0; i < inputTy.getRank(); i++) {
2672 if (inputTy.isDynamicDim(i) || multiples[i] == -1) {
2673 dynDims.push_back(tensor::DimOp::create(rewriter, loc, input, i));
2677 auto emptyTensor = tensor::EmptyOp::create(
2678 rewriter, op.getLoc(), genericShape, elementTy, dynDims);
2681 SmallVector<AffineExpr, 4> dimExprs;
2682 dimExprs.reserve(rank);
2683 for (
unsigned i = 0; i < rank; ++i)
2684 dimExprs.push_back(rewriter.getAffineDimExpr(i * 2 + 1));
2686 auto readAffineMap =
2688 rewriter.getContext());
2690 SmallVector<AffineMap, 2> affineMaps = {
2691 readAffineMap, rewriter.getMultiDimIdentityMap(genericShape.size())};
2693 auto genericOp = linalg::GenericOp::create(
2694 rewriter, loc, RankedTensorType::get(genericShape, elementTy), input,
2697 [&](OpBuilder &nestedBuilder, Location nestedLoc,
ValueRange args) {
2698 linalg::YieldOp::create(nestedBuilder, op.getLoc(), *args.begin());
2703 rewriter.replaceOpWithNewOp<tosa::ReshapeOp>(
2704 op, resultTy, genericOp.getResult(0), shapeValue);
2722template <
typename SrcOp>
2725 ArgMinMaxConverter(MLIRContext *context,
bool allowNonFinites)
2726 : OpRewritePattern<SrcOp>(context), allowNonFinites(allowNonFinites) {}
2728 LogicalResult matchAndRewrite(SrcOp argOp,
2729 PatternRewriter &rewriter)
const final {
2730 constexpr bool isMin = std::is_same_v<SrcOp, tosa::ArgMinOp>;
2732 auto loc = argOp.getLoc();
2733 Value input = argOp.getInput();
2734 auto inputTy = cast<ShapedType>(input.
getType());
2735 auto resultTy = cast<ShapedType>(argOp.getOutput().getType());
2736 auto inElementTy = inputTy.getElementType();
2737 auto outElementTy = resultTy.getElementType();
2738 int axis = argOp.getAxis();
2739 auto resultValueTy =
2740 RankedTensorType::get(resultTy.getShape(), inElementTy);
2742 if (!isa<IntegerType>(outElementTy))
2743 return rewriter.notifyMatchFailure(argOp,
2744 "requires integer-like result type");
2746 SmallVector<Value> dynDims;
2747 for (
int i = 0; i < inputTy.getRank(); i++) {
2748 if (inputTy.isDynamicDim(i) && i != axis) {
2749 dynDims.push_back(tensor::DimOp::create(rewriter, loc, input, i));
2754 auto emptyTensorIdx =
2755 tensor::EmptyOp::create(rewriter, loc, resultTy.getShape(),
2756 outElementTy, dynDims)
2758 auto fillValueIdx = arith::ConstantOp::create(
2759 rewriter, loc, rewriter.getIntegerAttr(outElementTy, 0));
2760 auto filledTensorIdx =
2761 linalg::FillOp::create(rewriter, loc,
ValueRange{fillValueIdx},
2766 auto emptyTensorValue =
2767 tensor::EmptyOp::create(rewriter, loc, resultTy.getShape(), inElementTy,
2771 argOp, inElementTy, rewriter, allowNonFinites);
2774 return rewriter.notifyMatchFailure(argOp,
"unsupported element type");
2776 auto fillValue = arith::ConstantOp::create(rewriter, loc, fillValueAttr);
2777 auto filledTensorValue =
2778 linalg::FillOp::create(rewriter, loc,
ValueRange{fillValue},
2784 SmallVector<utils::IteratorType, 4> iteratorTypes;
2785 iteratorTypes.resize(inputTy.getRank(), utils::IteratorType::parallel);
2786 iteratorTypes[axis] = utils::IteratorType::reduction;
2788 SmallVector<AffineExpr, 2> srcExprs;
2789 SmallVector<AffineExpr, 2> dstExprs;
2790 for (
int i = 0, rank = inputTy.getRank(); i != rank; ++i) {
2796 bool didEncounterError =
false;
2798 rewriter.getContext());
2799 auto linalgOp = linalg::GenericOp::create(
2800 rewriter, loc, ArrayRef<Type>({resultTy, resultValueTy}), input,
2801 ValueRange({filledTensorIdx, filledTensorValue}), maps, iteratorTypes,
2802 [&](OpBuilder &nestedBuilder, Location nestedLoc,
2804 auto newValue = blockArgs[0];
2805 auto oldIndex = blockArgs[1];
2806 auto oldValue = blockArgs[2];
2808 Value newIndex = arith::IndexCastOp::create(
2809 rewriter, nestedLoc, oldIndex.getType(),
2810 linalg::IndexOp::create(rewriter, loc, axis));
2813 if (isa<FloatType>(inElementTy)) {
2814 if (argOp.getNanMode() == NanPropagationMode::IGNORE) {
2817 predicate = arith::CmpFOp::create(
2818 rewriter, nestedLoc,
2819 isMin ? arith::CmpFPredicate::OLT : arith::CmpFPredicate::OGT,
2820 newValue, oldValue);
2825 Value comparison = arith::CmpFOp::create(
2826 rewriter, nestedLoc,
2827 isMin ? arith::CmpFPredicate::ULT : arith::CmpFPredicate::UGT,
2828 newValue, oldValue);
2829 Value oldNonNaN = arith::CmpFOp::create(rewriter, nestedLoc,
2830 arith::CmpFPredicate::ORD,
2831 oldValue, oldValue);
2832 predicate = arith::AndIOp::create(rewriter, nestedLoc,
2833 rewriter.getI1Type(),
2834 comparison, oldNonNaN);
2836 }
else if (isa<IntegerType>(inElementTy)) {
2837 predicate = arith::CmpIOp::create(rewriter, nestedLoc,
2838 isMin ? arith::CmpIPredicate::slt
2839 : arith::CmpIPredicate::sgt,
2840 newValue, oldValue);
2842 didEncounterError =
true;
2846 auto resultValue = arith::SelectOp::create(
2847 rewriter, nestedLoc, predicate, newValue, oldValue);
2848 auto resultIndex = arith::SelectOp::create(
2849 rewriter, nestedLoc, predicate, newIndex, oldIndex);
2850 linalg::YieldOp::create(nestedBuilder, nestedLoc,
2854 if (didEncounterError)
2855 return rewriter.notifyMatchFailure(argOp,
"unsupported element type");
2857 rewriter.replaceOp(argOp, linalgOp.getResult(0));
2862 bool allowNonFinites;
2865class GatherConverter :
public OpConversionPattern<tosa::GatherOp> {
2867 using OpConversionPattern<tosa::GatherOp>::OpConversionPattern;
2869 matchAndRewrite(tosa::GatherOp op, OpAdaptor adaptor,
2870 ConversionPatternRewriter &rewriter)
const final {
2871 auto input = adaptor.getOperands()[0];
2872 auto indices = adaptor.getOperands()[1];
2874 auto valuesTy = dyn_cast<RankedTensorType>(op.getValues().getType());
2875 auto resultTy = dyn_cast<RankedTensorType>(op.getType());
2876 if (!valuesTy || !resultTy)
2877 return rewriter.notifyMatchFailure(op,
"unranked tensors not supported");
2879 auto dynamicDims = inferDynamicDimsForGather(
2880 rewriter, op.getLoc(), adaptor.getValues(), adaptor.getIndices());
2882 auto resultElementTy = resultTy.getElementType();
2884 auto loc = op.getLoc();
2886 tensor::EmptyOp::create(rewriter, loc, resultTy.getShape(),
2887 resultElementTy, dynamicDims)
2890 SmallVector<AffineMap, 2> affineMaps = {
2892 resultTy.getRank(), 0,
2893 {rewriter.getAffineDimExpr(0), rewriter.getAffineDimExpr(1)},
2894 rewriter.getContext()),
2895 rewriter.getMultiDimIdentityMap(resultTy.getRank())};
2897 auto genericOp = linalg::GenericOp::create(
2901 [&](OpBuilder &
b, Location loc,
ValueRange args) {
2902 auto indexValue = args[0];
2903 auto index0 = linalg::IndexOp::create(rewriter, loc, 0);
2904 Value index1 = arith::IndexCastOp::create(
2905 rewriter, loc, rewriter.getIndexType(), indexValue);
2906 auto index2 = linalg::IndexOp::create(rewriter, loc, 2);
2907 Value extract = tensor::ExtractOp::create(
2908 rewriter, loc, input,
ValueRange{index0, index1, index2});
2909 linalg::YieldOp::create(rewriter, loc, extract);
2911 rewriter.replaceOp(op, genericOp.getResult(0));
2915 static llvm::SmallVector<Value> inferDynamicDimsForGather(OpBuilder &builder,
2919 llvm::SmallVector<Value> results;
2921 auto addDynamicDimension = [&](Value source, int64_t dim) {
2923 if (
auto dimValue = llvm::dyn_cast_if_present<Value>(sz))
2924 results.push_back(dimValue);
2927 addDynamicDimension(values, 0);
2928 addDynamicDimension(
indices, 1);
2929 addDynamicDimension(values, 2);
2934class RowGatherConverter :
public OpConversionPattern<tosa::RowGatherOp> {
2936 using OpConversionPattern<tosa::RowGatherOp>::OpConversionPattern;
2938 matchAndRewrite(tosa::RowGatherOp op, OpAdaptor adaptor,
2939 ConversionPatternRewriter &rewriter)
const final {
2940 auto valuesTy = dyn_cast<RankedTensorType>(adaptor.getValues().getType());
2941 auto indicesTy = dyn_cast<RankedTensorType>(adaptor.getIndices().getType());
2943 dyn_cast<RankedTensorType>(adaptor.getRowCount().getType());
2944 auto resultTy = dyn_cast<RankedTensorType>(op.getType());
2945 if (!valuesTy || !indicesTy || !rowCountTy || !resultTy)
2946 return rewriter.notifyMatchFailure(op,
"unranked tensors not supported");
2948 Location loc = op.getLoc();
2950 Value rowCount = tensor::ExtractOp::create(
2951 rewriter, loc, adaptor.getRowCount(),
ValueRange{zero});
2952 Value rowCountIndex = arith::IndexCastOp::create(
2953 rewriter, loc, rewriter.getIndexType(), rowCount);
2955 SmallVector<Value> dynamicDims;
2956 if (resultTy.isDynamicDim(0))
2957 dynamicDims.push_back(
2958 tensor::DimOp::create(rewriter, loc, adaptor.getValues(), 0));
2959 if (resultTy.isDynamicDim(1)) {
2960 Value indicesWidth =
2961 tensor::DimOp::create(rewriter, loc, adaptor.getIndices(), 1);
2962 dynamicDims.push_back(
2963 arith::MulIOp::create(rewriter, loc, indicesWidth, rowCountIndex));
2965 if (resultTy.isDynamicDim(2))
2966 dynamicDims.push_back(
2967 tensor::DimOp::create(rewriter, loc, adaptor.getValues(), 2));
2970 tensor::EmptyOp::create(rewriter, loc, resultTy.getShape(),
2971 resultTy.getElementType(), dynamicDims);
2972 SmallVector<AffineMap> affineMaps = {
2973 rewriter.getMultiDimIdentityMap(resultTy.getRank())};
2975 auto genericOp = linalg::GenericOp::create(
2976 rewriter, loc, ArrayRef<Type>{resultTy},
ValueRange{},
2979 [&](OpBuilder &builder, Location nestedLoc,
ValueRange) {
2980 Value batch = linalg::IndexOp::create(builder, nestedLoc, 0);
2981 Value outputRow = linalg::IndexOp::create(builder, nestedLoc, 1);
2982 Value channel = linalg::IndexOp::create(builder, nestedLoc, 2);
2983 Value indexSlot = arith::DivUIOp::create(builder, nestedLoc,
2984 outputRow, rowCountIndex);
2985 Value rowOffset = arith::RemUIOp::create(builder, nestedLoc,
2986 outputRow, rowCountIndex);
2987 Value index = tensor::ExtractOp::create(builder, nestedLoc,
2988 adaptor.getIndices(),
2990 Value row = arith::IndexCastOp::create(builder, nestedLoc,
2991 builder.getIndexType(), index);
2992 row = arith::AddIOp::create(builder, nestedLoc, row, rowOffset);
2994 tensor::ExtractOp::create(builder, nestedLoc, adaptor.getValues(),
2996 linalg::YieldOp::create(builder, nestedLoc,
result);
2998 rewriter.replaceOp(op, genericOp.getResult(0));
3008 using OpRewritePattern<tosa::TableOp>::OpRewritePattern;
3010 LogicalResult matchAndRewrite(tosa::TableOp op,
3011 PatternRewriter &rewriter)
const final {
3012 auto loc = op.getLoc();
3013 Value input = op.getInput1();
3014 Value table = op.getTable();
3015 auto inputTy = cast<ShapedType>(input.
getType());
3016 auto tableTy = cast<ShapedType>(table.
getType());
3017 auto resultTy = cast<ShapedType>(op.getType());
3019 auto inputElementTy = inputTy.getElementType();
3020 auto tableElementTy = tableTy.getElementType();
3021 auto resultElementTy = resultTy.getElementType();
3023 SmallVector<Value> dynDims;
3024 for (
int i = 0; i < resultTy.getRank(); ++i) {
3025 if (inputTy.isDynamicDim(i)) {
3027 tensor::DimOp::create(rewriter, loc, op.getOperand(0), i));
3032 tensor::EmptyOp::create(rewriter, loc, resultTy.getShape(),
3033 resultElementTy, dynDims)
3036 SmallVector<AffineMap, 2> affineMaps = {
3037 rewriter.getMultiDimIdentityMap(resultTy.getRank()),
3038 rewriter.getMultiDimIdentityMap(resultTy.getRank())};
3040 auto genericOp = linalg::GenericOp::create(
3043 rewriter.replaceOp(op, genericOp.getResult(0));
3046 OpBuilder::InsertionGuard regionGuard(rewriter);
3047 Block *block = rewriter.createBlock(
3048 &genericOp.getRegion(), genericOp.getRegion().end(),
3049 TypeRange({inputElementTy, resultElementTy}), {loc, loc});
3052 rewriter.setInsertionPointToStart(block);
3053 if (inputElementTy.isInteger(8) && tableElementTy.isInteger(8) &&
3054 resultElementTy.isInteger(8)) {
3055 Value index = arith::IndexCastOp::create(
3056 rewriter, loc, rewriter.getIndexType(), inputValue);
3058 index = arith::AddIOp::create(rewriter, loc, rewriter.getIndexType(),
3061 tensor::ExtractOp::create(rewriter, loc, table,
ValueRange{index});
3062 linalg::YieldOp::create(rewriter, loc, extract);
3066 if (inputElementTy.isInteger(16) && tableElementTy.isInteger(16) &&
3067 resultElementTy.isInteger(32)) {
3068 Value extend = arith::ExtSIOp::create(
3069 rewriter, loc, rewriter.getI32Type(), inputValue);
3071 auto offset = arith::ConstantOp::create(
3072 rewriter, loc, rewriter.getI32IntegerAttr(32768));
3073 auto seven = arith::ConstantOp::create(rewriter, loc,
3074 rewriter.getI32IntegerAttr(7));
3075 auto one = arith::ConstantOp::create(rewriter, loc,
3076 rewriter.getI32IntegerAttr(1));
3077 auto b1111111 = arith::ConstantOp::create(
3078 rewriter, loc, rewriter.getI32IntegerAttr(127));
3084 auto extendAdd = arith::AddIOp::create(rewriter, loc, extend, offset);
3085 Value index = arith::ShRUIOp::create(rewriter, loc, extendAdd, seven);
3087 arith::AndIOp::create(rewriter, loc, extendAdd, b1111111);
3092 Value indexPlusOne = arith::AddIOp::create(rewriter, loc, index, one);
3094 index = arith::IndexCastOp::create(rewriter, loc,
3095 rewriter.getIndexType(), index);
3096 indexPlusOne = arith::IndexCastOp::create(
3097 rewriter, loc, rewriter.getIndexType(), indexPlusOne);
3100 tensor::ExtractOp::create(rewriter, loc, table,
ValueRange{index});
3101 Value
next = tensor::ExtractOp::create(rewriter, loc, table,
3105 arith::ExtSIOp::create(rewriter, loc, rewriter.getI32Type(), base);
3107 arith::ExtSIOp::create(rewriter, loc, rewriter.getI32Type(), next);
3111 Value baseScaled = arith::ShLIOp::create(rewriter, loc, base, seven);
3112 Value diff = arith::SubIOp::create(rewriter, loc, next, base);
3113 Value diffScaled = arith::MulIOp::create(rewriter, loc, diff, fraction);
3115 arith::AddIOp::create(rewriter, loc, baseScaled, diffScaled);
3117 linalg::YieldOp::create(rewriter, loc,
result);
3123 return rewriter.notifyMatchFailure(
3124 op,
"unable to create body for tosa.table op");
3129 using OpRewritePattern<RFFT2dOp>::OpRewritePattern;
3131 static bool isRankedTensor(Type type) {
return isa<RankedTensorType>(type); }
3133 static OpFoldResult halfPlusOne(OpBuilder &builder, Location loc,
3139 auto divBy2 = builder.
createOrFold<arith::DivUIOp>(loc, value, two);
3140 auto plusOne = builder.
createOrFold<arith::AddIOp>(loc, divBy2, one);
3144 static RankedTensorType
3145 computeOutputShape(OpBuilder &builder, Location loc, Value input,
3146 llvm::SmallVectorImpl<Value> &dynamicSizes) {
3152 dims[2] = halfPlusOne(builder, loc, dims[2]);
3154 llvm::SmallVector<int64_t, 3> staticSizes;
3157 auto elementType = cast<RankedTensorType>(input.
getType()).getElementType();
3158 return RankedTensorType::get(staticSizes, elementType);
3161 static Value createZeroTensor(PatternRewriter &rewriter, Location loc,
3162 RankedTensorType type,
3163 llvm::ArrayRef<Value> dynamicSizes) {
3165 tensor::EmptyOp::create(rewriter, loc, type, dynamicSizes);
3166 auto fillValueAttr = rewriter.
getZeroAttr(type.getElementType());
3167 auto fillValue = arith::ConstantOp::create(rewriter, loc, fillValueAttr);
3169 linalg::FillOp::create(rewriter, loc,
ValueRange{fillValue},
3172 return filledTensor;
3175 static Value castIndexToFloat(OpBuilder &builder, Location loc,
3176 FloatType type, Value value) {
3177 auto integerVal = arith::IndexCastUIOp::create(
3179 type.getIntOrFloatBitWidth() > 32 ? builder.
getI64Type()
3183 return arith::UIToFPOp::create(builder, loc, type, integerVal);
3186 static Value createLinalgIndex(OpBuilder &builder, Location loc,
3187 FloatType type, int64_t index) {
3188 auto indexVal = linalg::IndexOp::create(builder, loc, index);
3189 return castIndexToFloat(builder, loc, type, indexVal);
3192 template <
typename... Args>
3193 static llvm::SmallVector<AffineExpr, 4> affineDimsExpr(OpBuilder &builder,
3198 LogicalResult matchAndRewrite(RFFT2dOp rfft2d,
3199 PatternRewriter &rewriter)
const override {
3200 if (!llvm::all_of(rfft2d->getOperandTypes(), isRankedTensor) ||
3201 !llvm::all_of(rfft2d->getResultTypes(), isRankedTensor)) {
3203 "only supports ranked tensors");
3206 auto loc = rfft2d.getLoc();
3207 auto input = rfft2d.getInputReal();
3209 dyn_cast<FloatType>(cast<ShapedType>(input.
getType()).getElementType());
3212 "only supports float element types");
3215 llvm::SmallVector<Value> dynamicSizes;
3216 auto outputType = computeOutputShape(rewriter, loc, input, dynamicSizes);
3219 llvm::SmallVector<utils::IteratorType, 5> iteratorTypes = {
3220 utils::IteratorType::parallel, utils::IteratorType::parallel,
3221 utils::IteratorType::parallel, utils::IteratorType::reduction,
3222 utils::IteratorType::reduction};
3225 llvm::SmallVector<Value> genericOpInputs = {input};
3226 llvm::SmallVector<Value> genericOpOutputs = {
3227 createZeroTensor(rewriter, loc, outputType, dynamicSizes),
3228 createZeroTensor(rewriter, loc, outputType, dynamicSizes)};
3232 llvm::ArrayRef{affineDimsExpr(rewriter, 0, 3, 4),
3233 affineDimsExpr(rewriter, 0, 1, 2),
3234 affineDimsExpr(rewriter, 0, 1, 2)},
3238 auto dimH = rewriter.
createOrFold<tensor::DimOp>(loc, input, 1);
3239 auto dimW = rewriter.
createOrFold<tensor::DimOp>(loc, input, 2);
3242 auto zeroFloat = arith::ConstantOp::create(
3243 rewriter, loc, rewriter.
getZeroAttr(elementType));
3244 auto twoPiAttr = rewriter.
getFloatAttr(elementType, 6.283185307179586);
3245 auto twoPi = arith::ConstantOp::create(rewriter, loc, twoPiAttr);
3250 auto constH = castIndexToFloat(rewriter, loc, elementType, dimH);
3251 auto constW = castIndexToFloat(rewriter, loc, elementType, dimW);
3252 auto halfH = index::DivUOp::create(rewriter, loc, dimH, twoIndex);
3253 auto halfW = index::DivUOp::create(rewriter, loc, dimW, twoIndex);
3255 auto buildBody = [&](OpBuilder &builder, Location loc,
ValueRange args) {
3256 Value valReal = args[0];
3257 Value sumReal = args[1];
3258 Value sumImag = args[2];
3261 Value oy = linalg::IndexOp::create(builder, loc, 1);
3262 Value ox = linalg::IndexOp::create(builder, loc, 2);
3263 Value iy = linalg::IndexOp::create(builder, loc, 3);
3264 Value ix = linalg::IndexOp::create(builder, loc, 4);
3269 auto iyXoy = index::MulOp::create(builder, loc, iy, oy);
3270 auto ixXox = index::MulOp::create(builder, loc, ix, ox);
3272 auto iyRem = index::RemUOp::create(builder, loc, iyXoy, dimH);
3273 auto ixRem = index::RemUOp::create(builder, loc, ixXox, dimW);
3275 auto iyRemFloat = castIndexToFloat(builder, loc, elementType, iyRem);
3276 auto ixRemFloat = castIndexToFloat(builder, loc, elementType, ixRem);
3278 auto yComponent = arith::DivFOp::create(builder, loc, iyRemFloat, constH);
3279 auto xComponent = arith::DivFOp::create(builder, loc, ixRemFloat, constW);
3280 auto sumXY = arith::AddFOp::create(builder, loc, yComponent, xComponent);
3281 auto angle = arith::MulFOp::create(builder, loc, twoPi, sumXY);
3288 auto iyIs0 = arith::CmpIOp::create(builder, loc, arith::CmpIPredicate::eq,
3290 auto iyIsHalfH = arith::CmpIOp::create(
3291 builder, loc, arith::CmpIPredicate::eq, iyRem, halfH);
3292 auto ixIs0 = arith::CmpIOp::create(builder, loc, arith::CmpIPredicate::eq,
3294 auto ixIsHalfW = arith::CmpIOp::create(
3295 builder, loc, arith::CmpIPredicate::eq, ixRem, halfW);
3297 auto iyIsSinSkippable =
3298 arith::OrIOp::create(builder, loc, iyIs0, iyIsHalfH);
3299 auto ixIsSinSkippable =
3300 arith::OrIOp::create(builder, loc, ixIs0, ixIsHalfW);
3301 auto shouldSkipSin = arith::AndIOp::create(builder, loc, iyIsSinSkippable,
3306 auto cosAngle = math::CosOp::create(builder, loc, angle);
3307 auto sinAngle = math::SinOp::create(builder, loc, angle);
3308 auto imagWeight = arith::SelectOp::create(builder, loc, shouldSkipSin,
3309 zeroFloat, sinAngle);
3310 auto realComponent =
3311 arith::MulFOp::create(builder, loc, valReal, cosAngle);
3312 auto imagComponent =
3313 arith::MulFOp::create(builder, loc, valReal, imagWeight);
3318 arith::AddFOp::create(builder, loc, sumReal, realComponent);
3320 arith::SubFOp::create(builder, loc, sumImag, imagComponent);
3322 linalg::YieldOp::create(builder, loc,
ValueRange{outReal, outImag});
3326 rfft2d, rfft2d.getResultTypes(), genericOpInputs, genericOpOutputs,
3327 indexingMaps, iteratorTypes, buildBody);
3336 LogicalResult matchAndRewrite(FFT2dOp fft2d,
3337 PatternRewriter &rewriter)
const override {
3338 if (!llvm::all_of(fft2d->getOperandTypes(),
3339 RFFT2dConverter::isRankedTensor) ||
3340 !llvm::all_of(fft2d->getResultTypes(),
3341 RFFT2dConverter::isRankedTensor)) {
3345 Location loc = fft2d.getLoc();
3346 Value input_real = fft2d.getInputReal();
3347 Value input_imag = fft2d.getInputImag();
3348 BoolAttr inverse = fft2d.getInverseAttr();
3350 auto real_el_ty = cast<FloatType>(
3351 cast<ShapedType>(input_real.
getType()).getElementType());
3352 [[maybe_unused]]
auto imag_el_ty = cast<FloatType>(
3353 cast<ShapedType>(input_imag.
getType()).getElementType());
3355 assert(real_el_ty == imag_el_ty);
3358 SmallVector<Value> dynamicSizes;
3363 SmallVector<int64_t, 3> staticSizes;
3366 auto outputType = RankedTensorType::get(staticSizes, real_el_ty);
3369 SmallVector<utils::IteratorType, 5> iteratorTypes = {
3370 utils::IteratorType::parallel, utils::IteratorType::parallel,
3371 utils::IteratorType::parallel, utils::IteratorType::reduction,
3372 utils::IteratorType::reduction};
3375 SmallVector<Value> genericOpInputs = {input_real, input_imag};
3376 SmallVector<Value> genericOpOutputs = {
3377 RFFT2dConverter::createZeroTensor(rewriter, loc, outputType,
3379 RFFT2dConverter::createZeroTensor(rewriter, loc, outputType,
3384 ArrayRef{RFFT2dConverter::affineDimsExpr(rewriter, 0, 3, 4),
3385 RFFT2dConverter::affineDimsExpr(rewriter, 0, 3, 4),
3386 RFFT2dConverter::affineDimsExpr(rewriter, 0, 1, 2),
3387 RFFT2dConverter::affineDimsExpr(rewriter, 0, 1, 2)},
3391 auto dimH = rewriter.
createOrFold<tensor::DimOp>(loc, input_real, 1);
3392 auto dimW = rewriter.
createOrFold<tensor::DimOp>(loc, input_real, 2);
3395 auto twoPiAttr = rewriter.
getFloatAttr(real_el_ty, 6.283185307179586);
3396 auto twoPi = arith::ConstantOp::create(rewriter, loc, twoPiAttr);
3398 RFFT2dConverter::castIndexToFloat(rewriter, loc, real_el_ty, dimH);
3400 RFFT2dConverter::castIndexToFloat(rewriter, loc, real_el_ty, dimW);
3402 auto buildBody = [&](OpBuilder &builder, Location loc,
ValueRange args) {
3403 Value valReal = args[0];
3404 Value valImag = args[1];
3405 Value sumReal = args[2];
3406 Value sumImag = args[3];
3409 Value oy = linalg::IndexOp::create(builder, loc, 1);
3410 Value ox = linalg::IndexOp::create(builder, loc, 2);
3411 Value iy = linalg::IndexOp::create(builder, loc, 3);
3412 Value ix = linalg::IndexOp::create(builder, loc, 4);
3416 auto iyXoy = index::MulOp::create(builder, loc, iy, oy);
3417 auto ixXox = index::MulOp::create(builder, loc, ix, ox);
3419 auto iyRem = index::RemUOp::create(builder, loc, iyXoy, dimH);
3420 auto ixRem = index::RemUOp::create(builder, loc, ixXox, dimW);
3423 RFFT2dConverter::castIndexToFloat(builder, loc, real_el_ty, iyRem);
3425 RFFT2dConverter::castIndexToFloat(builder, loc, real_el_ty, ixRem);
3427 auto yComponent = arith::DivFOp::create(builder, loc, iyRemFloat, constH);
3428 auto xComponent = arith::DivFOp::create(builder, loc, ixRemFloat, constW);
3430 auto sumXY = arith::AddFOp::create(builder, loc, yComponent, xComponent);
3431 auto angle = arith::MulFOp::create(builder, loc, twoPi, sumXY);
3434 angle = arith::MulFOp::create(
3435 builder, loc, angle,
3436 arith::ConstantOp::create(rewriter, loc,
3442 auto cosAngle = math::CosOp::create(builder, loc, angle);
3443 auto sinAngle = math::SinOp::create(builder, loc, angle);
3445 auto rcos = arith::MulFOp::create(builder, loc, valReal, cosAngle);
3446 auto rsin = arith::MulFOp::create(builder, loc, valImag, sinAngle);
3447 auto realComponent = arith::AddFOp::create(builder, loc, rcos, rsin);
3449 auto icos = arith::MulFOp::create(builder, loc, valImag, cosAngle);
3450 auto isin = arith::MulFOp::create(builder, loc, valReal, sinAngle);
3452 auto imagComponent = arith::SubFOp::create(builder, loc, icos, isin);
3457 arith::AddFOp::create(builder, loc, sumReal, realComponent);
3459 arith::AddFOp::create(builder, loc, sumImag, imagComponent);
3461 linalg::YieldOp::create(builder, loc,
ValueRange{outReal, outImag});
3465 fft2d, fft2d.getResultTypes(), genericOpInputs, genericOpOutputs,
3466 indexingMaps, iteratorTypes, buildBody);
3476 const TosaToLinalgOptions &
options) {
3479 patterns->
add<GenericResizeConverter>(patterns->
getContext(),
3483 patterns->
add<MaterializeResizeBroadcast>(patterns->
getContext(),
3488 PointwiseConverter<tosa::AddOp>,
3489 PointwiseConverter<tosa::SubOp>,
3490 PointwiseConverter<tosa::MulOp>,
3491 PointwiseConverter<tosa::IntDivOp>,
3492 PointwiseConverter<tosa::NegateOp>,
3493 PointwiseConverter<tosa::PowOp>,
3494 PointwiseConverter<tosa::ReciprocalOp>,
3495 PointwiseConverter<tosa::RsqrtOp>,
3496 PointwiseConverter<tosa::LogOp>,
3497 PointwiseConverter<tosa::ExpOp>,
3498 PointwiseConverter<tosa::AbsOp>,
3499 PointwiseConverter<tosa::SinOp>,
3500 PointwiseConverter<tosa::CosOp>,
3501 PointwiseConverter<tosa::TanhOp>,
3502 PointwiseConverter<tosa::ErfOp>,
3503 PointwiseConverter<tosa::BitwiseAndOp>,
3504 PointwiseConverter<tosa::BitwiseOrOp>,
3505 PointwiseConverter<tosa::BitwiseNotOp>,
3506 PointwiseConverter<tosa::BitwiseXorOp>,
3507 PointwiseConverter<tosa::LogicalAndOp>,
3508 PointwiseConverter<tosa::LogicalNotOp>,
3509 PointwiseConverter<tosa::LogicalOrOp>,
3510 PointwiseConverter<tosa::LogicalXorOp>,
3511 PointwiseConverter<tosa::CastOp>,
3512 PointwiseConverter<tosa::LogicalLeftShiftOp>,
3513 PointwiseConverter<tosa::LogicalRightShiftOp>,
3514 PointwiseConverter<tosa::ArithmeticRightShiftOp>,
3515 PointwiseConverter<tosa::ClzOp>,
3516 PointwiseConverter<tosa::SelectOp>,
3517 PointwiseConverter<tosa::GreaterOp>,
3518 PointwiseConverter<tosa::GreaterEqualOp>,
3519 PointwiseConverter<tosa::EqualOp>,
3520 PointwiseConverter<tosa::MaximumOp>,
3521 PointwiseConverter<tosa::MinimumOp>,
3522 PointwiseConverter<tosa::CeilOp>,
3523 PointwiseConverter<tosa::FloorOp>,
3524 PointwiseConverter<tosa::ClampOp>,
3525 PointwiseConverter<tosa::SigmoidOp>
3528 patterns->
add<QuantizedMatMulTConverter>(converter, patterns->
getContext());
3531 IdentityNConverter<tosa::IdentityOp>,
3544 ReduceConverter<tosa::ReduceAllOp>,
3545 ReduceConverter<tosa::ReduceAnyOp>,
3546 ReduceConverter<tosa::ReduceMinOp>,
3547 ReduceConverter<tosa::ReduceMaxOp>,
3548 ReduceConverter<tosa::ReduceSumOp>,
3549 ReduceConverter<tosa::ReduceProductOp>,
3550 ArgMinMaxConverter<tosa::ArgMaxOp>,
3551 ArgMinMaxConverter<tosa::ArgMinOp>>(patterns->
getContext(),
options.allowNonFinites);
static int64_t product(ArrayRef< int64_t > vals)
*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 inserted(the insertion happens right before the *insertion point). Since `begin` can itself be invalidated due to the memref *rewriting done from this method
static llvm::ManagedStatic< PassManagerOptions > options
static Value clamp(ImplicitLocOpBuilder &builder, Value value, Value lowerBound, Value upperBound)
static Value max(ImplicitLocOpBuilder &builder, Value value, Value bound)
static Value min(ImplicitLocOpBuilder &builder, Value value, Value bound)
static TypedAttr createInitialValueForReduceOp(Operation *op, Type elementTy, PatternRewriter &rewriter, bool allowNonFinites)
static OpFoldResult getOrFoldTensorDim(PatternRewriter &rewriter, Location loc, IndexPool &indexPool, Value tensor, int64_t index)
static Value createLinalgBodyCalculationForReduceOp(Operation *op, ValueRange args, Type elementTy, PatternRewriter &rewriter)
static std::pair< Value, Value > normalizeMatMulBatchDimensions(PatternRewriter &rewriter, Location loc, Value a, Value b, int64_t batchRank)
static OpTy createWithDefaultProperties(OpBuilder &builder, Location loc, TypeRange resultTypes, ValueRange operands)
static Value broadcastMatMulLeadingDimensions(PatternRewriter &rewriter, Location loc, Value operand, Value other)
static Value getTensorDim(PatternRewriter &rewriter, Location loc, IndexPool &indexPool, Value tensor, int64_t index)
static Value createIndex(PatternRewriter &rewriter, Location loc, IndexPool &indexPool, int64_t index)
static LogicalResult reduceMatchAndRewriteHelper(OpTy op, uint64_t axis, PatternRewriter &rewriter, bool allowNonFinites)
static ValueRange getScalarOperands(Operation *operation, ValueRange operands)
static Value broadcastDynamicDimensions(PatternRewriter &rewriter, Location loc, IndexPool &indexPool, Value operand, ArrayRef< OpFoldResult > targetShape, ArrayRef< Value > masterOperands)
static Value broadcastDynamicDimension(PatternRewriter &rewriter, Location loc, IndexPool &indexPool, Value operand, int64_t dim, OpFoldResult targetSize, Value masterOperand)
static LogicalResult elementwiseMatchAndRewriteHelper(Operation *operation, ValueRange operands, ConversionPatternRewriter &rewriter, const TypeConverter &converter)
static Value createLinalgBodyCalculationForElementwiseOp(Operation *op, ValueRange args, ArrayRef< Type > resultTypes, ConversionPatternRewriter &rewriter)
static LogicalResult emitElementwiseComputation(ConversionPatternRewriter &rewriter, Location loc, Operation *operation, ValueRange operands, ValueRange scalarOperands, ArrayRef< OpFoldResult > targetShape, const TypeConverter &converter)
static ValueRange getBroadcastableOperands(Operation *operation, ValueRange operands)
static Value materializeBinaryNanCheckIfRequired(OpTy op, PatternRewriter &rewriter, Value lhs, Value rhs, Value result)
static std::pair< SmallVector< OpFoldResult >, SmallVector< Value > > computeTargetShape(PatternRewriter &rewriter, Location loc, IndexPool &indexPool, ValueRange operands)
static bool operandsAndResultsRanked(Operation *operation)
static Value broadcastMatMulSingletonDimension(PatternRewriter &rewriter, Location loc, Value operand, int64_t dim, OpFoldResult size)
static SmallVector< Value > slice(OpBuilder &b, Location loc, Value value, VectorType nativeType)
static const llvm::fltSemantics * getFloatSemantics(TruncfSrcElemTypes etype)
Float semantics the element type attributes of xevm.truncf and xevm.extf stand for.
A multi-dimensional affine map Affine map's are immutable like Type's, and they are uniqued.
static AffineMap get(MLIRContext *context)
Returns a zero result affine map with no dimensions or symbols: () -> ().
static SmallVector< AffineMap, 4 > inferFromExprList(ArrayRef< ArrayRef< AffineExpr > > exprsList, MLIRContext *context)
Returns a vector of AffineMaps; each with as many results as exprs.size(), as many dims as the larges...
BlockArgument getArgument(unsigned i)
bool getValue() const
Return the boolean value of this attribute.
IntegerAttr getIndexAttr(int64_t value)
IntegerAttr getI32IntegerAttr(int32_t value)
IntegerAttr getIntegerAttr(Type type, int64_t value)
AffineMap getMultiDimIdentityMap(unsigned rank)
FloatAttr getFloatAttr(Type type, double value)
AffineExpr getAffineConstantExpr(int64_t constant)
IntegerType getIntegerType(unsigned width)
Ty getType(Args &&...args)
Get or construct an instance of the type Ty with provided arguments.
BoolAttr getBoolAttr(bool value)
TypedAttr getZeroAttr(Type type)
AffineExpr getAffineDimExpr(unsigned position)
MLIRContext * getContext() const
IntegerAttr getI8IntegerAttr(int8_t value)
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.
An attribute that represents a reference to a dense integer vector or tensor object.
static DenseIntElementsAttr get(const ShapedType &type, Arg &&arg)
Get an instance of a DenseIntElementsAttr with the given arguments.
ImplicitLocOpBuilder maintains a 'current location', allowing use of the create<> method without spec...
This class defines the main interface for locations in MLIR and acts as a non-nullable wrapper around...
This class helps build Operations.
void createOrFold(SmallVectorImpl< Value > &results, Location location, Args &&...args)
Create an operation of specific op type at the current insertion point, and immediately try to fold i...
This class represents a single result from folding an operation.
Operation is the basic unit of execution within MLIR.
Value getOperand(unsigned idx)
Location getLoc()
The source location the operation was defined or derived from.
unsigned getNumOperands()
result_type_range getResultTypes()
operand_range getOperands()
Returns an iterator on the underlying Value's.
result_range getResults()
unsigned getNumResults()
Return the number of results held by this operation.
A special type of RewriterBase that coordinates the application of a rewrite pattern on the current I...
MLIRContext * getContext() const
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,...
OpTy replaceOpWithNewOp(Operation *op, Args &&...args)
Replace the results of the given (original) op with a new op that is created without verification (re...
This class provides an abstraction over the various different ranges of value types.
Instances of the Type class are uniqued, have an immutable identifier and an optional mutable compone...
bool isUnsignedInteger() const
Return true if this is an unsigned integer type (with the specified width).
bool isInteger() const
Return true if this is an integer type (with the specified width).
bool isIntOrFloat() const
Return true if this is an integer (of any signedness) or a float type.
unsigned getIntOrFloatBitWidth() const
Return the bit width of an integer or a float type, assert failure on other types.
This class provides an abstraction over the different types of ranges over Values.
type_range getType() const
Type front()
Return first type in the range.
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.
static ConstantIndexOp create(OpBuilder &builder, Location location, int64_t value)
static ConstantIntOp create(OpBuilder &builder, Location location, int64_t value, unsigned width)
InFlightDiagnostic & next(InFlightDiagnostic &diag)
Starts a new message part in an in-flight diagnostic.
Value createCanonicalRankReducingExtractSliceOp(OpBuilder &b, Location loc, Value tensor, RankedTensorType targetType)
Create a rank-reducing ExtractSliceOp @[0 .
OpFoldResult getMixedSize(OpBuilder &builder, Location loc, Value value, int64_t dim)
Return the dimension of the given tensor value.
SmallVector< OpFoldResult > getMixedSizes(OpBuilder &builder, Location loc, Value value)
Return the dimensions of the given tensor value.
Value clampFloatHelper(Location loc, Value arg, Value min, Value max, OpBuilder &rewriter)
llvm::DenseMap< int64_t, Value > IndexPool
Caches the arith.constant index values created for a set of dimension indices so that broadcasting he...
SmallVector< utils::IteratorType > getNParallelLoopsAttrs(unsigned nParallelLoops)
SmallVector< Value > condenseValues(const SmallVector< Value > &values)
std::optional< SmallVector< Value > > checkHasDynamicBatchDims(PatternRewriter &rewriter, Op op, ArrayRef< Value > params)
MatMulTOperands prepareMatMulOperands(PatternRewriter &rewriter, Location loc, Value a, Value b, RankedTensorType outputType, bool transposeB)
Broadcast the batch dimensions of the ranked operands and create a zero-filled result tensor for eith...
std::pair< OpFoldResult, Value > computeTargetSize(PatternRewriter &rewriter, Location loc, IndexPool &indexPool, ValueRange operands, int64_t dim)
Compute the broadcasted runtime size of dimension dim of an elementwise operation whose operands all ...
APFloat getFloatMinMaxIdentity(const llvm::fltSemantics &semantics, bool negative, bool allowNonFinites)
void populateTosaToLinalgConversionPatterns(const TypeConverter &converter, RewritePatternSet *patterns, const TosaToLinalgOptions &options=TosaToLinalgOptions())
Populates conversion passes from TOSA dialect to Linalg dialect.
Value getTosaConstShape(ImplicitLocOpBuilder &builder, llvm::ArrayRef< int64_t > shape)
SmallVector< int64_t > convertFromMlirShape(ArrayRef< int64_t > shape)
Value clampIntHelper(Location loc, Value arg, Value min, Value max, OpBuilder &rewriter, bool isUnsigned)
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.
void dispatchIndexOpFoldResults(ArrayRef< OpFoldResult > ofrs, SmallVectorImpl< Value > &dynamicVec, SmallVectorImpl< int64_t > &staticVec)
Helper function to dispatch multiple OpFoldResults according to the behavior of dispatchIndexOpFoldRe...
Value getValueOrCreateConstantIndexOp(OpBuilder &b, Location loc, OpFoldResult ofr)
Converts an OpFoldResult to a Value.
OpFoldResult getAsOpFoldResult(Value val)
Given a value, try to extract a constant Attribute.
detail::constant_op_matcher m_Constant()
Matches a constant foldable operation.
AffineExpr getAffineDimExpr(unsigned position, MLIRContext *context)
These free functions allow clients of the API to not use classes in detail.
OpRewritePattern is a wrapper around RewritePattern that allows for matching and rewriting against an...