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) {
230 const int64_t zpAdd = inZp + outZp;
232 APInt::getSignedMaxValue(inputBitWidth).getSExtValue() +
237 if (maxValue <= APInt::getSignedMaxValue(16).getSExtValue()) {
238 intermediateBitWidth = 16;
239 }
else if (maxValue <= APInt::getSignedMaxValue(32).getSExtValue()) {
240 intermediateBitWidth = 32;
243 intermediateType = rewriter.getIntegerType(intermediateBitWidth);
244 zpAddValue = arith::ConstantOp::create(
245 rewriter, loc, rewriter.getIntegerAttr(intermediateType, zpAdd));
247 intermediateType = rewriter.getIntegerType(intermediateBitWidth);
248 Value arg1 = args[1];
249 Value arg2 = args[2];
251 if (arg1.
getType() != intermediateType)
252 arg1 = arith::ExtSIOp::create(rewriter, loc, intermediateType, arg1);
253 if (arg2.
getType() != intermediateType)
254 arg2 = arith::ExtSIOp::create(rewriter, loc, intermediateType, arg2);
256 arith::AddIOp::create(rewriter, loc, intermediateType, arg1, arg2);
262 if (ext.
getType() != intermediateType)
263 ext = arith::ExtSIOp::create(rewriter, loc, intermediateType, ext);
264 auto sub = arith::SubIOp::create(rewriter, loc, zpAddValue, ext);
268 rewriter, loc, intermediateType,
269 APInt::getSignedMinValue(inputBitWidth).getSExtValue());
271 rewriter, loc, intermediateType,
272 APInt::getSignedMaxValue(inputBitWidth).getSExtValue());
276 if (
clamp.getType() == elementTy)
278 return arith::TruncIOp::create(rewriter, loc, elementTy,
clamp);
283 if (isa<tosa::BitwiseAndOp>(op) && isa<IntegerType>(elementTy))
284 return arith::AndIOp::create(rewriter, loc, resultTypes, args);
287 if (isa<tosa::BitwiseOrOp>(op) && isa<IntegerType>(elementTy))
288 return arith::OrIOp::create(rewriter, loc, resultTypes, args);
291 if (isa<tosa::BitwiseNotOp>(op) && isa<IntegerType>(elementTy)) {
292 auto allOnesAttr = rewriter.getIntegerAttr(
293 elementTy, APInt::getAllOnes(elementTy.getIntOrFloatBitWidth()));
294 auto allOnes = arith::ConstantOp::create(rewriter, loc, allOnesAttr);
295 return arith::XOrIOp::create(rewriter, loc, resultTypes, args[0], allOnes);
299 if (isa<tosa::BitwiseXorOp>(op) && isa<IntegerType>(elementTy))
300 return arith::XOrIOp::create(rewriter, loc, resultTypes, args);
303 if (isa<tosa::LogicalLeftShiftOp>(op) && isa<IntegerType>(elementTy))
308 if (isa<tosa::LogicalRightShiftOp>(op) && isa<IntegerType>(elementTy))
313 if (isa<tosa::ArithmeticRightShiftOp>(op) && isa<IntegerType>(elementTy)) {
315 rewriter, loc, resultTypes, args);
316 bool round = cast<tosa::ArithmeticRightShiftOp>(op).getRound();
321 Type i1Ty = IntegerType::get(rewriter.getContext(), 1);
322 auto one = arith::ConstantOp::create(rewriter, loc,
323 IntegerAttr::get(elementTy, 1));
324 auto zero = arith::ConstantOp::create(rewriter, loc,
325 IntegerAttr::get(elementTy, 0));
327 arith::ConstantOp::create(rewriter, loc, IntegerAttr::get(i1Ty, 0));
329 arith::ConstantOp::create(rewriter, loc, IntegerAttr::get(i1Ty, 1));
332 auto shiftValueGreaterThanZero = arith::CmpIOp::create(
333 rewriter, loc, arith::CmpIPredicate::sgt, args[1], zero);
337 arith::SubIOp::create(rewriter, loc, resultTypes, args[1], one);
339 arith::ShRSIOp::create(rewriter, loc, resultTypes, args[0], subtract)
342 rewriter, loc,
TypeRange{i1Ty}, shifted);
344 arith::AndIOp::create(rewriter, loc, i1Ty, truncated, i1one);
346 auto shouldRound = arith::SelectOp::create(
347 rewriter, loc, i1Ty, shiftValueGreaterThanZero, isInputOdd, i1zero);
349 arith::ExtUIOp::create(rewriter, loc, resultTypes, shouldRound);
350 return arith::AddIOp::create(rewriter, loc, resultTypes,
result, extended);
354 if (isa<tosa::ClzOp>(op) && isa<IntegerType>(elementTy)) {
355 return math::CountLeadingZerosOp::create(rewriter, loc, elementTy, args[0]);
359 if (isa<tosa::LogicalAndOp>(op) && elementTy.isInteger(1))
360 return arith::AndIOp::create(rewriter, loc, resultTypes, args);
363 if (isa<tosa::LogicalNotOp>(op) && elementTy.isInteger(1)) {
364 auto one = arith::ConstantOp::create(rewriter, loc,
365 rewriter.getIntegerAttr(elementTy, 1));
366 return arith::XOrIOp::create(rewriter, loc, resultTypes, args[0], one);
370 if (isa<tosa::LogicalOrOp>(op) && elementTy.isInteger(1))
371 return arith::OrIOp::create(rewriter, loc, resultTypes, args);
374 if (isa<tosa::LogicalXorOp>(op) && elementTy.isInteger(1))
375 return arith::XOrIOp::create(rewriter, loc, resultTypes, args);
378 if (isa<tosa::PowOp>(op) && isa<FloatType>(elementTy))
383 if (isa<tosa::RsqrtOp>(op) && isa<FloatType>(elementTy))
388 if (isa<tosa::LogOp>(op) && isa<FloatType>(elementTy))
393 if (isa<tosa::ExpOp>(op) && isa<FloatType>(elementTy))
398 if (isa<tosa::SinOp>(op) && isa<FloatType>(elementTy))
403 if (isa<tosa::CosOp>(op) && isa<FloatType>(elementTy))
408 if (isa<tosa::TanhOp>(op) && isa<FloatType>(elementTy))
413 if (isa<tosa::ErfOp>(op) && llvm::isa<FloatType>(elementTy))
418 if (isa<tosa::GreaterOp>(op) && isa<FloatType>(elementTy))
419 return arith::CmpFOp::create(rewriter, loc, arith::CmpFPredicate::OGT,
422 if (isa<tosa::GreaterOp>(op) && elementTy.isSignlessInteger())
423 return arith::CmpIOp::create(rewriter, loc, arith::CmpIPredicate::sgt,
427 if (isa<tosa::GreaterEqualOp>(op) && isa<FloatType>(elementTy))
428 return arith::CmpFOp::create(rewriter, loc, arith::CmpFPredicate::OGE,
431 if (isa<tosa::GreaterEqualOp>(op) && elementTy.isSignlessInteger())
432 return arith::CmpIOp::create(rewriter, loc, arith::CmpIPredicate::sge,
436 if (isa<tosa::EqualOp>(op) && isa<FloatType>(elementTy))
437 return arith::CmpFOp::create(rewriter, loc, arith::CmpFPredicate::OEQ,
440 if (isa<tosa::EqualOp>(op) && elementTy.isSignlessInteger())
441 return arith::CmpIOp::create(rewriter, loc, arith::CmpIPredicate::eq,
445 if (isa<tosa::SelectOp>(op)) {
447 if (isa<FloatType>(elementTy) || isa<IntegerType>(elementTy))
448 return arith::SelectOp::create(rewriter, loc, args[0], args[1], args[2]);
452 if (isa<tosa::MaximumOp>(op) && isa<FloatType>(elementTy)) {
453 auto max = arith::MaximumFOp::create(rewriter, loc, args[0], args[1]);
455 rewriter, args[0], args[1],
max);
458 if (isa<tosa::MaximumOp>(op) && elementTy.isSignlessInteger()) {
459 return arith::MaxSIOp::create(rewriter, loc, args[0], args[1]);
463 if (isa<tosa::MinimumOp>(op) && isa<FloatType>(elementTy)) {
464 auto min = arith::MinimumFOp::create(rewriter, loc, args[0], args[1]);
466 rewriter, args[0], args[1],
min);
469 if (isa<tosa::MinimumOp>(op) && elementTy.isSignlessInteger()) {
470 return arith::MinSIOp::create(rewriter, loc, args[0], args[1]);
474 if (isa<tosa::CeilOp>(op) && isa<FloatType>(elementTy))
479 if (isa<tosa::FloorOp>(op) && isa<FloatType>(elementTy))
484 if (isa<tosa::ClampOp>(op) && isa<FloatType>(elementTy)) {
485 bool losesInfo =
false;
486 auto clampOp = cast<tosa::ClampOp>(op);
487 APFloat minApf = cast<FloatAttr>(clampOp.getMinValAttr()).getValue();
488 APFloat maxApf = cast<FloatAttr>(clampOp.getMaxValAttr()).getValue();
490 APFloat::rmNearestTiesToEven, &losesInfo);
492 APFloat::rmNearestTiesToEven, &losesInfo);
493 auto min = arith::ConstantOp::create(
494 rewriter, loc, elementTy, rewriter.getFloatAttr(elementTy, minApf));
495 auto max = arith::ConstantOp::create(
496 rewriter, loc, elementTy, rewriter.getFloatAttr(elementTy, maxApf));
499 const auto nanMode = clampOp.getNanMode();
502 if (!isa<FloatType>(elementTy))
507 if (nanMode == NanPropagationMode::PROPAGATE)
521 Value isNaN = arith::CmpFOp::create(
522 rewriter, op->
getLoc(), arith::CmpFPredicate::UNO, args[0], args[0]);
525 return arith::SelectOp::create(rewriter, op->
getLoc(), isNaN,
min,
result);
528 if (isa<tosa::ClampOp>(op) && isa<IntegerType>(elementTy)) {
529 auto intTy = cast<IntegerType>(elementTy);
530 auto clampOp = cast<tosa::ClampOp>(op);
532 cast<IntegerAttr>(clampOp.getMinValAttr()).getValue().getSExtValue();
534 cast<IntegerAttr>(clampOp.getMaxValAttr()).getValue().getSExtValue();
536 int64_t minRepresentable = std::numeric_limits<int64_t>::min();
537 int64_t maxRepresentable = std::numeric_limits<int64_t>::max();
538 if (intTy.isUnsignedInteger()) {
539 minRepresentable = 0;
540 if (intTy.getIntOrFloatBitWidth() <= 63) {
542 (
int64_t)APInt::getMaxValue(intTy.getIntOrFloatBitWidth())
545 }
else if (intTy.getIntOrFloatBitWidth() <= 64) {
547 minRepresentable = APInt::getSignedMinValue(intTy.getIntOrFloatBitWidth())
549 maxRepresentable = APInt::getSignedMaxValue(intTy.getIntOrFloatBitWidth())
554 min = std::max(
min, minRepresentable);
555 max = std::max(
max, minRepresentable);
556 min = std::min(
min, maxRepresentable);
557 max = std::min(
max, maxRepresentable);
560 intTy.getIntOrFloatBitWidth());
562 intTy.getIntOrFloatBitWidth());
564 intTy.isUnsignedInteger());
568 if (isa<tosa::SigmoidOp>(op) && isa<FloatType>(elementTy)) {
570 arith::ConstantOp::create(rewriter, loc, FloatAttr::get(elementTy, 1));
571 auto negate = arith::NegFOp::create(rewriter, loc, resultTypes, args[0]);
572 auto exp = mlir::math::ExpOp::create(rewriter, loc, resultTypes, negate);
573 auto added = arith::AddFOp::create(rewriter, loc, exp, one);
574 return arith::DivFOp::create(rewriter, loc, one, added);
578 if (isa<tosa::CastOp>(op)) {
579 Type srcTy = elementTy;
580 Type dstTy = resultTypes.front();
582 (
void)rewriter.notifyMatchFailure(op,
"unsupported type");
590 bool inputUnsigned = cast<tosa::CastOp>(op).getInputUnsigned();
595 if (isa<FloatType>(srcTy) && isa<FloatType>(dstTy) && bitExtend)
599 if (isa<FloatType>(srcTy) && isa<FloatType>(dstTy) && !bitExtend)
604 if (srcTy.
isInteger(1) && arith::UIToFPOp::areCastCompatible(srcTy, dstTy))
608 if (srcTy.
isInteger(1) && isa<IntegerType>(dstTy) && bitExtend)
615 auto unrealizedCast =
616 UnrealizedConversionCastOp::create(
620 return arith::UIToFPOp::create(rewriter, loc, resultTypes[0],
625 if (inputUnsigned && arith::UIToFPOp::areCastCompatible(srcTy, dstTy))
630 if (arith::SIToFPOp::areCastCompatible(srcTy, dstTy))
631 return arith::SIToFPOp::create(rewriter, loc, resultTypes, args,
635 if (isa<FloatType>(srcTy) && dstTy.
isInteger(1)) {
636 Value zero = arith::ConstantOp::create(rewriter, loc,
637 rewriter.getFloatAttr(srcTy, 0.0));
638 return arith::CmpFOp::create(rewriter, loc, arith::CmpFPredicate::UNE,
642 if (arith::FPToSIOp::areCastCompatible(srcTy, dstTy)) {
643 auto rounded = math::RoundEvenOp::create(rewriter, loc, args[0]);
645 const auto &fltSemantics = cast<FloatType>(srcTy).getFloatSemantics();
649 APFloat::semanticsMaxExponent(fltSemantics)) {
652 auto conv = arith::FPToSIOp::create(rewriter, loc, dstTy, rounded);
653 auto posInf = arith::ConstantOp::create(
656 APFloat::getInf(fltSemantics)));
657 auto negInf = arith::ConstantOp::create(
659 rewriter.getFloatAttr(
661 APFloat::getInf(fltSemantics,
true)));
662 auto overflow = arith::CmpFOp::create(
663 rewriter, loc, arith::CmpFPredicate::UEQ, rounded, posInf);
664 auto underflow = arith::CmpFOp::create(
665 rewriter, loc, arith::CmpFPredicate::UEQ, rounded, negInf);
666 auto intMin = arith::ConstantOp::create(
668 rewriter.getIntegerAttr(
671 auto intMax = arith::ConstantOp::create(
673 rewriter.getIntegerAttr(
677 arith::SelectOp::create(rewriter, loc, overflow, intMax, conv);
678 return arith::SelectOp::create(rewriter, loc, underflow, intMin,
682 auto intMinFP = arith::ConstantOp::create(
684 rewriter.getFloatAttr(
690 if (cast<FloatType>(srcTy).getFPMantissaWidth() >=
696 auto intMaxFP = arith::ConstantOp::create(
698 rewriter.getFloatAttr(
705 return arith::FPToSIOp::create(rewriter, loc, dstTy, clamped);
712 auto intMaxPlusOneFP = arith::ConstantOp::create(
714 rewriter.getFloatAttr(
721 auto intMax = arith::ConstantOp::create(
723 rewriter.getIntegerAttr(
727 arith::MaximumFOp::create(rewriter, loc, rounded, intMinFP);
729 arith::FPToSIOp::create(rewriter, loc, dstTy, minClampedFP);
730 auto overflow = arith::CmpFOp::create(
731 rewriter, loc, arith::CmpFPredicate::UGE, rounded, intMaxPlusOneFP);
732 return arith::SelectOp::create(rewriter, loc, overflow, intMax,
738 if (isa<IntegerType>(srcTy) && dstTy.
isInteger(1)) {
741 return arith::CmpIOp::create(rewriter, loc, arith::CmpIPredicate::ne,
746 if (inputUnsigned && isa<IntegerType>(srcTy) && isa<IntegerType>(dstTy) &&
751 if (isa<IntegerType>(srcTy) && isa<IntegerType>(dstTy) && bitExtend)
752 return arith::ExtSIOp::create(rewriter, loc, resultTypes, args,
755 if (isa<IntegerType>(srcTy) && isa<IntegerType>(dstTy) && !bitExtend) {
756 return arith::TruncIOp::create(rewriter, loc, dstTy, args[0]);
760 (
void)rewriter.notifyMatchFailure(
761 op,
"unhandled op for linalg body calculation for elementwise op");
782 return tensor::DimOp::create(rewriter, loc,
tensor, indexValue).getResult();
788 auto shapedType = dyn_cast<ShapedType>(
tensor.getType());
789 assert(shapedType && shapedType.hasRank() &&
"expected a ranked shaped type");
790 assert(
index >= 0 &&
index < shapedType.getRank() &&
"index out of bounds");
791 if (shapedType.isDynamicDim(
index))
797 auto isRanked = [](
Value value) {
798 return isa<RankedTensorType>(value.getType());
800 return llvm::all_of(operation->
getOperands(), isRanked) &&
801 llvm::all_of(operation->
getResults(), isRanked);
814static std::pair<OpFoldResult, Value>
820 for (
auto operand : operands) {
821 auto size = cast<RankedTensorType>(operand.getType()).getDimSize(dim);
822 if (ShapedType::isStatic(size) && size > 1)
827 auto operandsWithDynamicDim =
828 llvm::filter_to_vector(operands, [&](
Value operand) {
829 return cast<RankedTensorType>(operand.
getType()).isDynamicDim(dim);
833 if (operandsWithDynamicDim.empty())
840 getTensorDim(rewriter, loc, indexPool, operandsWithDynamicDim[0], dim);
841 if (operandsWithDynamicDim.size() == 1)
842 return {targetSize, operandsWithDynamicDim[0]};
845 for (
size_t i = 1; i < operandsWithDynamicDim.size(); i++) {
847 getTensorDim(rewriter, loc, indexPool, operandsWithDynamicDim[i], dim);
848 targetSize = arith::MaxUIOp::create(rewriter, loc, targetSize, nextSize);
850 return {targetSize,
nullptr};
858 assert(!operands.empty());
859 auto rank = cast<RankedTensorType>(operands.front().
getType()).getRank();
862 for (
auto dim : llvm::seq<int64_t>(0, rank)) {
863 auto [targetSize, masterOperand] =
865 targetShape.push_back(targetSize);
866 masterOperands.push_back(masterOperand);
868 return {targetShape, masterOperands};
874 Value masterOperand) {
876 auto rankedTensorType = cast<RankedTensorType>(operand.
getType());
877 if (!rankedTensorType.isDynamicDim(dim))
884 if (operand == masterOperand)
888 auto rank = rankedTensorType.getRank();
890 for (
auto index : llvm::seq<int64_t>(0, rank)) {
893 affineExprs.push_back(affineExpr);
895 auto broadcastAffineMap =
901 auto one =
createIndex(rewriter, loc, indexPool, 1);
902 auto runtimeSize =
getTensorDim(rewriter, loc, indexPool, operand, dim);
903 auto broadcastNecessary = arith::CmpIOp::create(
904 rewriter, loc, arith::CmpIPredicate::eq, runtimeSize, one);
914 for (
auto index : llvm::seq<int64_t>(0, rank)) {
915 auto size =
index == dim ? targetSize
918 outputTensorShape.push_back(size);
920 Value outputTensor = tensor::EmptyOp::create(
921 opBuilder, loc, outputTensorShape, rankedTensorType.getElementType());
925 linalg::GenericOp::create(
926 opBuilder, loc, outputTensor.
getType(), operand, outputTensor,
930 linalg::YieldOp::create(opBuilder, loc, blockArgs.front());
935 auto castResultTensor = rewriter.
createOrFold<tensor::CastOp>(
936 loc, operand.
getType(), resultTensor);
939 scf::YieldOp::create(opBuilder, loc, castResultTensor);
944 scf::YieldOp::create(opBuilder, loc, operand);
948 auto ifOp = scf::IfOp::create(rewriter, loc, broadcastNecessary,
949 emitThenRegion, emitElseRegion);
950 return ifOp.getResult(0);
957 int64_t rank = cast<RankedTensorType>(operand.
getType()).getRank();
958 assert((
int64_t)targetShape.size() == rank);
959 assert((
int64_t)masterOperands.size() == rank);
960 for (
auto index : llvm::seq<int64_t>(0, rank))
973 if (operands.size() == 1)
977 bool hasDynamic =
false;
978 for (
auto op : operands) {
979 const auto tType = dyn_cast<RankedTensorType>(op.getType());
980 if (tType && !tType.hasStaticShape()) {
989 return llvm::map_to_vector(operands, [&](
Value operand) {
991 targetShape, masterOperands);
1001 auto resultType = cast_or_null<RankedTensorType>(
1004 return rewriter.notifyMatchFailure(operation,
"failed to convert type");
1006 Value outputTensor = tensor::EmptyOp::create(rewriter, loc, targetShape,
1007 resultType.getElementType());
1012 auto rank = resultType.getRank();
1013 auto affineMaps = llvm::map_to_vector(operands, [&](
Value operand) {
1014 auto shape = cast<ShapedType>(operand.
getType()).getShape();
1016 for (
auto it : llvm::enumerate(
shape)) {
1020 bool requiresBroadcast =
1021 (it.value() == 1 && resultType.getDimSize(it.index()) != 1);
1022 auto affineExpr = requiresBroadcast
1023 ? rewriter.getAffineConstantExpr(0)
1024 : rewriter.getAffineDimExpr(it.index());
1025 affineExprs.push_back(affineExpr);
1027 return AffineMap::get(rank, 0, affineExprs, rewriter.getContext());
1029 affineMaps.push_back(rewriter.getMultiDimIdentityMap(rank));
1032 bool encounteredError =
false;
1033 auto linalgOp = linalg::GenericOp::create(
1034 rewriter, loc, outputTensor.
getType(), operands, outputTensor, affineMaps,
1039 {resultType.getElementType()}, rewriter);
1041 encounteredError =
true;
1044 linalg::YieldOp::create(opBuilder, loc, opResult);
1046 if (encounteredError)
1047 return rewriter.notifyMatchFailure(
1048 operation,
"unable to create linalg.generic body for elementwise op");
1051 auto castResult = rewriter.createOrFold<tensor::CastOp>(
1052 loc, resultType, linalgOp->getResult(0));
1053 rewriter.replaceOp(operation, castResult);
1060 if (isa<tosa::MulOp>(operation)) {
1064 return operands.take_front(2);
1066 return operands.take_front(3);
1068 if (
auto negate = dyn_cast<tosa::NegateOp>(operation)) {
1069 FailureOr<int64_t> maybeInZp = negate.getInput1ZeroPoint();
1070 FailureOr<int64_t> maybeOutZp = negate.getOutputZeroPoint();
1071 if (failed(maybeOutZp) && failed(maybeInZp))
1074 return operands.take_front(1);
1081 ConversionPatternRewriter &rewriter,
1085 assert(operation->
getNumResults() == 1 &&
"elementwise op expects 1 result");
1087 "elementwise op expects at least 1 operand");
1089 return rewriter.notifyMatchFailure(operation,
1090 "Unranked tensors not supported");
1094 auto loc = operation->
getLoc();
1096 auto [targetShape, masterOperands] =
1098 auto broadcastOperands =
1100 targetShape, masterOperands);
1102 targetShape, converter);
1109 bool allowNonFinites) {
1110 if (isa<tosa::ReduceSumOp>(op) && isa<FloatType>(elementTy))
1113 if (isa<tosa::ReduceSumOp>(op) && isa<IntegerType>(elementTy))
1116 if (isa<tosa::ReduceProductOp>(op) && isa<FloatType>(elementTy))
1119 if (isa<tosa::ReduceProductOp>(op) && isa<IntegerType>(elementTy))
1122 if (isa<tosa::ReduceMinOp>(op) && isa<FloatType>(elementTy))
1126 false, allowNonFinites));
1128 if (isa<tosa::ReduceMinOp>(op) && isa<IntegerType>(elementTy))
1132 if (isa<tosa::ReduceMaxOp>(op) && isa<FloatType>(elementTy))
1136 true, allowNonFinites));
1138 if (isa<tosa::ReduceMaxOp>(op) && isa<IntegerType>(elementTy))
1142 if (isa<tosa::ReduceAllOp>(op) && elementTy.
isInteger(1))
1145 if (isa<tosa::ReduceAnyOp>(op) && elementTy.
isInteger(1))
1148 if (isa<tosa::ArgMaxOp>(op) && isa<FloatType>(elementTy))
1152 true, allowNonFinites));
1154 if (isa<tosa::ArgMaxOp>(op) && isa<IntegerType>(elementTy))
1168 if (isa<tosa::ReduceSumOp>(op) && isa<FloatType>(elementTy)) {
1170 rewriter, loc,
TypeRange{elementTy}, args);
1173 if (isa<tosa::ReduceSumOp>(op) && isa<IntegerType>(elementTy)) {
1175 rewriter, loc,
TypeRange{elementTy}, args);
1178 if (isa<tosa::ReduceProductOp>(op) && isa<FloatType>(elementTy)) {
1180 rewriter, loc,
TypeRange{elementTy}, args);
1183 if (isa<tosa::ReduceProductOp>(op) && isa<IntegerType>(elementTy)) {
1185 rewriter, loc,
TypeRange{elementTy}, args);
1188 if (isa<tosa::ReduceMinOp>(op) && isa<FloatType>(elementTy)) {
1189 return arith::MinimumFOp::create(rewriter, loc, args[0], args[1]);
1192 if (isa<tosa::ReduceMinOp>(op) && isa<IntegerType>(elementTy)) {
1193 return arith::MinSIOp::create(rewriter, loc, args[0], args[1]);
1196 if (isa<tosa::ReduceMaxOp>(op) && isa<FloatType>(elementTy)) {
1197 return arith::MaximumFOp::create(rewriter, loc, args[0], args[1]);
1200 if (isa<tosa::ReduceMaxOp>(op) && isa<IntegerType>(elementTy)) {
1201 return arith::MaxSIOp::create(rewriter, loc, args[0], args[1]);
1204 if (isa<tosa::ReduceAllOp>(op) && elementTy.
isInteger(1))
1205 return arith::AndIOp::create(rewriter, loc, args);
1207 if (isa<tosa::ReduceAnyOp>(op) && elementTy.
isInteger(1))
1208 return arith::OrIOp::create(rewriter, loc, args);
1216template <
typename OpTy>
1219 bool allowNonFinites) {
1220 auto loc = op->getLoc();
1221 auto inputTy = dyn_cast<RankedTensorType>(op->getOperand(0).getType());
1222 auto resultTy = dyn_cast<RankedTensorType>(op->getResult(0).getType());
1223 if (!inputTy || !resultTy)
1226 auto elementTy = resultTy.getElementType();
1227 Value input = op->getOperand(0);
1230 bool widenAccTy = std::is_same_v<OpTy, tosa::ReduceSumOp> &&
1231 isa<FloatType>(elementTy) &&
1232 cast<FloatType>(elementTy).isBF16();
1237 for (
unsigned i = 0; i < inputTy.getRank(); i++) {
1239 reduceShape.push_back(inputTy.getDimSize(i));
1240 if (inputTy.isDynamicDim(i))
1241 dynDims.push_back(tensor::DimOp::create(rewriter, loc, input, i));
1246 inputs.push_back(input);
1250 tensor::EmptyOp::create(rewriter, loc, reduceShape, accTy, dynDims)
1253 auto fillValueAttr =
1257 op,
"No initial value found for reduction operation");
1259 auto fillValue = arith::ConstantOp::create(rewriter, loc, fillValueAttr);
1261 linalg::FillOp::create(rewriter, loc,
ValueRange{fillValue},
1264 outputs.push_back(filledTensor);
1266 bool isNanIgnoreMode =
false;
1267 if constexpr (std::is_same_v<OpTy, tosa::ReduceMinOp> ||
1268 std::is_same_v<OpTy, tosa::ReduceMaxOp>) {
1270 if (isa<FloatType>(elementTy) &&
1271 op.getNanMode() == NanPropagationMode::IGNORE) {
1272 isNanIgnoreMode =
true;
1278 auto trueValue = arith::ConstantOp::create(rewriter, loc, trueAttr);
1279 auto emptyBoolTensor =
1280 tensor::EmptyOp::create(rewriter, loc, reduceShape,
1281 trueValue.getType(), dynDims)
1283 auto allResultsNaNTensor =
1284 linalg::FillOp::create(rewriter, loc,
ValueRange{trueValue},
1296 inputs.push_back(input);
1297 outputs.push_back(allResultsNaNTensor);
1301 bool didEncounterError =
false;
1302 linalg::LinalgOp linalgOp = linalg::ReduceOp::create(
1303 rewriter, loc, inputs, outputs, axis,
1305 std::array<Value, 2> binaryArgs{
1306 blockArgs[0], isNanIgnoreMode ? blockArgs[2] : blockArgs[1]};
1309 if (binaryArgs[0].
getType() != accTy)
1310 binaryArgs[0] = arith::ExtFOp::create(
1311 nestedBuilder, nestedLoc,
TypeRange{accTy},
1312 ValueRange{binaryArgs[0]}, arith::ExtFOp::Properties{});
1317 didEncounterError =
true;
1320 if (isNanIgnoreMode) {
1321 auto inputValue = blockArgs[0];
1322 auto initialValue = blockArgs[2];
1323 auto oldAllResultsNanFlagValue = blockArgs[3];
1326 Value isNaN = arith::CmpFOp::create(nestedBuilder, op->getLoc(),
1327 arith::CmpFPredicate::UNO,
1328 inputValue, inputValue);
1330 auto selectOp = arith::SelectOp::create(nestedBuilder, op->getLoc(),
1331 isNaN, initialValue,
result);
1334 auto newAllResultsNanFlagValue = arith::AndIOp::create(
1335 nestedBuilder, op->getLoc(), oldAllResultsNanFlagValue, isNaN);
1336 resultsToYield.push_back(selectOp);
1337 resultsToYield.push_back(newAllResultsNanFlagValue);
1339 resultsToYield.push_back(
result);
1341 linalg::YieldOp::create(nestedBuilder, loc, resultsToYield);
1344 if (!didEncounterError)
1346 op,
"unable to create linalg.generic body for reduce op");
1348 if (isNanIgnoreMode) {
1358 auto nanValue = arith::ConstantOp::create(rewriter, loc, nanValueAttr);
1359 auto emptyNanTensor =
1360 tensor::EmptyOp::create(rewriter, loc, reduceShape, accTy, dynDims)
1362 auto nanFilledTensor =
1363 linalg::FillOp::create(rewriter, loc,
ValueRange{nanValue},
1369 auto finalEmptyTensor =
1370 tensor::EmptyOp::create(rewriter, loc, reduceShape, accTy, dynDims)
1376 ins.push_back(linalgOp->getOpResult(1));
1377 ins.push_back(nanFilledTensor);
1378 ins.push_back(linalgOp->getResult(0));
1379 outs.push_back(finalEmptyTensor);
1381 linalg::ElementwiseOp::create(rewriter, op->getLoc(), ins, outs,
1382 mlir::linalg::ElementwiseKind::select);
1383 linalgOp = linalgSelect;
1387 Value reducedRes = linalgOp->getResult(0);
1390 tensor::EmptyOp::create(rewriter, loc, reduceShape, elementTy, dynDims)
1393 const unsigned reducedRank =
1394 cast<ShapedType>(reducedRes.
getType()).getRank();
1397 linalg::GenericOp::create(
1403 Value truncf = arith::TruncFOp::create(nestedBuilder, nestedLoc,
1404 elementTy, args[0]);
1405 linalg::YieldOp::create(nestedBuilder, nestedLoc, truncf);
1411 uint64_t expandInputRank = cast<ShapedType>(reducedRes.
getType()).getRank();
1412 reassociationMap.resize(expandInputRank);
1414 for (uint64_t i = 0; i < expandInputRank; i++) {
1415 int32_t dimToPush = i > axis ? i + 1 : i;
1419 if (expandInputRank != 0) {
1420 int32_t expandedDim = axis < expandInputRank ? axis : expandInputRank - 1;
1421 reassociationMap[expandedDim].push_back(
1438 auto type = cast<RankedTensorType>(operand.
getType());
1440 cast<RankedTensorType>(other.
getType()).getRank() - type.getRank();
1441 if (missingDims <= 0)
1446 for (
int64_t dim = 0; dim < missingDims; ++dim) {
1448 dimensions.push_back(dim);
1451 Value empty = tensor::EmptyOp::create(
1452 rewriter, loc,
shape, type.getElementType(), type.getEncoding());
1453 return linalg::BroadcastOp::create(rewriter, loc, operand, empty, dimensions)
1466 auto type = cast<RankedTensorType>(operand.
getType());
1468 sliceShape.erase(sliceShape.begin() + dim);
1469 auto sliceType = RankedTensorType::get(sliceShape, type.getElementType(),
1470 type.getEncoding());
1472 rewriter, loc, operand, sliceType);
1477 Value empty = tensor::EmptyOp::create(
1478 rewriter, loc,
shape, type.getElementType(), type.getEncoding());
1479 return linalg::BroadcastOp::create(rewriter, loc, slice, empty,
1486static std::pair<Value, Value>
1490 int64_t bRank = cast<RankedTensorType>(
b.getType()).getRank();
1491 int64_t firstSharedDim = std::max(aRank, bRank) - std::min(aRank, bRank);
1497 for (
int64_t dim = firstSharedDim; dim < batchRank; ++dim) {
1498 auto aType = cast<RankedTensorType>(a.
getType());
1499 auto bType = cast<RankedTensorType>(
b.getType());
1500 int64_t aSize = aType.getDimSize(dim);
1501 int64_t bSize = bType.getDimSize(dim);
1502 if (aSize == bSize && ShapedType::isStatic(aSize))
1507 auto [size, masterOperand] =
1528 RankedTensorType outputType) {
1529 const int64_t outputRank = outputType.getRank();
1530 const int64_t batchRank = outputRank - 2;
1531 auto normalizedOperands =
1533 a = normalizedOperands.first;
1534 b = normalizedOperands.second;
1537 outputShape.reserve(outputRank);
1538 for (
int64_t batchDim = 0; batchDim < batchRank; ++batchDim)
1543 for (
auto [dim, size] : llvm::enumerate(outputShape))
1544 if (outputType.isDynamicDim(dim))
1545 dynamicDims.push_back(
1548 Value zero = arith::ConstantOp::create(
1549 rewriter, loc, rewriter.
getZeroAttr(outputType.getElementType()));
1550 Value emptyTensor = tensor::EmptyOp::create(
1551 rewriter, loc, outputType.getShape(), outputType.getElementType(),
1553 Value zeroTensor = linalg::FillOp::create(rewriter, loc,
ValueRange{zero},
1557 return {a,
b, zeroTensor};
1564class QuantizedMatMulTConverter :
public OpConversionPattern<tosa::MatMulTOp> {
1566 using OpConversionPattern<tosa::MatMulTOp>::OpConversionPattern;
1568 matchAndRewrite(tosa::MatMulTOp op, OpAdaptor adaptor,
1569 ConversionPatternRewriter &rewriter)
const final {
1571 auto aType = dyn_cast<RankedTensorType>(adaptor.getA().getType());
1572 auto bType = dyn_cast<RankedTensorType>(adaptor.getB().getType());
1573 auto outputType = dyn_cast<RankedTensorType>(op.getType());
1574 if (!aType || !bType || !outputType)
1575 return rewriter.notifyMatchFailure(op,
"only supports ranked tensors");
1577 FailureOr<int64_t> maybeAZp = op.getAZeroPoint();
1578 FailureOr<int64_t> maybeBZp = op.getBZeroPoint();
1579 if (failed(maybeAZp))
1580 return rewriter.notifyMatchFailure(
1581 op,
"input A zero point cannot be statically determined");
1582 if (failed(maybeBZp))
1583 return rewriter.notifyMatchFailure(
1584 op,
"input B zero point cannot be statically determined");
1586 const int64_t aZpVal = *maybeAZp;
1587 const int64_t bZpVal = *maybeBZp;
1589 if (aZpVal == 0 && bZpVal == 0)
1590 return rewriter.notifyMatchFailure(
1591 op,
"requires at least one nonzero zero point");
1593 const int64_t outputRank = outputType.getRank();
1594 const int64_t batchRank = outputRank - 2;
1596 rewriter, loc, adaptor.getA(), adaptor.getB(), outputType);
1597 Type outputElementType = outputType.getElementType();
1599 int64_t loopRank = outputRank + 1;
1601 llvm::map_to_vector(llvm::seq<int64_t>(0, loopRank), [&](
int64_t dim) {
1602 return rewriter.getAffineDimExpr(dim);
1605 dimensions.begin() + batchRank);
1606 aMap.push_back(dimensions[batchRank]);
1607 aMap.push_back(dimensions[batchRank + 2]);
1609 dimensions.begin() + batchRank);
1610 bMap.push_back(dimensions[batchRank + 1]);
1611 bMap.push_back(dimensions[batchRank + 2]);
1613 dimensions.begin() + batchRank);
1614 outputMap.push_back(dimensions[batchRank]);
1615 outputMap.push_back(dimensions[batchRank + 1]);
1620 SmallVector<utils::IteratorType> iteratorTypes(
1621 loopRank, utils::IteratorType::parallel);
1622 iteratorTypes.back() = utils::IteratorType::reduction;
1624 auto generic = linalg::GenericOp::create(
1625 rewriter, loc, outputType,
ValueRange{a,
b}, zeroTensor, indexingMaps,
1627 [=](OpBuilder &builder, Location nestedLoc,
ValueRange args) {
1628 auto extendToOutputType = [&](Value value) {
1629 if (value.getType() == outputElementType)
1631 return arith::ExtSIOp::create(builder, nestedLoc, outputElementType,
1636 Value aValue = extendToOutputType(args[0]);
1637 Value bValue = extendToOutputType(args[1]);
1638 Value aZp = arith::ConstantOp::create(
1640 builder.getIntegerAttr(outputElementType, aZpVal));
1641 Value bZp = arith::ConstantOp::create(
1643 builder.getIntegerAttr(outputElementType, bZpVal));
1645 arith::SubIOp::create(builder, nestedLoc, aValue, aZp);
1647 arith::SubIOp::create(builder, nestedLoc, bValue, bZp);
1649 arith::MulIOp::create(builder, nestedLoc, adjustedA, adjustedB);
1651 arith::AddIOp::create(builder, nestedLoc, args[2],
product);
1652 linalg::YieldOp::create(builder, nestedLoc, sum);
1654 rewriter.replaceOp(op, generic.getResult(0));
1659template <
typename SrcOp>
1660class PointwiseConverter :
public OpConversionPattern<SrcOp> {
1662 using OpConversionPattern<SrcOp>::OpConversionPattern;
1663 using typename OpConversionPattern<SrcOp>::OpAdaptor;
1666 matchAndRewrite(SrcOp op, OpAdaptor operands,
1667 ConversionPatternRewriter &rewriter)
const final {
1669 op, operands.getOperands(), rewriter, *this->getTypeConverter());
1679 auto inputType = cast<RankedTensorType>(input.
getType());
1680 auto elemType = inputType.getElementType();
1681 auto collapsedType = RankedTensorType::get({}, elemType);
1683 return tensor::CollapseShapeOp::create(rewriter, loc, collapsedType, input,
1690 output.reserve(input.size());
1692 for (
auto v : llvm::map_range(
1693 input, [](int32_t val) {
return static_cast<int8_t
>(val); })) {
1694 output.push_back(v);
1706static void setupLinalgGenericOpInputAndIndexingMap(
1709 bool isConstant, tosa::RescaleOp op,
Value &constant,
int64_t &arg,
1710 bool isShift =
false) {
1712 auto loc = op.getLoc();
1713 auto inputTy = cast<ShapedType>(op.getInput().getType());
1714 unsigned rank = inputTy.getRank();
1720 if (values.size() == 1) {
1721 IntegerAttr intAttr = isShift
1724 constant = arith::ConstantOp::create(rewriter, loc, intAttr);
1728 auto tensorType = RankedTensorType::get(
1729 {
static_cast<int64_t>(values.size())}, elementType);
1735 genericInputs.push_back(
1736 arith::ConstantOp::create(rewriter, loc, EltAttr));
1744 auto operand = isShift ? op.getShift() : op.getMultiplier();
1745 auto tensorType = dyn_cast<RankedTensorType>(operand.getType());
1746 if (tensorType && tensorType.hasStaticShape() &&
1747 tensorType.getShape()[0] == 1) {
1752 genericInputs.push_back(collapse1xNTensorToN(rewriter, operand, loc));
1753 indexingMaps.push_back(broadcastMap);
1755 genericInputs.push_back(operand);
1761 arg = indexingMaps.size() - 1;
1766 FailureOr<int64_t> maybeZp,
Location loc,
1768 bool isOutputZp =
false) {
1771 const uint32_t attrBitwidth =
1772 isOutputZp ? 32 : (bitwidth > 32 ? bitwidth : 32);
1779 result = blockArgs[zpArg];
1780 auto zpTy =
result.getType();
1781 if (zpTy.getIntOrFloatBitWidth() < attrBitwidth) {
1784 if (zpTy.isUnsignedInteger()) {
1786 UnrealizedConversionCastOp::create(
1791 if (zpTy.isUnsignedInteger()) {
1792 return arith::ExtUIOp::create(builder, loc, extendType,
result);
1794 return arith::ExtSIOp::create(builder, loc, extendType,
result);
1798 return arith::ConstantOp::create(builder, loc,
1799 IntegerAttr::get(extendType, *maybeZp));
1806 using OpRewritePattern<tosa::RescaleOp>::OpRewritePattern;
1808 LogicalResult matchAndRewrite(tosa::RescaleOp op,
1809 PatternRewriter &rewriter)
const final {
1810 auto loc = op.getLoc();
1811 auto input = op.getInput();
1812 auto inputTy = cast<ShapedType>(op.getInput().getType());
1813 auto outputTy = cast<ShapedType>(op.getOutput().getType());
1814 unsigned rank = inputTy.getRank();
1817 if (op.getRoundingMode() == RoundingMode::INEXACT_ROUND)
1819 op,
"tosa.rescale with rounding mode = 'INEXACT_ROUND' is not "
1820 "currently supported");
1821 if (op.getRoundingMode() == RoundingMode::DOUBLE_ROUND && !op.getScale32())
1823 op,
"tosa.rescale requires scale32 for double_round to be true");
1825 if (!isa<IntegerType>(inputTy.getElementType()))
1828 SmallVector<Value> dynDims;
1829 for (
int i = 0; i < outputTy.getRank(); i++) {
1830 if (outputTy.isDynamicDim(i)) {
1831 dynDims.push_back(tensor::DimOp::create(rewriter, loc, input, i));
1835 DenseElementsAttr shiftElems;
1836 bool isShiftConstant =
false;
1838 isShiftConstant =
true;
1840 DenseElementsAttr multiplierElems;
1841 bool isMultiplierConstant =
false;
1843 isMultiplierConstant =
true;
1845 llvm::SmallVector<int32_t> shiftValues;
1846 llvm::SmallVector<int32_t> multiplierValues;
1849 if (isMultiplierConstant && isShiftConstant) {
1851 shiftValues = llvm::map_to_vector(
1852 shiftElems.
getValues<IntegerAttr>(), [](IntegerAttr attr) -> int32_t {
1853 return static_cast<int32_t>(attr.getInt());
1856 llvm::map_to_vector(multiplierElems.
getValues<IntegerAttr>(),
1857 [](IntegerAttr attr) -> int32_t {
1858 return static_cast<int32_t>(attr.getInt());
1862 for (
int i = 0, s = multiplierValues.size(); i < s; i++) {
1863 if (shiftValues[i] > 63) {
1865 multiplierValues[i] = 0;
1870 doubleRound = op.getRoundingMode() == RoundingMode::DOUBLE_ROUND &&
1871 llvm::any_of(shiftValues, [](int32_t v) {
return v > 31; });
1873 doubleRound = op.getRoundingMode() == RoundingMode::DOUBLE_ROUND;
1875 RoundingMode roundingMode =
1876 doubleRound ? RoundingMode::DOUBLE_ROUND : RoundingMode::SINGLE_ROUND;
1878 SmallVector<AffineMap> indexingMaps = {
1880 SmallVector<Value, 4> genericInputs = {input};
1884 Value multiplierConstant;
1885 int64_t multiplierArg = 0;
1886 setupLinalgGenericOpInputAndIndexingMap(
1887 rewriter, multiplierValues, genericInputs, indexingMaps,
1888 isMultiplierConstant, op, multiplierConstant, multiplierArg);
1892 Value shiftConstant;
1893 int64_t shiftArg = 0;
1894 setupLinalgGenericOpInputAndIndexingMap(
1895 rewriter, shiftValues, genericInputs, indexingMaps, isShiftConstant, op,
1896 shiftConstant, shiftArg,
true);
1901 FailureOr<int64_t> maybeIZp = op.getInputZeroPoint();
1902 FailureOr<int64_t> maybeOZp = op.getOutputZeroPoint();
1912 genericInputs.push_back(
1913 collapse1xNTensorToN(rewriter, op->getOperand(3), loc));
1914 indexingMaps.push_back(broadcastMap);
1915 iZpArg = indexingMaps.size() - 1;
1919 genericInputs.push_back(
1920 collapse1xNTensorToN(rewriter, op->getOperand(4), loc));
1921 indexingMaps.push_back(broadcastMap);
1922 oZpArg = indexingMaps.size() - 1;
1929 Value emptyTensor = tensor::EmptyOp::create(
1930 rewriter, loc, outputTy.getShape(), outputTy.getElementType(),
1931 ArrayRef<Value>({dynDims}));
1933 auto linalgOp = linalg::GenericOp::create(
1934 rewriter, loc, outputTy, genericInputs,
ValueRange{emptyTensor},
1936 [&](OpBuilder &nestedBuilder, Location nestedLoc,
1938 Value value = blockArgs[0];
1939 Type valueTy = value.
getType();
1941 FailureOr<int64_t> maybeIZp = op.getInputZeroPoint();
1942 auto inputZp = getExtendZp(nestedBuilder, valueTy, maybeIZp,
1943 nestedLoc, blockArgs, iZpArg);
1945 FailureOr<int64_t> maybeOZp = op.getOutputZeroPoint();
1946 auto outputZp = getExtendZp(nestedBuilder, valueTy, maybeOZp,
1947 nestedLoc, blockArgs, oZpArg,
true);
1949 IntegerType outIntType =
1950 cast<IntegerType>(blockArgs.back().
getType());
1951 unsigned outBitWidth = outIntType.getWidth();
1952 assert(outBitWidth <= 32 &&
"Unexpected output zeropoint bitwidth");
1954 Value multiplier = multiplierConstant ? multiplierConstant
1955 : blockArgs[multiplierArg];
1956 Value shift = shiftConstant ? shiftConstant : blockArgs[shiftArg];
1959 value = UnrealizedConversionCastOp::create(
1960 nestedBuilder, nestedLoc,
1961 nestedBuilder.getIntegerType(
1967 if (op.getInputUnsigned()) {
1968 value = arith::ExtUIOp::create(nestedBuilder, nestedLoc,
1969 nestedBuilder.getI32Type(), value);
1971 value = arith::ExtSIOp::create(nestedBuilder, nestedLoc,
1972 nestedBuilder.getI32Type(), value);
1977 arith::SubIOp::create(nestedBuilder, nestedLoc, value, inputZp);
1979 value = tosa::ApplyScaleOp::create(nestedBuilder, loc,
1980 nestedBuilder.getI32Type(), value,
1981 multiplier, shift, roundingMode);
1985 arith::AddIOp::create(nestedBuilder, nestedLoc, value, outputZp);
1988 int32_t intMin = APInt::getSignedMinValue(outBitWidth).getSExtValue();
1989 int32_t intMax = APInt::getSignedMaxValue(outBitWidth).getSExtValue();
1992 if (op.getOutputUnsigned()) {
1994 intMax = APInt::getMaxValue(outBitWidth).getZExtValue();
1997 auto intMinVal = arith::ConstantOp::create(
1998 nestedBuilder, loc, nestedBuilder.getI32IntegerAttr(intMin));
1999 auto intMaxVal = arith::ConstantOp::create(
2000 nestedBuilder, loc, nestedBuilder.getI32IntegerAttr(intMax));
2003 nestedBuilder,
false);
2005 if (outIntType.getWidth() < 32) {
2006 value = arith::TruncIOp::create(
2007 nestedBuilder, nestedLoc,
2011 if (outIntType.isUnsignedInteger()) {
2012 value = UnrealizedConversionCastOp::create(nestedBuilder, nestedLoc,
2016 linalg::YieldOp::create(nestedBuilder, loc, value);
2019 rewriter.
replaceOp(op, linalgOp->getResults());
2029 using OpRewritePattern<tosa::ResizeOp>::OpRewritePattern;
2031 LogicalResult matchAndRewrite(tosa::ResizeOp op,
2032 PatternRewriter &rewriter)
const final {
2033 Location loc = op.getLoc();
2034 ImplicitLocOpBuilder builder(loc, rewriter);
2035 auto input = op.getInput();
2036 auto inputTy = cast<RankedTensorType>(input.getType());
2037 auto resultTy = cast<RankedTensorType>(op.getType());
2038 const bool isBilinear = op.getMode() == ResizeMode::BILINEAR;
2040 auto inputH = inputTy.getDimSize(1);
2041 auto inputW = inputTy.getDimSize(2);
2042 auto outputH = resultTy.getDimSize(1);
2043 auto outputW = resultTy.getDimSize(2);
2045 if (inputH != 1 || inputW != 1 || outputH != 1 || outputW != 1)
2047 op,
"tosa.resize is not a pure 1x1->1x1 image operation");
2049 if (op.getMode() != ResizeMode::NEAREST_NEIGHBOR &&
2050 op.getMode() != ResizeMode::BILINEAR)
2052 op,
"tosa.resize mode should be NEAREST_NEIGHBOR or BILINEAR");
2054 if (inputTy == resultTy) {
2059 SmallVector<int64_t> scale;
2065 SmallVector<ReassociationExprs, 4> reassociationMap(2);
2072 RankedTensorType::get({inputTy.getDimSize(0), inputTy.getDimSize(3)},
2073 inputTy.getElementType());
2074 Value collapse = tensor::CollapseShapeOp::create(builder, collapseTy, input,
2078 llvm::SmallVector<Value> outputDynSize;
2079 if (inputTy.isDynamicDim(0))
2080 outputDynSize.push_back(tensor::DimOp::create(builder, input, 0));
2081 if (inputTy.isDynamicDim(3))
2082 outputDynSize.push_back(tensor::DimOp::create(builder, input, 3));
2085 auto genericTy = collapseTy.clone(resultTy.getElementType());
2087 tensor::EmptyOp::create(builder, genericTy.getShape(),
2088 resultTy.getElementType(), outputDynSize);
2090 SmallVector<utils::IteratorType> iterators(genericTy.getRank(),
2091 utils::IteratorType::parallel);
2093 auto generic = linalg::GenericOp::create(
2095 ArrayRef<AffineMap>{genericMap, genericMap}, iterators,
2096 [=](OpBuilder &
b, Location loc,
ValueRange args) {
2097 Value value = args[0];
2099 if (inputTy.getElementType() != resultTy.getElementType()) {
2100 value = arith::ExtSIOp::create(
b, loc, resultTy.getElementType(),
2103 if (isBilinear && scale[0] != 0) {
2104 Value scaleY = arith::ConstantOp::create(
2105 b, loc,
b.getI32IntegerAttr(scale[0]));
2106 value = arith::MulIOp::create(
b, loc, value, scaleY);
2109 if (isBilinear && scale[2] != 0) {
2110 Value scaleX = arith::ConstantOp::create(
2111 b, loc,
b.getI32IntegerAttr(scale[2]));
2112 value = arith::MulIOp::create(
b, loc, value, scaleX);
2116 linalg::YieldOp::create(
b, loc, value);
2120 op, resultTy,
generic.getResults()[0], reassociationMap);
2132 LogicalResult matchAndRewrite(tosa::ResizeOp op,
2136 auto input = op.getInput();
2137 auto inputTy = dyn_cast<RankedTensorType>(input.getType());
2138 auto resultTy = dyn_cast<RankedTensorType>(op.getType());
2140 if (!inputTy || !resultTy)
2142 "requires ranked input/output types");
2144 auto batch = inputTy.getDimSize(0);
2145 auto channels = inputTy.getDimSize(3);
2146 auto inputH = inputTy.getDimSize(1);
2147 auto inputW = inputTy.getDimSize(2);
2148 auto outputH = resultTy.getDimSize(1);
2149 auto outputW = resultTy.getDimSize(2);
2151 if ((inputH != 1 || outputH == 1) && (inputW != 1 || outputW == 1))
2153 op,
"tosa.resize has no broadcasting behavior");
2158 resizeShape.push_back(batch);
2159 resizeShape.push_back(inputH == 1 ? 1 : outputH);
2160 resizeShape.push_back(inputW == 1 ? 1 : outputW);
2161 resizeShape.push_back(channels);
2163 auto resizeTy = resultTy.clone(resizeShape);
2165 tosa::ResizeOp::create(builder, resizeTy, input, op.getScale(),
2166 op.getOffset(), op.getBorder(), op.getMode());
2173 reassociationMap.push_back({});
2176 reassociationMap.push_back({});
2181 collapseShape.push_back(outputH);
2183 collapseShape.push_back(outputW);
2184 collapseShape.push_back(channels);
2186 auto collapseTy = resultTy.clone(collapseShape);
2187 Value collapse = tensor::CollapseShapeOp::create(builder, collapseTy,
2188 resize, reassociationMap);
2192 if (inputTy.isDynamicDim(0))
2193 outputDynSize.push_back(tensor::DimOp::create(builder, input, 0));
2194 if (inputTy.isDynamicDim(3))
2195 outputDynSize.push_back(tensor::DimOp::create(builder, input, 3));
2198 utils::IteratorType::parallel);
2199 Value empty = tensor::EmptyOp::create(
2200 builder, resultTy.getShape(), resultTy.getElementType(), outputDynSize);
2217 Value value = args[0];
2218 linalg::YieldOp::create(
b, loc, value);
2227 using OpRewritePattern<tosa::ResizeOp>::OpRewritePattern;
2229 LogicalResult matchAndRewrite(tosa::ResizeOp op,
2230 PatternRewriter &rewriter)
const final {
2231 Location loc = op.getLoc();
2232 ImplicitLocOpBuilder
b(loc, rewriter);
2233 auto input = op.getInput();
2234 auto inputTy = cast<ShapedType>(input.getType());
2235 auto resultTy = cast<ShapedType>(op.getType());
2236 auto resultETy = resultTy.getElementType();
2238 bool floatingPointMode = isa<FloatType>(resultETy);
2239 auto floatTy = resultETy;
2241 auto imageH = inputTy.getShape()[1];
2242 auto imageW = inputTy.getShape()[2];
2244 auto dynamicDimsOr =
2246 if (!dynamicDimsOr.has_value())
2248 op,
"unable to get dynamic dimensions of tosa.resize");
2250 if (op.getMode() != ResizeMode::NEAREST_NEIGHBOR &&
2251 op.getMode() != ResizeMode::BILINEAR)
2253 op,
"tosa.resize mode should be NEAREST_NEIGHBOR or BILINEAR");
2255 SmallVector<AffineMap, 2> affineMaps = {
2257 auto emptyTensor = tensor::EmptyOp::create(
b, resultTy.getShape(),
2258 resultETy, *dynamicDimsOr);
2259 auto genericOp = linalg::GenericOp::create(
2262 Value resize = genericOp.getResult(0);
2265 OpBuilder::InsertionGuard regionGuard(
b);
2266 b.createBlock(&genericOp.getRegion(), genericOp.getRegion().end(),
2268 Value batch = linalg::IndexOp::create(
b, 0);
2269 Value y = linalg::IndexOp::create(
b, 1);
2270 Value x = linalg::IndexOp::create(
b, 2);
2271 Value channel = linalg::IndexOp::create(
b, 3);
2274 arith::ConstantOp::create(
b,
b.getZeroAttr(
b.getI32Type()));
2275 Value zeroFp = arith::ConstantOp::create(
b,
b.getZeroAttr(floatTy));
2277 arith::ConstantOp::create(
b,
b.getI32IntegerAttr(imageH - 1));
2279 arith::ConstantOp::create(
b,
b.getI32IntegerAttr(imageW - 1));
2281 Value inY = arith::IndexCastOp::create(
b,
b.getI32Type(), y);
2282 Value inX = arith::IndexCastOp::create(
b,
b.getI32Type(), x);
2284 SmallVector<int64_t> scale, offset, border;
2289 op,
"tosa.resize scale/offset/border should have compile time "
2290 "constant values.");
2293 Value yScaleN, yScaleD, xScaleN, xScaleD;
2294 yScaleN = arith::ConstantOp::create(
b,
b.getI32IntegerAttr(scale[0]));
2295 yScaleD = arith::ConstantOp::create(
b,
b.getI32IntegerAttr(scale[1]));
2296 xScaleN = arith::ConstantOp::create(
b,
b.getI32IntegerAttr(scale[2]));
2297 xScaleD = arith::ConstantOp::create(
b,
b.getI32IntegerAttr(scale[3]));
2299 Value yOffset, xOffset, yBorder, xBorder;
2300 yOffset = arith::ConstantOp::create(
b,
b.getI32IntegerAttr(offset[0]));
2301 xOffset = arith::ConstantOp::create(
b,
b.getI32IntegerAttr(offset[1]));
2302 yBorder = arith::ConstantOp::create(
b,
b.getI32IntegerAttr(border[0]));
2303 xBorder = arith::ConstantOp::create(
b,
b.getI32IntegerAttr(border[1]));
2306 auto getIndexAndDeltaFp = [&](Value &index, Value &delta, Value in,
2307 Value scaleN, Value scaleD, Value offset,
2308 int size, ImplicitLocOpBuilder &
b) {
2316 Value val = arith::MulIOp::create(
b, in, scaleD);
2317 val = arith::AddIOp::create(
b, val, offset);
2318 index = arith::FloorDivSIOp::create(
b, val, scaleN);
2321 Value scaledIndex = arith::MulIOp::create(
b, index, scaleN);
2322 Value r = arith::SubIOp::create(
b, val, scaledIndex);
2323 Value rFp = arith::SIToFPOp::create(
b, floatTy, r);
2326 Value scaleNfp = arith::UIToFPOp::create(
b, floatTy, scaleN);
2327 delta = arith::DivFOp::create(
b, rFp, scaleNfp);
2331 auto getIndexAndDeltaInt = [&](Value &index, Value &delta, Value in,
2332 Value scaleN, Value scaleD, Value offset,
2333 int size, ImplicitLocOpBuilder &
b) {
2342 Value val = arith::MulIOp::create(
b, in, scaleD);
2343 val = arith::AddIOp::create(
b, val, offset);
2344 index = arith::FloorDivSIOp::create(
b, val, scaleN);
2345 delta = arith::MulIOp::create(
b, index, scaleN);
2346 delta = arith::SubIOp::create(
b, val, delta);
2349 Value ix, iy, dx, dy;
2350 if (floatingPointMode) {
2351 getIndexAndDeltaFp(iy, dy, inY, yScaleN, yScaleD, yOffset, imageH,
b);
2352 getIndexAndDeltaFp(ix, dx, inX, xScaleN, xScaleD, xOffset, imageW,
b);
2354 getIndexAndDeltaInt(iy, dy, inY, yScaleN, yScaleD, yOffset, imageH,
b);
2355 getIndexAndDeltaInt(ix, dx, inX, xScaleN, xScaleD, xOffset, imageW,
b);
2358 if (op.getMode() == ResizeMode::NEAREST_NEIGHBOR) {
2359 auto one = arith::ConstantOp::create(
b,
b.getI32IntegerAttr(1));
2361 auto getNearestIndexAndClamp = [&](Value val, Value dval, Value scale,
2362 Value
max,
int size,
2363 ImplicitLocOpBuilder &
b) -> Value {
2369 if (floatingPointMode) {
2371 arith::ConstantOp::create(
b,
b.getFloatAttr(floatTy, 0.5f));
2372 pred = arith::CmpFOp::create(
b, arith::CmpFPredicate::OGE, dval, h);
2374 Value dvalDouble = arith::ShLIOp::create(
b, dval, one);
2375 pred = arith::CmpIOp::create(
b, arith::CmpIPredicate::sge,
2379 auto offset = arith::SelectOp::create(
b, pred, one, zeroI32);
2380 val = arith::AddIOp::create(
b, val, offset);
2382 return arith::IndexCastOp::create(
b,
b.getIndexType(), val);
2385 iy = getNearestIndexAndClamp(iy, dy, yScaleN, hMax, imageH,
b);
2386 ix = getNearestIndexAndClamp(ix, dx, xScaleN, wMax, imageW,
b);
2388 Value
result = tensor::ExtractOp::create(
2391 linalg::YieldOp::create(
b,
result);
2394 assert(op.getMode() == ResizeMode::BILINEAR);
2396 auto oneVal = arith::ConstantOp::create(
b,
b.getI32IntegerAttr(1));
2398 auto getClampedIdxs = [&](Value &val0, Value &val1,
int size, Value in,
2399 Value
max, ImplicitLocOpBuilder &
b) {
2401 val1 = arith::AddIOp::create(
b, val0, oneVal);
2406 val0 = arith::IndexCastOp::create(
b,
b.getIndexType(), val0);
2407 val1 = arith::IndexCastOp::create(
b,
b.getIndexType(), val1);
2415 Value x0, x1, y0, y1;
2416 getClampedIdxs(y0, y1, imageH, iy, hMax,
b);
2417 getClampedIdxs(x0, x1, imageW, ix, wMax,
b);
2419 Value y0x0 = tensor::ExtractOp::create(
2421 Value y0x1 = tensor::ExtractOp::create(
2423 Value y1x0 = tensor::ExtractOp::create(
2425 Value y1x1 = tensor::ExtractOp::create(
2428 if (floatingPointMode) {
2430 arith::ConstantOp::create(
b,
b.getFloatAttr(floatTy, 1.0f));
2431 auto interpolate = [&](Value val0, Value val1, Value delta,
2433 ImplicitLocOpBuilder &
b) -> Value {
2436 Value oneMinusDelta = arith::SubFOp::create(
b, oneVal, delta);
2437 Value mul0 = arith::MulFOp::create(
b, val0, oneMinusDelta);
2438 Value mul1 = arith::MulFOp::create(
b, val1, delta);
2439 return arith::AddFOp::create(
b, mul0, mul1);
2445 Value topAcc = interpolate(y0x0, y0x1, dx, imageW,
b);
2450 Value bottomAcc = interpolate(y1x0, y1x1, dx, imageW,
b);
2454 Value
result = interpolate(topAcc, bottomAcc, dy, imageH,
b);
2455 linalg::YieldOp::create(
b,
result);
2458 y0x0 = arith::ExtSIOp::create(
b, resultETy, y0x0);
2459 y0x1 = arith::ExtSIOp::create(
b, resultETy, y0x1);
2460 y1x0 = arith::ExtSIOp::create(
b, resultETy, y1x0);
2461 y1x1 = arith::ExtSIOp::create(
b, resultETy, y1x1);
2464 if (resultETy.getIntOrFloatBitWidth() > deltaBitwidth) {
2465 dx = arith::ExtSIOp::create(
b, resultETy, dx);
2466 dy = arith::ExtSIOp::create(
b, resultETy, dy);
2469 Value yScaleNExt = yScaleN;
2470 Value xScaleNExt = xScaleN;
2472 const int64_t scaleBitwidth =
2474 if (resultETy.getIntOrFloatBitWidth() > scaleBitwidth) {
2475 yScaleNExt = arith::ExtSIOp::create(
b, resultETy, yScaleN);
2476 xScaleNExt = arith::ExtSIOp::create(
b, resultETy, xScaleN);
2479 auto interpolate = [](Value val0, Value val1, Value weight1,
2480 Value scale,
int inputSize,
2481 ImplicitLocOpBuilder &
b) -> Value {
2483 return arith::MulIOp::create(
b, val0, scale);
2484 Value weight0 = arith::SubIOp::create(
b, scale, weight1);
2485 Value mul0 = arith::MulIOp::create(
b, val0, weight0);
2486 Value mul1 = arith::MulIOp::create(
b, val1, weight1);
2487 return arith::AddIOp::create(
b, mul0, mul1);
2490 Value topAcc = interpolate(y0x0, y0x1, dx, xScaleNExt, imageW,
b);
2491 Value bottomAcc = interpolate(y1x0, y1x1, dx, xScaleNExt, imageW,
b);
2493 interpolate(topAcc, bottomAcc, dy, yScaleNExt, imageH,
b);
2494 linalg::YieldOp::create(
b,
result);
2507template <
typename SrcOp>
2510 using OpRewritePattern<SrcOp>::OpRewritePattern;
2512 LogicalResult matchAndRewrite(SrcOp op,
2513 PatternRewriter &rewriter)
const final {
2514 rewriter.
replaceOp(op, op.getOperation()->getOperands());
2519template <
typename SrcOp>
2522 ReduceConverter(MLIRContext *context,
bool allowNonFinites)
2523 : OpRewritePattern<SrcOp>(context), allowNonFinites(allowNonFinites) {}
2525 LogicalResult matchAndRewrite(SrcOp reduceOp,
2526 PatternRewriter &rewriter)
const final {
2532 bool allowNonFinites;
2537 using OpRewritePattern<tosa::ReverseOp>::OpRewritePattern;
2539 LogicalResult matchAndRewrite(tosa::ReverseOp op,
2540 PatternRewriter &rewriter)
const final {
2541 auto loc = op.getLoc();
2542 Value input = op.getInput1();
2543 auto inputTy = cast<ShapedType>(input.
getType());
2544 auto resultTy = cast<ShapedType>(op.getType());
2545 auto axis = op.getAxis();
2547 SmallVector<Value> dynDims;
2548 for (
int i = 0; i < inputTy.getRank(); i++) {
2549 if (inputTy.isDynamicDim(i)) {
2550 dynDims.push_back(tensor::DimOp::create(rewriter, loc, input, i));
2554 Value axisDimSize = tensor::DimOp::create(rewriter, loc, input, axis);
2557 auto emptyTensor = tensor::EmptyOp::create(
2558 rewriter, loc, inputTy.getShape(),
2559 inputTy.getElementType(), ArrayRef<Value>({dynDims}))
2561 SmallVector<AffineMap, 2> affineMaps = {
2565 op, resultTy, ArrayRef<Value>({}),
ValueRange{emptyTensor}, affineMaps,
2567 [&](OpBuilder &nestedBuilder, Location nestedLoc,
ValueRange args) {
2568 llvm::SmallVector<Value>
indices;
2569 for (
unsigned int i = 0; i < inputTy.getRank(); i++) {
2571 linalg::IndexOp::create(rewriter, nestedLoc, i).getResult();
2575 arith::SubIOp::create(rewriter, nestedLoc, axisDimSize, one);
2576 index = arith::SubIOp::create(rewriter, nestedLoc, sizeMinusOne,
2583 auto extract = tensor::ExtractOp::create(nestedBuilder, nestedLoc,
2585 linalg::YieldOp::create(nestedBuilder, op.getLoc(),
2586 extract.getResult());
2596struct TileConverter :
public OpConversionPattern<tosa::TileOp> {
2597 using OpConversionPattern<tosa::TileOp>::OpConversionPattern;
2600 matchAndRewrite(tosa::TileOp op, OpAdaptor adaptor,
2601 ConversionPatternRewriter &rewriter)
const override {
2602 auto loc = op.getLoc();
2603 auto input = op.getInput1();
2604 auto inputTy = cast<ShapedType>(input.
getType());
2605 auto inputShape = inputTy.getShape();
2606 auto resultTy = cast<ShapedType>(op.getType());
2607 auto elementTy = inputTy.getElementType();
2608 int64_t rank = inputTy.getRank();
2610 SmallVector<int64_t> multiples;
2611 if (
failed(op.getConstantMultiples(multiples)))
2615 SmallVector<int64_t, 2> genericShape;
2616 for (
int i = 0; i < rank; i++) {
2617 int64_t dim = multiples[i];
2618 genericShape.push_back(dim == -1 ? ShapedType::kDynamic : dim);
2619 genericShape.push_back(inputShape[i]);
2622 SmallVector<Value> dynDims;
2623 for (
int i = 0; i < inputTy.getRank(); i++) {
2624 if (inputTy.isDynamicDim(i) || multiples[i] == -1) {
2625 dynDims.push_back(tensor::DimOp::create(rewriter, loc, input, i));
2629 auto emptyTensor = tensor::EmptyOp::create(
2630 rewriter, op.getLoc(), genericShape, elementTy, dynDims);
2633 SmallVector<AffineExpr, 4> dimExprs;
2634 dimExprs.reserve(rank);
2635 for (
unsigned i = 0; i < rank; ++i)
2636 dimExprs.push_back(rewriter.getAffineDimExpr(i * 2 + 1));
2638 auto readAffineMap =
2640 rewriter.getContext());
2642 SmallVector<AffineMap, 2> affineMaps = {
2643 readAffineMap, rewriter.getMultiDimIdentityMap(genericShape.size())};
2645 auto genericOp = linalg::GenericOp::create(
2646 rewriter, loc, RankedTensorType::get(genericShape, elementTy), input,
2649 [&](OpBuilder &nestedBuilder, Location nestedLoc,
ValueRange args) {
2650 linalg::YieldOp::create(nestedBuilder, op.getLoc(), *args.begin());
2655 rewriter.replaceOpWithNewOp<tosa::ReshapeOp>(
2656 op, resultTy, genericOp.getResult(0), shapeValue);
2676 ArgMaxConverter(MLIRContext *context,
bool allowNonFinites)
2677 : OpRewritePattern<tosa::ArgMaxOp>(context),
2678 allowNonFinites(allowNonFinites) {}
2680 LogicalResult matchAndRewrite(tosa::ArgMaxOp argmaxOp,
2681 PatternRewriter &rewriter)
const final {
2682 auto loc = argmaxOp.getLoc();
2683 Value input = argmaxOp.getInput();
2684 auto inputTy = cast<ShapedType>(input.
getType());
2685 auto resultTy = cast<ShapedType>(argmaxOp.getOutput().getType());
2686 auto inElementTy = inputTy.getElementType();
2687 auto outElementTy = resultTy.getElementType();
2688 int axis = argmaxOp.getAxis();
2689 auto resultMaxTy = RankedTensorType::get(resultTy.getShape(), inElementTy);
2691 if (!isa<IntegerType>(outElementTy))
2692 return rewriter.notifyMatchFailure(
2694 "tosa.arg_max to linalg.* requires integer-like result type");
2696 SmallVector<Value> dynDims;
2697 for (
int i = 0; i < inputTy.getRank(); i++) {
2698 if (inputTy.isDynamicDim(i) && i != axis) {
2699 dynDims.push_back(tensor::DimOp::create(rewriter, loc, input, i));
2704 auto emptyTensorIdx =
2705 tensor::EmptyOp::create(rewriter, loc, resultTy.getShape(),
2706 outElementTy, dynDims)
2708 auto fillValueIdx = arith::ConstantOp::create(
2709 rewriter, loc, rewriter.getIntegerAttr(outElementTy, 0));
2710 auto filledTensorIdx =
2711 linalg::FillOp::create(rewriter, loc,
ValueRange{fillValueIdx},
2716 auto emptyTensorMax =
2717 tensor::EmptyOp::create(rewriter, loc, resultTy.getShape(), inElementTy,
2721 argmaxOp, inElementTy, rewriter, allowNonFinites);
2723 if (!fillValueMaxAttr)
2724 return rewriter.notifyMatchFailure(
2725 argmaxOp,
"unsupported tosa.argmax element type");
2728 arith::ConstantOp::create(rewriter, loc, fillValueMaxAttr);
2729 auto filledTensorMax =
2730 linalg::FillOp::create(rewriter, loc,
ValueRange{fillValueMax},
2736 SmallVector<utils::IteratorType, 4> iteratorTypes;
2737 iteratorTypes.resize(inputTy.getRank(), utils::IteratorType::parallel);
2738 iteratorTypes[axis] = utils::IteratorType::reduction;
2740 SmallVector<AffineExpr, 2> srcExprs;
2741 SmallVector<AffineExpr, 2> dstExprs;
2742 for (
int i = 0, rank = inputTy.getRank(); i != rank; ++i) {
2748 bool didEncounterError =
false;
2750 rewriter.getContext());
2751 auto linalgOp = linalg::GenericOp::create(
2752 rewriter, loc, ArrayRef<Type>({resultTy, resultMaxTy}), input,
2753 ValueRange({filledTensorIdx, filledTensorMax}), maps, iteratorTypes,
2754 [&](OpBuilder &nestedBuilder, Location nestedLoc,
2756 auto newValue = blockArgs[0];
2757 auto oldIndex = blockArgs[1];
2758 auto oldValue = blockArgs[2];
2760 Value newIndex = arith::IndexCastOp::create(
2761 rewriter, nestedLoc, oldIndex.getType(),
2762 linalg::IndexOp::create(rewriter, loc, axis));
2765 if (isa<FloatType>(inElementTy)) {
2766 if (argmaxOp.getNanMode() == NanPropagationMode::IGNORE) {
2769 predicate = arith::CmpFOp::create(rewriter, nestedLoc,
2770 arith::CmpFPredicate::OGT,
2771 newValue, oldValue);
2776 Value gt = arith::CmpFOp::create(rewriter, nestedLoc,
2777 arith::CmpFPredicate::UGT,
2778 newValue, oldValue);
2779 Value oldNonNaN = arith::CmpFOp::create(rewriter, nestedLoc,
2780 arith::CmpFPredicate::ORD,
2781 oldValue, oldValue);
2782 predicate = arith::AndIOp::create(
2783 rewriter, nestedLoc, rewriter.getI1Type(), gt, oldNonNaN);
2785 }
else if (isa<IntegerType>(inElementTy)) {
2786 predicate = arith::CmpIOp::create(rewriter, nestedLoc,
2787 arith::CmpIPredicate::sgt,
2788 newValue, oldValue);
2790 didEncounterError =
true;
2794 auto resultMax = arith::SelectOp::create(
2795 rewriter, nestedLoc, predicate, newValue, oldValue);
2796 auto resultIndex = arith::SelectOp::create(
2797 rewriter, nestedLoc, predicate, newIndex, oldIndex);
2798 linalg::YieldOp::create(nestedBuilder, nestedLoc,
2802 if (didEncounterError)
2803 return rewriter.notifyMatchFailure(
2804 argmaxOp,
"unsupported tosa.argmax element type");
2806 rewriter.replaceOp(argmaxOp, linalgOp.getResult(0));
2811 bool allowNonFinites;
2814class GatherConverter :
public OpConversionPattern<tosa::GatherOp> {
2816 using OpConversionPattern<tosa::GatherOp>::OpConversionPattern;
2818 matchAndRewrite(tosa::GatherOp op, OpAdaptor adaptor,
2819 ConversionPatternRewriter &rewriter)
const final {
2820 auto input = adaptor.getOperands()[0];
2821 auto indices = adaptor.getOperands()[1];
2823 auto valuesTy = dyn_cast<RankedTensorType>(op.getValues().getType());
2824 auto resultTy = dyn_cast<RankedTensorType>(op.getType());
2825 if (!valuesTy || !resultTy)
2826 return rewriter.notifyMatchFailure(op,
"unranked tensors not supported");
2828 auto dynamicDims = inferDynamicDimsForGather(
2829 rewriter, op.getLoc(), adaptor.getValues(), adaptor.getIndices());
2831 auto resultElementTy = resultTy.getElementType();
2833 auto loc = op.getLoc();
2835 tensor::EmptyOp::create(rewriter, loc, resultTy.getShape(),
2836 resultElementTy, dynamicDims)
2839 SmallVector<AffineMap, 2> affineMaps = {
2841 resultTy.getRank(), 0,
2842 {rewriter.getAffineDimExpr(0), rewriter.getAffineDimExpr(1)},
2843 rewriter.getContext()),
2844 rewriter.getMultiDimIdentityMap(resultTy.getRank())};
2846 auto genericOp = linalg::GenericOp::create(
2850 [&](OpBuilder &
b, Location loc,
ValueRange args) {
2851 auto indexValue = args[0];
2852 auto index0 = linalg::IndexOp::create(rewriter, loc, 0);
2853 Value index1 = arith::IndexCastOp::create(
2854 rewriter, loc, rewriter.getIndexType(), indexValue);
2855 auto index2 = linalg::IndexOp::create(rewriter, loc, 2);
2856 Value extract = tensor::ExtractOp::create(
2857 rewriter, loc, input,
ValueRange{index0, index1, index2});
2858 linalg::YieldOp::create(rewriter, loc, extract);
2860 rewriter.replaceOp(op, genericOp.getResult(0));
2864 static llvm::SmallVector<Value> inferDynamicDimsForGather(OpBuilder &builder,
2868 llvm::SmallVector<Value> results;
2870 auto addDynamicDimension = [&](Value source, int64_t dim) {
2872 if (
auto dimValue = llvm::dyn_cast_if_present<Value>(sz))
2873 results.push_back(dimValue);
2876 addDynamicDimension(values, 0);
2877 addDynamicDimension(
indices, 1);
2878 addDynamicDimension(values, 2);
2883class RowGatherConverter :
public OpConversionPattern<tosa::RowGatherOp> {
2885 using OpConversionPattern<tosa::RowGatherOp>::OpConversionPattern;
2887 matchAndRewrite(tosa::RowGatherOp op, OpAdaptor adaptor,
2888 ConversionPatternRewriter &rewriter)
const final {
2889 auto valuesTy = dyn_cast<RankedTensorType>(adaptor.getValues().getType());
2890 auto indicesTy = dyn_cast<RankedTensorType>(adaptor.getIndices().getType());
2892 dyn_cast<RankedTensorType>(adaptor.getRowCount().getType());
2893 auto resultTy = dyn_cast<RankedTensorType>(op.getType());
2894 if (!valuesTy || !indicesTy || !rowCountTy || !resultTy)
2895 return rewriter.notifyMatchFailure(op,
"unranked tensors not supported");
2897 Location loc = op.getLoc();
2899 Value rowCount = tensor::ExtractOp::create(
2900 rewriter, loc, adaptor.getRowCount(),
ValueRange{zero});
2901 Value rowCountIndex = arith::IndexCastOp::create(
2902 rewriter, loc, rewriter.getIndexType(), rowCount);
2904 SmallVector<Value> dynamicDims;
2905 if (resultTy.isDynamicDim(0))
2906 dynamicDims.push_back(
2907 tensor::DimOp::create(rewriter, loc, adaptor.getValues(), 0));
2908 if (resultTy.isDynamicDim(1)) {
2909 Value indicesWidth =
2910 tensor::DimOp::create(rewriter, loc, adaptor.getIndices(), 1);
2911 dynamicDims.push_back(
2912 arith::MulIOp::create(rewriter, loc, indicesWidth, rowCountIndex));
2914 if (resultTy.isDynamicDim(2))
2915 dynamicDims.push_back(
2916 tensor::DimOp::create(rewriter, loc, adaptor.getValues(), 2));
2919 tensor::EmptyOp::create(rewriter, loc, resultTy.getShape(),
2920 resultTy.getElementType(), dynamicDims);
2921 SmallVector<AffineMap> affineMaps = {
2922 rewriter.getMultiDimIdentityMap(resultTy.getRank())};
2924 auto genericOp = linalg::GenericOp::create(
2925 rewriter, loc, ArrayRef<Type>{resultTy},
ValueRange{},
2928 [&](OpBuilder &builder, Location nestedLoc,
ValueRange) {
2929 Value batch = linalg::IndexOp::create(builder, nestedLoc, 0);
2930 Value outputRow = linalg::IndexOp::create(builder, nestedLoc, 1);
2931 Value channel = linalg::IndexOp::create(builder, nestedLoc, 2);
2932 Value indexSlot = arith::DivUIOp::create(builder, nestedLoc,
2933 outputRow, rowCountIndex);
2934 Value rowOffset = arith::RemUIOp::create(builder, nestedLoc,
2935 outputRow, rowCountIndex);
2936 Value index = tensor::ExtractOp::create(builder, nestedLoc,
2937 adaptor.getIndices(),
2939 Value row = arith::IndexCastOp::create(builder, nestedLoc,
2940 builder.getIndexType(), index);
2941 row = arith::AddIOp::create(builder, nestedLoc, row, rowOffset);
2943 tensor::ExtractOp::create(builder, nestedLoc, adaptor.getValues(),
2945 linalg::YieldOp::create(builder, nestedLoc,
result);
2947 rewriter.replaceOp(op, genericOp.getResult(0));
2957 using OpRewritePattern<tosa::TableOp>::OpRewritePattern;
2959 LogicalResult matchAndRewrite(tosa::TableOp op,
2960 PatternRewriter &rewriter)
const final {
2961 auto loc = op.getLoc();
2962 Value input = op.getInput1();
2963 Value table = op.getTable();
2964 auto inputTy = cast<ShapedType>(input.
getType());
2965 auto tableTy = cast<ShapedType>(table.
getType());
2966 auto resultTy = cast<ShapedType>(op.getType());
2968 auto inputElementTy = inputTy.getElementType();
2969 auto tableElementTy = tableTy.getElementType();
2970 auto resultElementTy = resultTy.getElementType();
2972 SmallVector<Value> dynDims;
2973 for (
int i = 0; i < resultTy.getRank(); ++i) {
2974 if (inputTy.isDynamicDim(i)) {
2976 tensor::DimOp::create(rewriter, loc, op.getOperand(0), i));
2981 tensor::EmptyOp::create(rewriter, loc, resultTy.getShape(),
2982 resultElementTy, dynDims)
2985 SmallVector<AffineMap, 2> affineMaps = {
2986 rewriter.getMultiDimIdentityMap(resultTy.getRank()),
2987 rewriter.getMultiDimIdentityMap(resultTy.getRank())};
2989 auto genericOp = linalg::GenericOp::create(
2992 rewriter.replaceOp(op, genericOp.getResult(0));
2995 OpBuilder::InsertionGuard regionGuard(rewriter);
2996 Block *block = rewriter.createBlock(
2997 &genericOp.getRegion(), genericOp.getRegion().end(),
2998 TypeRange({inputElementTy, resultElementTy}), {loc, loc});
3001 rewriter.setInsertionPointToStart(block);
3002 if (inputElementTy.isInteger(8) && tableElementTy.isInteger(8) &&
3003 resultElementTy.isInteger(8)) {
3004 Value index = arith::IndexCastOp::create(
3005 rewriter, loc, rewriter.getIndexType(), inputValue);
3007 index = arith::AddIOp::create(rewriter, loc, rewriter.getIndexType(),
3010 tensor::ExtractOp::create(rewriter, loc, table,
ValueRange{index});
3011 linalg::YieldOp::create(rewriter, loc, extract);
3015 if (inputElementTy.isInteger(16) && tableElementTy.isInteger(16) &&
3016 resultElementTy.isInteger(32)) {
3017 Value extend = arith::ExtSIOp::create(
3018 rewriter, loc, rewriter.getI32Type(), inputValue);
3020 auto offset = arith::ConstantOp::create(
3021 rewriter, loc, rewriter.getI32IntegerAttr(32768));
3022 auto seven = arith::ConstantOp::create(rewriter, loc,
3023 rewriter.getI32IntegerAttr(7));
3024 auto one = arith::ConstantOp::create(rewriter, loc,
3025 rewriter.getI32IntegerAttr(1));
3026 auto b1111111 = arith::ConstantOp::create(
3027 rewriter, loc, rewriter.getI32IntegerAttr(127));
3033 auto extendAdd = arith::AddIOp::create(rewriter, loc, extend, offset);
3034 Value index = arith::ShRUIOp::create(rewriter, loc, extendAdd, seven);
3036 arith::AndIOp::create(rewriter, loc, extendAdd, b1111111);
3041 Value indexPlusOne = arith::AddIOp::create(rewriter, loc, index, one);
3043 index = arith::IndexCastOp::create(rewriter, loc,
3044 rewriter.getIndexType(), index);
3045 indexPlusOne = arith::IndexCastOp::create(
3046 rewriter, loc, rewriter.getIndexType(), indexPlusOne);
3049 tensor::ExtractOp::create(rewriter, loc, table,
ValueRange{index});
3050 Value
next = tensor::ExtractOp::create(rewriter, loc, table,
3054 arith::ExtSIOp::create(rewriter, loc, rewriter.getI32Type(), base);
3056 arith::ExtSIOp::create(rewriter, loc, rewriter.getI32Type(), next);
3060 Value baseScaled = arith::ShLIOp::create(rewriter, loc, base, seven);
3061 Value diff = arith::SubIOp::create(rewriter, loc, next, base);
3062 Value diffScaled = arith::MulIOp::create(rewriter, loc, diff, fraction);
3064 arith::AddIOp::create(rewriter, loc, baseScaled, diffScaled);
3066 linalg::YieldOp::create(rewriter, loc,
result);
3072 return rewriter.notifyMatchFailure(
3073 op,
"unable to create body for tosa.table op");
3078 using OpRewritePattern<RFFT2dOp>::OpRewritePattern;
3080 static bool isRankedTensor(Type type) {
return isa<RankedTensorType>(type); }
3082 static OpFoldResult halfPlusOne(OpBuilder &builder, Location loc,
3088 auto divBy2 = builder.
createOrFold<arith::DivUIOp>(loc, value, two);
3089 auto plusOne = builder.
createOrFold<arith::AddIOp>(loc, divBy2, one);
3093 static RankedTensorType
3094 computeOutputShape(OpBuilder &builder, Location loc, Value input,
3095 llvm::SmallVectorImpl<Value> &dynamicSizes) {
3101 dims[2] = halfPlusOne(builder, loc, dims[2]);
3103 llvm::SmallVector<int64_t, 3> staticSizes;
3106 auto elementType = cast<RankedTensorType>(input.
getType()).getElementType();
3107 return RankedTensorType::get(staticSizes, elementType);
3110 static Value createZeroTensor(PatternRewriter &rewriter, Location loc,
3111 RankedTensorType type,
3112 llvm::ArrayRef<Value> dynamicSizes) {
3114 tensor::EmptyOp::create(rewriter, loc, type, dynamicSizes);
3115 auto fillValueAttr = rewriter.
getZeroAttr(type.getElementType());
3116 auto fillValue = arith::ConstantOp::create(rewriter, loc, fillValueAttr);
3118 linalg::FillOp::create(rewriter, loc,
ValueRange{fillValue},
3121 return filledTensor;
3124 static Value castIndexToFloat(OpBuilder &builder, Location loc,
3125 FloatType type, Value value) {
3126 auto integerVal = arith::IndexCastUIOp::create(
3128 type.getIntOrFloatBitWidth() > 32 ? builder.
getI64Type()
3132 return arith::UIToFPOp::create(builder, loc, type, integerVal);
3135 static Value createLinalgIndex(OpBuilder &builder, Location loc,
3136 FloatType type, int64_t index) {
3137 auto indexVal = linalg::IndexOp::create(builder, loc, index);
3138 return castIndexToFloat(builder, loc, type, indexVal);
3141 template <
typename... Args>
3142 static llvm::SmallVector<AffineExpr, 4> affineDimsExpr(OpBuilder &builder,
3147 LogicalResult matchAndRewrite(RFFT2dOp rfft2d,
3148 PatternRewriter &rewriter)
const override {
3149 if (!llvm::all_of(rfft2d->getOperandTypes(), isRankedTensor) ||
3150 !llvm::all_of(rfft2d->getResultTypes(), isRankedTensor)) {
3152 "only supports ranked tensors");
3155 auto loc = rfft2d.getLoc();
3156 auto input = rfft2d.getInputReal();
3158 dyn_cast<FloatType>(cast<ShapedType>(input.
getType()).getElementType());
3161 "only supports float element types");
3164 llvm::SmallVector<Value> dynamicSizes;
3165 auto outputType = computeOutputShape(rewriter, loc, input, dynamicSizes);
3168 llvm::SmallVector<utils::IteratorType, 5> iteratorTypes = {
3169 utils::IteratorType::parallel, utils::IteratorType::parallel,
3170 utils::IteratorType::parallel, utils::IteratorType::reduction,
3171 utils::IteratorType::reduction};
3174 llvm::SmallVector<Value> genericOpInputs = {input};
3175 llvm::SmallVector<Value> genericOpOutputs = {
3176 createZeroTensor(rewriter, loc, outputType, dynamicSizes),
3177 createZeroTensor(rewriter, loc, outputType, dynamicSizes)};
3181 llvm::ArrayRef{affineDimsExpr(rewriter, 0, 3, 4),
3182 affineDimsExpr(rewriter, 0, 1, 2),
3183 affineDimsExpr(rewriter, 0, 1, 2)},
3187 auto dimH = rewriter.
createOrFold<tensor::DimOp>(loc, input, 1);
3188 auto dimW = rewriter.
createOrFold<tensor::DimOp>(loc, input, 2);
3191 auto zeroFloat = arith::ConstantOp::create(
3192 rewriter, loc, rewriter.
getZeroAttr(elementType));
3193 auto twoPiAttr = rewriter.
getFloatAttr(elementType, 6.283185307179586);
3194 auto twoPi = arith::ConstantOp::create(rewriter, loc, twoPiAttr);
3199 auto constH = castIndexToFloat(rewriter, loc, elementType, dimH);
3200 auto constW = castIndexToFloat(rewriter, loc, elementType, dimW);
3201 auto halfH = index::DivUOp::create(rewriter, loc, dimH, twoIndex);
3202 auto halfW = index::DivUOp::create(rewriter, loc, dimW, twoIndex);
3204 auto buildBody = [&](OpBuilder &builder, Location loc,
ValueRange args) {
3205 Value valReal = args[0];
3206 Value sumReal = args[1];
3207 Value sumImag = args[2];
3210 Value oy = linalg::IndexOp::create(builder, loc, 1);
3211 Value ox = linalg::IndexOp::create(builder, loc, 2);
3212 Value iy = linalg::IndexOp::create(builder, loc, 3);
3213 Value ix = linalg::IndexOp::create(builder, loc, 4);
3218 auto iyXoy = index::MulOp::create(builder, loc, iy, oy);
3219 auto ixXox = index::MulOp::create(builder, loc, ix, ox);
3221 auto iyRem = index::RemUOp::create(builder, loc, iyXoy, dimH);
3222 auto ixRem = index::RemUOp::create(builder, loc, ixXox, dimW);
3224 auto iyRemFloat = castIndexToFloat(builder, loc, elementType, iyRem);
3225 auto ixRemFloat = castIndexToFloat(builder, loc, elementType, ixRem);
3227 auto yComponent = arith::DivFOp::create(builder, loc, iyRemFloat, constH);
3228 auto xComponent = arith::DivFOp::create(builder, loc, ixRemFloat, constW);
3229 auto sumXY = arith::AddFOp::create(builder, loc, yComponent, xComponent);
3230 auto angle = arith::MulFOp::create(builder, loc, twoPi, sumXY);
3237 auto iyIs0 = arith::CmpIOp::create(builder, loc, arith::CmpIPredicate::eq,
3239 auto iyIsHalfH = arith::CmpIOp::create(
3240 builder, loc, arith::CmpIPredicate::eq, iyRem, halfH);
3241 auto ixIs0 = arith::CmpIOp::create(builder, loc, arith::CmpIPredicate::eq,
3243 auto ixIsHalfW = arith::CmpIOp::create(
3244 builder, loc, arith::CmpIPredicate::eq, ixRem, halfW);
3246 auto iyIsSinSkippable =
3247 arith::OrIOp::create(builder, loc, iyIs0, iyIsHalfH);
3248 auto ixIsSinSkippable =
3249 arith::OrIOp::create(builder, loc, ixIs0, ixIsHalfW);
3250 auto shouldSkipSin = arith::AndIOp::create(builder, loc, iyIsSinSkippable,
3255 auto cosAngle = math::CosOp::create(builder, loc, angle);
3256 auto sinAngle = math::SinOp::create(builder, loc, angle);
3257 auto imagWeight = arith::SelectOp::create(builder, loc, shouldSkipSin,
3258 zeroFloat, sinAngle);
3259 auto realComponent =
3260 arith::MulFOp::create(builder, loc, valReal, cosAngle);
3261 auto imagComponent =
3262 arith::MulFOp::create(builder, loc, valReal, imagWeight);
3267 arith::AddFOp::create(builder, loc, sumReal, realComponent);
3269 arith::SubFOp::create(builder, loc, sumImag, imagComponent);
3271 linalg::YieldOp::create(builder, loc,
ValueRange{outReal, outImag});
3275 rfft2d, rfft2d.getResultTypes(), genericOpInputs, genericOpOutputs,
3276 indexingMaps, iteratorTypes, buildBody);
3285 LogicalResult matchAndRewrite(FFT2dOp fft2d,
3286 PatternRewriter &rewriter)
const override {
3287 if (!llvm::all_of(fft2d->getOperandTypes(),
3288 RFFT2dConverter::isRankedTensor) ||
3289 !llvm::all_of(fft2d->getResultTypes(),
3290 RFFT2dConverter::isRankedTensor)) {
3294 Location loc = fft2d.getLoc();
3295 Value input_real = fft2d.getInputReal();
3296 Value input_imag = fft2d.getInputImag();
3297 BoolAttr inverse = fft2d.getInverseAttr();
3299 auto real_el_ty = cast<FloatType>(
3300 cast<ShapedType>(input_real.
getType()).getElementType());
3301 [[maybe_unused]]
auto imag_el_ty = cast<FloatType>(
3302 cast<ShapedType>(input_imag.
getType()).getElementType());
3304 assert(real_el_ty == imag_el_ty);
3307 SmallVector<Value> dynamicSizes;
3312 SmallVector<int64_t, 3> staticSizes;
3315 auto outputType = RankedTensorType::get(staticSizes, real_el_ty);
3318 SmallVector<utils::IteratorType, 5> iteratorTypes = {
3319 utils::IteratorType::parallel, utils::IteratorType::parallel,
3320 utils::IteratorType::parallel, utils::IteratorType::reduction,
3321 utils::IteratorType::reduction};
3324 SmallVector<Value> genericOpInputs = {input_real, input_imag};
3325 SmallVector<Value> genericOpOutputs = {
3326 RFFT2dConverter::createZeroTensor(rewriter, loc, outputType,
3328 RFFT2dConverter::createZeroTensor(rewriter, loc, outputType,
3333 ArrayRef{RFFT2dConverter::affineDimsExpr(rewriter, 0, 3, 4),
3334 RFFT2dConverter::affineDimsExpr(rewriter, 0, 3, 4),
3335 RFFT2dConverter::affineDimsExpr(rewriter, 0, 1, 2),
3336 RFFT2dConverter::affineDimsExpr(rewriter, 0, 1, 2)},
3340 auto dimH = rewriter.
createOrFold<tensor::DimOp>(loc, input_real, 1);
3341 auto dimW = rewriter.
createOrFold<tensor::DimOp>(loc, input_real, 2);
3344 auto twoPiAttr = rewriter.
getFloatAttr(real_el_ty, 6.283185307179586);
3345 auto twoPi = arith::ConstantOp::create(rewriter, loc, twoPiAttr);
3347 RFFT2dConverter::castIndexToFloat(rewriter, loc, real_el_ty, dimH);
3349 RFFT2dConverter::castIndexToFloat(rewriter, loc, real_el_ty, dimW);
3351 auto buildBody = [&](OpBuilder &builder, Location loc,
ValueRange args) {
3352 Value valReal = args[0];
3353 Value valImag = args[1];
3354 Value sumReal = args[2];
3355 Value sumImag = args[3];
3358 Value oy = linalg::IndexOp::create(builder, loc, 1);
3359 Value ox = linalg::IndexOp::create(builder, loc, 2);
3360 Value iy = linalg::IndexOp::create(builder, loc, 3);
3361 Value ix = linalg::IndexOp::create(builder, loc, 4);
3365 auto iyXoy = index::MulOp::create(builder, loc, iy, oy);
3366 auto ixXox = index::MulOp::create(builder, loc, ix, ox);
3368 auto iyRem = index::RemUOp::create(builder, loc, iyXoy, dimH);
3369 auto ixRem = index::RemUOp::create(builder, loc, ixXox, dimW);
3372 RFFT2dConverter::castIndexToFloat(builder, loc, real_el_ty, iyRem);
3374 RFFT2dConverter::castIndexToFloat(builder, loc, real_el_ty, ixRem);
3376 auto yComponent = arith::DivFOp::create(builder, loc, iyRemFloat, constH);
3377 auto xComponent = arith::DivFOp::create(builder, loc, ixRemFloat, constW);
3379 auto sumXY = arith::AddFOp::create(builder, loc, yComponent, xComponent);
3380 auto angle = arith::MulFOp::create(builder, loc, twoPi, sumXY);
3383 angle = arith::MulFOp::create(
3384 builder, loc, angle,
3385 arith::ConstantOp::create(rewriter, loc,
3391 auto cosAngle = math::CosOp::create(builder, loc, angle);
3392 auto sinAngle = math::SinOp::create(builder, loc, angle);
3394 auto rcos = arith::MulFOp::create(builder, loc, valReal, cosAngle);
3395 auto rsin = arith::MulFOp::create(builder, loc, valImag, sinAngle);
3396 auto realComponent = arith::AddFOp::create(builder, loc, rcos, rsin);
3398 auto icos = arith::MulFOp::create(builder, loc, valImag, cosAngle);
3399 auto isin = arith::MulFOp::create(builder, loc, valReal, sinAngle);
3401 auto imagComponent = arith::SubFOp::create(builder, loc, icos, isin);
3406 arith::AddFOp::create(builder, loc, sumReal, realComponent);
3408 arith::AddFOp::create(builder, loc, sumImag, imagComponent);
3410 linalg::YieldOp::create(builder, loc,
ValueRange{outReal, outImag});
3414 fft2d, fft2d.getResultTypes(), genericOpInputs, genericOpOutputs,
3415 indexingMaps, iteratorTypes, buildBody);
3428 patterns->
add<GenericResizeConverter>(patterns->
getContext(),
3432 patterns->
add<MaterializeResizeBroadcast>(patterns->
getContext(),
3437 PointwiseConverter<tosa::AddOp>,
3438 PointwiseConverter<tosa::SubOp>,
3439 PointwiseConverter<tosa::MulOp>,
3440 PointwiseConverter<tosa::IntDivOp>,
3441 PointwiseConverter<tosa::NegateOp>,
3442 PointwiseConverter<tosa::PowOp>,
3443 PointwiseConverter<tosa::ReciprocalOp>,
3444 PointwiseConverter<tosa::RsqrtOp>,
3445 PointwiseConverter<tosa::LogOp>,
3446 PointwiseConverter<tosa::ExpOp>,
3447 PointwiseConverter<tosa::AbsOp>,
3448 PointwiseConverter<tosa::SinOp>,
3449 PointwiseConverter<tosa::CosOp>,
3450 PointwiseConverter<tosa::TanhOp>,
3451 PointwiseConverter<tosa::ErfOp>,
3452 PointwiseConverter<tosa::BitwiseAndOp>,
3453 PointwiseConverter<tosa::BitwiseOrOp>,
3454 PointwiseConverter<tosa::BitwiseNotOp>,
3455 PointwiseConverter<tosa::BitwiseXorOp>,
3456 PointwiseConverter<tosa::LogicalAndOp>,
3457 PointwiseConverter<tosa::LogicalNotOp>,
3458 PointwiseConverter<tosa::LogicalOrOp>,
3459 PointwiseConverter<tosa::LogicalXorOp>,
3460 PointwiseConverter<tosa::CastOp>,
3461 PointwiseConverter<tosa::LogicalLeftShiftOp>,
3462 PointwiseConverter<tosa::LogicalRightShiftOp>,
3463 PointwiseConverter<tosa::ArithmeticRightShiftOp>,
3464 PointwiseConverter<tosa::ClzOp>,
3465 PointwiseConverter<tosa::SelectOp>,
3466 PointwiseConverter<tosa::GreaterOp>,
3467 PointwiseConverter<tosa::GreaterEqualOp>,
3468 PointwiseConverter<tosa::EqualOp>,
3469 PointwiseConverter<tosa::MaximumOp>,
3470 PointwiseConverter<tosa::MinimumOp>,
3471 PointwiseConverter<tosa::CeilOp>,
3472 PointwiseConverter<tosa::FloorOp>,
3473 PointwiseConverter<tosa::ClampOp>,
3474 PointwiseConverter<tosa::SigmoidOp>
3477 patterns->
add<QuantizedMatMulTConverter>(converter, patterns->
getContext());
3480 IdentityNConverter<tosa::IdentityOp>,
3493 ReduceConverter<tosa::ReduceAllOp>,
3494 ReduceConverter<tosa::ReduceAnyOp>,
3495 ReduceConverter<tosa::ReduceMinOp>,
3496 ReduceConverter<tosa::ReduceMaxOp>,
3497 ReduceConverter<tosa::ReduceSumOp>,
3498 ReduceConverter<tosa::ReduceProductOp>,
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 std::pair< Value, Value > normalizeMatMulTBatchDimensions(PatternRewriter &rewriter, Location loc, Value a, Value b, int64_t batchRank)
static LogicalResult emitElementwiseComputation(ConversionPatternRewriter &rewriter, Location loc, Operation *operation, ValueRange operands, ArrayRef< OpFoldResult > targetShape, const TypeConverter &converter)
static Value createLinalgBodyCalculationForReduceOp(Operation *op, ValueRange args, Type elementTy, PatternRewriter &rewriter)
static OpTy createWithDefaultProperties(OpBuilder &builder, Location loc, TypeRange resultTypes, ValueRange operands)
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 std::pair< OpFoldResult, Value > computeTargetSize(PatternRewriter &rewriter, Location loc, IndexPool &indexPool, ValueRange operands, int64_t dim)
DenseMap< int64_t, Value > IndexPool
static LogicalResult reduceMatchAndRewriteHelper(OpTy op, uint64_t axis, PatternRewriter &rewriter, bool allowNonFinites)
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 broadcastMatMulTSingletonDimension(PatternRewriter &rewriter, Location loc, Value operand, int64_t dim, OpFoldResult size)
static Value createLinalgBodyCalculationForElementwiseOp(Operation *op, ValueRange args, ArrayRef< Type > resultTypes, ConversionPatternRewriter &rewriter)
static Value broadcastMatMulTLeadingDimensions(PatternRewriter &rewriter, Location loc, Value operand, Value other)
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 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)
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)
APFloat getFloatMinMaxIdentity(const llvm::fltSemantics &semantics, bool negative, bool allowNonFinites)
MatMulTOperands prepareMatMulTOperands(PatternRewriter &rewriter, Location loc, Value a, Value b, RankedTensorType outputType)
Broadcast the batch dimensions of the ranked operands and create a zero-filled result tensor for eith...
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.
llvm::DenseMap< KeyT, ValueT, KeyInfoT, BucketT > DenseMap
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...