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);
1000 auto resultType = cast_or_null<RankedTensorType>(
1003 return rewriter.notifyMatchFailure(operation,
"failed to convert type");
1005 Value outputTensor = tensor::EmptyOp::create(rewriter, loc, targetShape,
1006 resultType.getElementType());
1011 auto rank = resultType.getRank();
1012 auto affineMaps = llvm::map_to_vector(operands, [&](
Value operand) {
1013 auto shape = cast<ShapedType>(operand.
getType()).getShape();
1015 for (
auto it : llvm::enumerate(
shape)) {
1019 bool requiresBroadcast =
1020 (it.value() == 1 && resultType.getDimSize(it.index()) != 1);
1021 auto affineExpr = requiresBroadcast
1022 ? rewriter.getAffineConstantExpr(0)
1023 : rewriter.getAffineDimExpr(it.index());
1024 affineExprs.push_back(affineExpr);
1026 return AffineMap::get(rank, 0, affineExprs, rewriter.getContext());
1030 for (
Value scalar : scalarOperands) {
1031 auto scalarRank = cast<ShapedType>(scalar.getType()).getRank();
1033 rewriter.getAffineConstantExpr(0));
1034 affineMaps.push_back(
AffineMap::get(rank, 0, zeros, rewriter.getContext()));
1036 affineMaps.push_back(rewriter.getMultiDimIdentityMap(rank));
1039 llvm::append_range(inputs, scalarOperands);
1042 bool encounteredError =
false;
1043 auto linalgOp = linalg::GenericOp::create(
1044 rewriter, loc, outputTensor.
getType(), inputs, outputTensor, affineMaps,
1049 {resultType.getElementType()}, rewriter);
1051 encounteredError =
true;
1054 linalg::YieldOp::create(opBuilder, loc, opResult);
1056 if (encounteredError)
1057 return rewriter.notifyMatchFailure(
1058 operation,
"unable to create linalg.generic body for elementwise op");
1061 auto castResult = rewriter.createOrFold<tensor::CastOp>(
1062 loc, resultType, linalgOp->getResult(0));
1063 rewriter.replaceOp(operation, castResult);
1071 if (isa<tosa::MulOp>(operation))
1072 return operands.take_front(2);
1073 if (isa<tosa::NegateOp>(operation))
1074 return operands.take_front(1);
1082 if (isa<tosa::MulOp>(operation)) {
1086 return operands.drop_front(2);
1088 if (
auto negate = dyn_cast<tosa::NegateOp>(operation)) {
1089 if (succeeded(negate.getInput1ZeroPoint()) &&
1090 succeeded(negate.getOutputZeroPoint()))
1092 return operands.drop_front(1);
1099 ConversionPatternRewriter &rewriter,
1103 assert(operation->
getNumResults() == 1 &&
"elementwise op expects 1 result");
1105 "elementwise op expects at least 1 operand");
1107 return rewriter.notifyMatchFailure(operation,
1108 "Unranked tensors not supported");
1112 auto loc = operation->
getLoc();
1115 auto [targetShape, masterOperands] =
1117 auto broadcastOperands =
1119 targetShape, masterOperands);
1121 scalarOperands, targetShape, converter);
1128 bool allowNonFinites) {
1129 if (isa<tosa::ReduceSumOp>(op) && isa<FloatType>(elementTy))
1132 if (isa<tosa::ReduceSumOp>(op) && isa<IntegerType>(elementTy))
1135 if (isa<tosa::ReduceProductOp>(op) && isa<FloatType>(elementTy))
1138 if (isa<tosa::ReduceProductOp>(op) && isa<IntegerType>(elementTy))
1141 if (isa<tosa::ReduceMinOp>(op) && isa<FloatType>(elementTy))
1145 false, allowNonFinites));
1147 if (isa<tosa::ReduceMinOp>(op) && isa<IntegerType>(elementTy))
1151 if (isa<tosa::ReduceMaxOp>(op) && isa<FloatType>(elementTy))
1155 true, allowNonFinites));
1157 if (isa<tosa::ReduceMaxOp>(op) && isa<IntegerType>(elementTy))
1161 if (isa<tosa::ReduceAllOp>(op) && elementTy.
isInteger(1))
1164 if (isa<tosa::ReduceAnyOp>(op) && elementTy.
isInteger(1))
1167 if (isa<tosa::ArgMaxOp>(op) && isa<FloatType>(elementTy))
1171 true, allowNonFinites));
1173 if (isa<tosa::ArgMaxOp>(op) && isa<IntegerType>(elementTy))
1187 if (isa<tosa::ReduceSumOp>(op) && isa<FloatType>(elementTy)) {
1189 rewriter, loc,
TypeRange{elementTy}, args);
1192 if (isa<tosa::ReduceSumOp>(op) && isa<IntegerType>(elementTy)) {
1194 rewriter, loc,
TypeRange{elementTy}, args);
1197 if (isa<tosa::ReduceProductOp>(op) && isa<FloatType>(elementTy)) {
1199 rewriter, loc,
TypeRange{elementTy}, args);
1202 if (isa<tosa::ReduceProductOp>(op) && isa<IntegerType>(elementTy)) {
1204 rewriter, loc,
TypeRange{elementTy}, args);
1207 if (isa<tosa::ReduceMinOp>(op) && isa<FloatType>(elementTy)) {
1208 return arith::MinimumFOp::create(rewriter, loc, args[0], args[1]);
1211 if (isa<tosa::ReduceMinOp>(op) && isa<IntegerType>(elementTy)) {
1212 return arith::MinSIOp::create(rewriter, loc, args[0], args[1]);
1215 if (isa<tosa::ReduceMaxOp>(op) && isa<FloatType>(elementTy)) {
1216 return arith::MaximumFOp::create(rewriter, loc, args[0], args[1]);
1219 if (isa<tosa::ReduceMaxOp>(op) && isa<IntegerType>(elementTy)) {
1220 return arith::MaxSIOp::create(rewriter, loc, args[0], args[1]);
1223 if (isa<tosa::ReduceAllOp>(op) && elementTy.
isInteger(1))
1224 return arith::AndIOp::create(rewriter, loc, args);
1226 if (isa<tosa::ReduceAnyOp>(op) && elementTy.
isInteger(1))
1227 return arith::OrIOp::create(rewriter, loc, args);
1235template <
typename OpTy>
1238 bool allowNonFinites) {
1239 auto loc = op->getLoc();
1240 auto inputTy = dyn_cast<RankedTensorType>(op->getOperand(0).getType());
1241 auto resultTy = dyn_cast<RankedTensorType>(op->getResult(0).getType());
1242 if (!inputTy || !resultTy)
1245 auto elementTy = resultTy.getElementType();
1246 Value input = op->getOperand(0);
1249 bool widenAccTy = std::is_same_v<OpTy, tosa::ReduceSumOp> &&
1250 isa<FloatType>(elementTy) &&
1251 cast<FloatType>(elementTy).isBF16();
1256 for (
unsigned i = 0; i < inputTy.getRank(); i++) {
1258 reduceShape.push_back(inputTy.getDimSize(i));
1259 if (inputTy.isDynamicDim(i))
1260 dynDims.push_back(tensor::DimOp::create(rewriter, loc, input, i));
1265 inputs.push_back(input);
1269 tensor::EmptyOp::create(rewriter, loc, reduceShape, accTy, dynDims)
1272 auto fillValueAttr =
1276 op,
"No initial value found for reduction operation");
1278 auto fillValue = arith::ConstantOp::create(rewriter, loc, fillValueAttr);
1280 linalg::FillOp::create(rewriter, loc,
ValueRange{fillValue},
1283 outputs.push_back(filledTensor);
1285 bool isNanIgnoreMode =
false;
1286 if constexpr (std::is_same_v<OpTy, tosa::ReduceMinOp> ||
1287 std::is_same_v<OpTy, tosa::ReduceMaxOp>) {
1289 if (isa<FloatType>(elementTy) &&
1290 op.getNanMode() == NanPropagationMode::IGNORE) {
1291 isNanIgnoreMode =
true;
1297 auto trueValue = arith::ConstantOp::create(rewriter, loc, trueAttr);
1298 auto emptyBoolTensor =
1299 tensor::EmptyOp::create(rewriter, loc, reduceShape,
1300 trueValue.getType(), dynDims)
1302 auto allResultsNaNTensor =
1303 linalg::FillOp::create(rewriter, loc,
ValueRange{trueValue},
1315 inputs.push_back(input);
1316 outputs.push_back(allResultsNaNTensor);
1320 bool didEncounterError =
false;
1321 linalg::LinalgOp linalgOp = linalg::ReduceOp::create(
1322 rewriter, loc, inputs, outputs, axis,
1324 std::array<Value, 2> binaryArgs{
1325 blockArgs[0], isNanIgnoreMode ? blockArgs[2] : blockArgs[1]};
1328 if (binaryArgs[0].
getType() != accTy)
1329 binaryArgs[0] = arith::ExtFOp::create(
1330 nestedBuilder, nestedLoc,
TypeRange{accTy},
1331 ValueRange{binaryArgs[0]}, arith::ExtFOp::Properties{});
1336 didEncounterError =
true;
1339 if (isNanIgnoreMode) {
1340 auto inputValue = blockArgs[0];
1341 auto initialValue = blockArgs[2];
1342 auto oldAllResultsNanFlagValue = blockArgs[3];
1345 Value isNaN = arith::CmpFOp::create(nestedBuilder, op->getLoc(),
1346 arith::CmpFPredicate::UNO,
1347 inputValue, inputValue);
1349 auto selectOp = arith::SelectOp::create(nestedBuilder, op->getLoc(),
1350 isNaN, initialValue,
result);
1353 auto newAllResultsNanFlagValue = arith::AndIOp::create(
1354 nestedBuilder, op->getLoc(), oldAllResultsNanFlagValue, isNaN);
1355 resultsToYield.push_back(selectOp);
1356 resultsToYield.push_back(newAllResultsNanFlagValue);
1358 resultsToYield.push_back(
result);
1360 linalg::YieldOp::create(nestedBuilder, loc, resultsToYield);
1363 if (!didEncounterError)
1365 op,
"unable to create linalg.generic body for reduce op");
1367 if (isNanIgnoreMode) {
1377 auto nanValue = arith::ConstantOp::create(rewriter, loc, nanValueAttr);
1378 auto emptyNanTensor =
1379 tensor::EmptyOp::create(rewriter, loc, reduceShape, accTy, dynDims)
1381 auto nanFilledTensor =
1382 linalg::FillOp::create(rewriter, loc,
ValueRange{nanValue},
1388 auto finalEmptyTensor =
1389 tensor::EmptyOp::create(rewriter, loc, reduceShape, accTy, dynDims)
1395 ins.push_back(linalgOp->getOpResult(1));
1396 ins.push_back(nanFilledTensor);
1397 ins.push_back(linalgOp->getResult(0));
1398 outs.push_back(finalEmptyTensor);
1400 linalg::ElementwiseOp::create(rewriter, op->getLoc(), ins, outs,
1401 mlir::linalg::ElementwiseKind::select);
1402 linalgOp = linalgSelect;
1406 Value reducedRes = linalgOp->getResult(0);
1409 tensor::EmptyOp::create(rewriter, loc, reduceShape, elementTy, dynDims)
1412 const unsigned reducedRank =
1413 cast<ShapedType>(reducedRes.
getType()).getRank();
1416 linalg::GenericOp::create(
1422 Value truncf = arith::TruncFOp::create(nestedBuilder, nestedLoc,
1423 elementTy, args[0]);
1424 linalg::YieldOp::create(nestedBuilder, nestedLoc, truncf);
1430 uint64_t expandInputRank = cast<ShapedType>(reducedRes.
getType()).getRank();
1431 reassociationMap.resize(expandInputRank);
1433 for (uint64_t i = 0; i < expandInputRank; i++) {
1434 int32_t dimToPush = i > axis ? i + 1 : i;
1438 if (expandInputRank != 0) {
1439 int32_t expandedDim = axis < expandInputRank ? axis : expandInputRank - 1;
1440 reassociationMap[expandedDim].push_back(
1457 auto type = cast<RankedTensorType>(operand.
getType());
1459 cast<RankedTensorType>(other.
getType()).getRank() - type.getRank();
1460 if (missingDims <= 0)
1465 for (
int64_t dim = 0; dim < missingDims; ++dim) {
1467 dimensions.push_back(dim);
1470 Value empty = tensor::EmptyOp::create(
1471 rewriter, loc,
shape, type.getElementType(), type.getEncoding());
1472 return linalg::BroadcastOp::create(rewriter, loc, operand, empty, dimensions)
1485 auto type = cast<RankedTensorType>(operand.
getType());
1487 sliceShape.erase(sliceShape.begin() + dim);
1488 auto sliceType = RankedTensorType::get(sliceShape, type.getElementType(),
1489 type.getEncoding());
1491 rewriter, loc, operand, sliceType);
1496 Value empty = tensor::EmptyOp::create(
1497 rewriter, loc,
shape, type.getElementType(), type.getEncoding());
1498 return linalg::BroadcastOp::create(rewriter, loc,
slice, empty,
1505static std::pair<Value, Value>
1509 int64_t bRank = cast<RankedTensorType>(
b.getType()).getRank();
1510 int64_t firstSharedDim = std::max(aRank, bRank) - std::min(aRank, bRank);
1516 for (
int64_t dim = firstSharedDim; dim < batchRank; ++dim) {
1517 auto aType = cast<RankedTensorType>(a.
getType());
1518 auto bType = cast<RankedTensorType>(
b.getType());
1519 int64_t aSize = aType.getDimSize(dim);
1520 int64_t bSize = bType.getDimSize(dim);
1521 if (aSize == bSize && ShapedType::isStatic(aSize))
1526 auto [size, masterOperand] =
1547 RankedTensorType outputType) {
1548 const int64_t outputRank = outputType.getRank();
1549 const int64_t batchRank = outputRank - 2;
1550 auto normalizedOperands =
1552 a = normalizedOperands.first;
1553 b = normalizedOperands.second;
1556 outputShape.reserve(outputRank);
1557 for (
int64_t batchDim = 0; batchDim < batchRank; ++batchDim)
1562 for (
auto [dim, size] : llvm::enumerate(outputShape))
1563 if (outputType.isDynamicDim(dim))
1564 dynamicDims.push_back(
1567 Value zero = arith::ConstantOp::create(
1568 rewriter, loc, rewriter.
getZeroAttr(outputType.getElementType()));
1569 Value emptyTensor = tensor::EmptyOp::create(
1570 rewriter, loc, outputType.getShape(), outputType.getElementType(),
1572 Value zeroTensor = linalg::FillOp::create(rewriter, loc,
ValueRange{zero},
1576 return {a,
b, zeroTensor};
1583class QuantizedMatMulTConverter :
public OpConversionPattern<tosa::MatMulTOp> {
1585 using OpConversionPattern<tosa::MatMulTOp>::OpConversionPattern;
1587 matchAndRewrite(tosa::MatMulTOp op, OpAdaptor adaptor,
1588 ConversionPatternRewriter &rewriter)
const final {
1590 auto aType = dyn_cast<RankedTensorType>(adaptor.getA().getType());
1591 auto bType = dyn_cast<RankedTensorType>(adaptor.getB().getType());
1592 auto outputType = dyn_cast<RankedTensorType>(op.getType());
1593 if (!aType || !bType || !outputType)
1594 return rewriter.notifyMatchFailure(op,
"only supports ranked tensors");
1596 FailureOr<int64_t> maybeAZp = op.getAZeroPoint();
1597 FailureOr<int64_t> maybeBZp = op.getBZeroPoint();
1598 if (failed(maybeAZp))
1599 return rewriter.notifyMatchFailure(
1600 op,
"input A zero point cannot be statically determined");
1601 if (failed(maybeBZp))
1602 return rewriter.notifyMatchFailure(
1603 op,
"input B zero point cannot be statically determined");
1605 const int64_t aZpVal = *maybeAZp;
1606 const int64_t bZpVal = *maybeBZp;
1608 if (aZpVal == 0 && bZpVal == 0)
1609 return rewriter.notifyMatchFailure(
1610 op,
"requires at least one nonzero zero point");
1612 const int64_t outputRank = outputType.getRank();
1613 const int64_t batchRank = outputRank - 2;
1615 rewriter, loc, adaptor.getA(), adaptor.getB(), outputType);
1616 Type outputElementType = outputType.getElementType();
1618 int64_t loopRank = outputRank + 1;
1620 llvm::map_to_vector(llvm::seq<int64_t>(0, loopRank), [&](
int64_t dim) {
1621 return rewriter.getAffineDimExpr(dim);
1624 dimensions.begin() + batchRank);
1625 aMap.push_back(dimensions[batchRank]);
1626 aMap.push_back(dimensions[batchRank + 2]);
1628 dimensions.begin() + batchRank);
1629 bMap.push_back(dimensions[batchRank + 1]);
1630 bMap.push_back(dimensions[batchRank + 2]);
1632 dimensions.begin() + batchRank);
1633 outputMap.push_back(dimensions[batchRank]);
1634 outputMap.push_back(dimensions[batchRank + 1]);
1639 SmallVector<utils::IteratorType> iteratorTypes(
1640 loopRank, utils::IteratorType::parallel);
1641 iteratorTypes.back() = utils::IteratorType::reduction;
1643 auto generic = linalg::GenericOp::create(
1644 rewriter, loc, outputType,
ValueRange{a,
b}, zeroTensor, indexingMaps,
1646 [=](OpBuilder &builder, Location nestedLoc,
ValueRange args) {
1647 auto extendToOutputType = [&](Value value) {
1648 if (value.getType() == outputElementType)
1650 return arith::ExtSIOp::create(builder, nestedLoc, outputElementType,
1655 Value aValue = extendToOutputType(args[0]);
1656 Value bValue = extendToOutputType(args[1]);
1657 Value aZp = arith::ConstantOp::create(
1659 builder.getIntegerAttr(outputElementType, aZpVal));
1660 Value bZp = arith::ConstantOp::create(
1662 builder.getIntegerAttr(outputElementType, bZpVal));
1664 arith::SubIOp::create(builder, nestedLoc, aValue, aZp);
1666 arith::SubIOp::create(builder, nestedLoc, bValue, bZp);
1668 arith::MulIOp::create(builder, nestedLoc, adjustedA, adjustedB);
1670 arith::AddIOp::create(builder, nestedLoc, args[2],
product);
1671 linalg::YieldOp::create(builder, nestedLoc, sum);
1673 rewriter.replaceOp(op, generic.getResult(0));
1678template <
typename SrcOp>
1679class PointwiseConverter :
public OpConversionPattern<SrcOp> {
1681 using OpConversionPattern<SrcOp>::OpConversionPattern;
1682 using typename OpConversionPattern<SrcOp>::OpAdaptor;
1685 matchAndRewrite(SrcOp op, OpAdaptor operands,
1686 ConversionPatternRewriter &rewriter)
const final {
1688 op, operands.getOperands(), rewriter, *this->getTypeConverter());
1698 auto inputType = cast<RankedTensorType>(input.
getType());
1699 auto elemType = inputType.getElementType();
1700 auto collapsedType = RankedTensorType::get({}, elemType);
1702 return tensor::CollapseShapeOp::create(rewriter, loc, collapsedType, input,
1709 output.reserve(input.size());
1711 for (
auto v : llvm::map_range(
1712 input, [](int32_t val) {
return static_cast<int8_t
>(val); })) {
1713 output.push_back(v);
1725static void setupLinalgGenericOpInputAndIndexingMap(
1728 bool isConstant, tosa::RescaleOp op,
Value &constant,
int64_t &arg,
1729 bool isShift =
false) {
1731 auto loc = op.getLoc();
1732 auto inputTy = cast<ShapedType>(op.getInput().getType());
1733 unsigned rank = inputTy.getRank();
1739 if (values.size() == 1) {
1740 IntegerAttr intAttr = isShift
1743 constant = arith::ConstantOp::create(rewriter, loc, intAttr);
1747 auto tensorType = RankedTensorType::get(
1748 {
static_cast<int64_t>(values.size())}, elementType);
1754 genericInputs.push_back(
1755 arith::ConstantOp::create(rewriter, loc, EltAttr));
1763 auto operand = isShift ? op.getShift() : op.getMultiplier();
1764 auto tensorType = dyn_cast<RankedTensorType>(operand.getType());
1765 if (tensorType && tensorType.hasStaticShape() &&
1766 tensorType.getShape()[0] == 1) {
1771 genericInputs.push_back(collapse1xNTensorToN(rewriter, operand, loc));
1772 indexingMaps.push_back(broadcastMap);
1774 genericInputs.push_back(operand);
1780 arg = indexingMaps.size() - 1;
1785 FailureOr<int64_t> maybeZp,
Location loc,
1787 bool isOutputZp =
false) {
1790 const uint32_t attrBitwidth =
1791 isOutputZp ? 32 : (bitwidth > 32 ? bitwidth : 32);
1798 result = blockArgs[zpArg];
1799 auto zpTy =
result.getType();
1800 if (zpTy.getIntOrFloatBitWidth() < attrBitwidth) {
1803 if (zpTy.isUnsignedInteger()) {
1805 UnrealizedConversionCastOp::create(
1810 if (zpTy.isUnsignedInteger()) {
1811 return arith::ExtUIOp::create(builder, loc, extendType,
result);
1813 return arith::ExtSIOp::create(builder, loc, extendType,
result);
1817 return arith::ConstantOp::create(builder, loc,
1818 IntegerAttr::get(extendType, *maybeZp));
1825 using OpRewritePattern<tosa::RescaleOp>::OpRewritePattern;
1827 LogicalResult matchAndRewrite(tosa::RescaleOp op,
1828 PatternRewriter &rewriter)
const final {
1829 auto loc = op.getLoc();
1830 auto input = op.getInput();
1831 auto inputTy = cast<ShapedType>(op.getInput().getType());
1832 auto outputTy = cast<ShapedType>(op.getOutput().getType());
1833 unsigned rank = inputTy.getRank();
1836 if (op.getRoundingMode() == RoundingMode::INEXACT_ROUND)
1838 op,
"tosa.rescale with rounding mode = 'INEXACT_ROUND' is not "
1839 "currently supported");
1840 if (op.getRoundingMode() == RoundingMode::DOUBLE_ROUND && !op.getScale32())
1842 op,
"tosa.rescale requires scale32 for double_round to be true");
1844 if (!isa<IntegerType>(inputTy.getElementType()))
1848 auto outputElemTy = dyn_cast<IntegerType>(outputTy.getElementType());
1849 if (!outputElemTy || outputElemTy.getWidth() > 32)
1851 op,
"only support integer output types up to 32 bits");
1853 SmallVector<Value> dynDims;
1854 for (
int i = 0; i < outputTy.getRank(); i++) {
1855 if (outputTy.isDynamicDim(i)) {
1856 dynDims.push_back(tensor::DimOp::create(rewriter, loc, input, i));
1860 DenseElementsAttr shiftElems;
1861 bool isShiftConstant =
false;
1863 isShiftConstant =
true;
1865 DenseElementsAttr multiplierElems;
1866 bool isMultiplierConstant =
false;
1868 isMultiplierConstant =
true;
1870 llvm::SmallVector<int32_t> shiftValues;
1871 llvm::SmallVector<int32_t> multiplierValues;
1874 if (isMultiplierConstant && isShiftConstant) {
1876 shiftValues = llvm::map_to_vector(
1877 shiftElems.
getValues<IntegerAttr>(), [](IntegerAttr attr) -> int32_t {
1878 return static_cast<int32_t>(attr.getInt());
1881 llvm::map_to_vector(multiplierElems.
getValues<IntegerAttr>(),
1882 [](IntegerAttr attr) -> int32_t {
1883 return static_cast<int32_t>(attr.getInt());
1887 for (
int i = 0, s = multiplierValues.size(); i < s; i++) {
1888 if (shiftValues[i] > 63) {
1890 multiplierValues[i] = 0;
1895 doubleRound = op.getRoundingMode() == RoundingMode::DOUBLE_ROUND &&
1896 llvm::any_of(shiftValues, [](int32_t v) {
return v > 31; });
1898 doubleRound = op.getRoundingMode() == RoundingMode::DOUBLE_ROUND;
1900 RoundingMode roundingMode =
1901 doubleRound ? RoundingMode::DOUBLE_ROUND : RoundingMode::SINGLE_ROUND;
1903 SmallVector<AffineMap> indexingMaps = {
1905 SmallVector<Value, 4> genericInputs = {input};
1909 Value multiplierConstant;
1910 int64_t multiplierArg = 0;
1911 setupLinalgGenericOpInputAndIndexingMap(
1912 rewriter, multiplierValues, genericInputs, indexingMaps,
1913 isMultiplierConstant, op, multiplierConstant, multiplierArg);
1917 Value shiftConstant;
1918 int64_t shiftArg = 0;
1919 setupLinalgGenericOpInputAndIndexingMap(
1920 rewriter, shiftValues, genericInputs, indexingMaps, isShiftConstant, op,
1921 shiftConstant, shiftArg,
true);
1926 FailureOr<int64_t> maybeIZp = op.getInputZeroPoint();
1927 FailureOr<int64_t> maybeOZp = op.getOutputZeroPoint();
1937 genericInputs.push_back(
1938 collapse1xNTensorToN(rewriter, op->getOperand(3), loc));
1939 indexingMaps.push_back(broadcastMap);
1940 iZpArg = indexingMaps.size() - 1;
1944 genericInputs.push_back(
1945 collapse1xNTensorToN(rewriter, op->getOperand(4), loc));
1946 indexingMaps.push_back(broadcastMap);
1947 oZpArg = indexingMaps.size() - 1;
1954 Value emptyTensor = tensor::EmptyOp::create(
1955 rewriter, loc, outputTy.getShape(), outputTy.getElementType(),
1956 ArrayRef<Value>({dynDims}));
1958 auto linalgOp = linalg::GenericOp::create(
1959 rewriter, loc, outputTy, genericInputs,
ValueRange{emptyTensor},
1961 [&](OpBuilder &nestedBuilder, Location nestedLoc,
1963 Value value = blockArgs[0];
1964 Type valueTy = value.
getType();
1966 FailureOr<int64_t> maybeIZp = op.getInputZeroPoint();
1967 auto inputZp = getExtendZp(nestedBuilder, valueTy, maybeIZp,
1968 nestedLoc, blockArgs, iZpArg);
1970 FailureOr<int64_t> maybeOZp = op.getOutputZeroPoint();
1971 auto outputZp = getExtendZp(nestedBuilder, valueTy, maybeOZp,
1972 nestedLoc, blockArgs, oZpArg,
true);
1974 IntegerType outIntType =
1975 cast<IntegerType>(blockArgs.back().
getType());
1976 unsigned outBitWidth = outIntType.getWidth();
1977 assert(outBitWidth <= 32 &&
"Unexpected output zeropoint bitwidth");
1979 Value multiplier = multiplierConstant ? multiplierConstant
1980 : blockArgs[multiplierArg];
1981 Value shift = shiftConstant ? shiftConstant : blockArgs[shiftArg];
1984 value = UnrealizedConversionCastOp::create(
1985 nestedBuilder, nestedLoc,
1986 nestedBuilder.getIntegerType(
1992 if (op.getInputUnsigned()) {
1993 value = arith::ExtUIOp::create(nestedBuilder, nestedLoc,
1994 nestedBuilder.getI32Type(), value);
1996 value = arith::ExtSIOp::create(nestedBuilder, nestedLoc,
1997 nestedBuilder.getI32Type(), value);
2002 arith::SubIOp::create(nestedBuilder, nestedLoc, value, inputZp);
2004 value = tosa::ApplyScaleOp::create(nestedBuilder, loc,
2005 nestedBuilder.getI32Type(), value,
2006 multiplier, shift, roundingMode);
2010 arith::AddIOp::create(nestedBuilder, nestedLoc, value, outputZp);
2013 int32_t intMin = APInt::getSignedMinValue(outBitWidth).getSExtValue();
2014 int32_t intMax = APInt::getSignedMaxValue(outBitWidth).getSExtValue();
2017 if (op.getOutputUnsigned()) {
2019 intMax = APInt::getMaxValue(outBitWidth).getZExtValue();
2022 auto intMinVal = arith::ConstantOp::create(
2023 nestedBuilder, loc, nestedBuilder.getI32IntegerAttr(intMin));
2024 auto intMaxVal = arith::ConstantOp::create(
2025 nestedBuilder, loc, nestedBuilder.getI32IntegerAttr(intMax));
2028 nestedBuilder,
false);
2030 if (outIntType.getWidth() < 32) {
2031 value = arith::TruncIOp::create(
2032 nestedBuilder, nestedLoc,
2036 if (outIntType.isUnsignedInteger()) {
2037 value = UnrealizedConversionCastOp::create(nestedBuilder, nestedLoc,
2041 linalg::YieldOp::create(nestedBuilder, loc, value);
2044 rewriter.
replaceOp(op, linalgOp->getResults());
2054 using OpRewritePattern<tosa::ResizeOp>::OpRewritePattern;
2056 LogicalResult matchAndRewrite(tosa::ResizeOp op,
2057 PatternRewriter &rewriter)
const final {
2058 Location loc = op.getLoc();
2059 ImplicitLocOpBuilder builder(loc, rewriter);
2060 auto input = op.getInput();
2061 auto inputTy = cast<RankedTensorType>(input.getType());
2062 auto resultTy = cast<RankedTensorType>(op.getType());
2063 const bool isBilinear = op.getMode() == ResizeMode::BILINEAR;
2065 auto inputH = inputTy.getDimSize(1);
2066 auto inputW = inputTy.getDimSize(2);
2067 auto outputH = resultTy.getDimSize(1);
2068 auto outputW = resultTy.getDimSize(2);
2070 if (inputH != 1 || inputW != 1 || outputH != 1 || outputW != 1)
2072 op,
"tosa.resize is not a pure 1x1->1x1 image operation");
2074 if (op.getMode() != ResizeMode::NEAREST_NEIGHBOR &&
2075 op.getMode() != ResizeMode::BILINEAR)
2077 op,
"tosa.resize mode should be NEAREST_NEIGHBOR or BILINEAR");
2079 if (inputTy == resultTy) {
2084 SmallVector<int64_t> scale;
2090 SmallVector<ReassociationExprs, 4> reassociationMap(2);
2097 RankedTensorType::get({inputTy.getDimSize(0), inputTy.getDimSize(3)},
2098 inputTy.getElementType());
2099 Value collapse = tensor::CollapseShapeOp::create(builder, collapseTy, input,
2103 llvm::SmallVector<Value> outputDynSize;
2104 if (inputTy.isDynamicDim(0))
2105 outputDynSize.push_back(tensor::DimOp::create(builder, input, 0));
2106 if (inputTy.isDynamicDim(3))
2107 outputDynSize.push_back(tensor::DimOp::create(builder, input, 3));
2110 auto genericTy = collapseTy.clone(resultTy.getElementType());
2112 tensor::EmptyOp::create(builder, genericTy.getShape(),
2113 resultTy.getElementType(), outputDynSize);
2115 SmallVector<utils::IteratorType> iterators(genericTy.getRank(),
2116 utils::IteratorType::parallel);
2118 auto generic = linalg::GenericOp::create(
2120 ArrayRef<AffineMap>{genericMap, genericMap}, iterators,
2121 [=](OpBuilder &
b, Location loc,
ValueRange args) {
2122 Value value = args[0];
2124 if (inputTy.getElementType() != resultTy.getElementType()) {
2125 value = arith::ExtSIOp::create(
b, loc, resultTy.getElementType(),
2128 if (isBilinear && scale[0] != 0) {
2129 Value scaleY = arith::ConstantOp::create(
2130 b, loc,
b.getI32IntegerAttr(scale[0]));
2131 value = arith::MulIOp::create(
b, loc, value, scaleY);
2134 if (isBilinear && scale[2] != 0) {
2135 Value scaleX = arith::ConstantOp::create(
2136 b, loc,
b.getI32IntegerAttr(scale[2]));
2137 value = arith::MulIOp::create(
b, loc, value, scaleX);
2141 linalg::YieldOp::create(
b, loc, value);
2145 op, resultTy,
generic.getResults()[0], reassociationMap);
2157 LogicalResult matchAndRewrite(tosa::ResizeOp op,
2161 auto input = op.getInput();
2162 auto inputTy = dyn_cast<RankedTensorType>(input.getType());
2163 auto resultTy = dyn_cast<RankedTensorType>(op.getType());
2165 if (!inputTy || !resultTy)
2167 "requires ranked input/output types");
2169 auto batch = inputTy.getDimSize(0);
2170 auto channels = inputTy.getDimSize(3);
2171 auto inputH = inputTy.getDimSize(1);
2172 auto inputW = inputTy.getDimSize(2);
2173 auto outputH = resultTy.getDimSize(1);
2174 auto outputW = resultTy.getDimSize(2);
2176 if ((inputH != 1 || outputH == 1) && (inputW != 1 || outputW == 1))
2178 op,
"tosa.resize has no broadcasting behavior");
2183 resizeShape.push_back(batch);
2184 resizeShape.push_back(inputH == 1 ? 1 : outputH);
2185 resizeShape.push_back(inputW == 1 ? 1 : outputW);
2186 resizeShape.push_back(channels);
2188 auto resizeTy = resultTy.clone(resizeShape);
2190 tosa::ResizeOp::create(builder, resizeTy, input, op.getScale(),
2191 op.getOffset(), op.getBorder(), op.getMode());
2198 reassociationMap.push_back({});
2201 reassociationMap.push_back({});
2206 collapseShape.push_back(outputH);
2208 collapseShape.push_back(outputW);
2209 collapseShape.push_back(channels);
2211 auto collapseTy = resultTy.clone(collapseShape);
2212 Value collapse = tensor::CollapseShapeOp::create(builder, collapseTy,
2213 resize, reassociationMap);
2217 if (inputTy.isDynamicDim(0))
2218 outputDynSize.push_back(tensor::DimOp::create(builder, input, 0));
2219 if (inputTy.isDynamicDim(3))
2220 outputDynSize.push_back(tensor::DimOp::create(builder, input, 3));
2223 utils::IteratorType::parallel);
2224 Value empty = tensor::EmptyOp::create(
2225 builder, resultTy.getShape(), resultTy.getElementType(), outputDynSize);
2242 Value value = args[0];
2243 linalg::YieldOp::create(
b, loc, value);
2252 using OpRewritePattern<tosa::ResizeOp>::OpRewritePattern;
2254 LogicalResult matchAndRewrite(tosa::ResizeOp op,
2255 PatternRewriter &rewriter)
const final {
2256 Location loc = op.getLoc();
2257 ImplicitLocOpBuilder
b(loc, rewriter);
2258 auto input = op.getInput();
2259 auto inputTy = cast<ShapedType>(input.getType());
2260 auto resultTy = cast<ShapedType>(op.getType());
2261 auto resultETy = resultTy.getElementType();
2263 bool floatingPointMode = isa<FloatType>(resultETy);
2264 auto floatTy = resultETy;
2266 auto imageH = inputTy.getShape()[1];
2267 auto imageW = inputTy.getShape()[2];
2269 auto dynamicDimsOr =
2271 if (!dynamicDimsOr.has_value())
2273 op,
"unable to get dynamic dimensions of tosa.resize");
2275 if (op.getMode() != ResizeMode::NEAREST_NEIGHBOR &&
2276 op.getMode() != ResizeMode::BILINEAR)
2278 op,
"tosa.resize mode should be NEAREST_NEIGHBOR or BILINEAR");
2280 SmallVector<AffineMap, 2> affineMaps = {
2282 auto emptyTensor = tensor::EmptyOp::create(
b, resultTy.getShape(),
2283 resultETy, *dynamicDimsOr);
2284 auto genericOp = linalg::GenericOp::create(
2287 Value resize = genericOp.getResult(0);
2290 OpBuilder::InsertionGuard regionGuard(
b);
2291 b.createBlock(&genericOp.getRegion(), genericOp.getRegion().end(),
2293 Value batch = linalg::IndexOp::create(
b, 0);
2294 Value y = linalg::IndexOp::create(
b, 1);
2295 Value x = linalg::IndexOp::create(
b, 2);
2296 Value channel = linalg::IndexOp::create(
b, 3);
2299 arith::ConstantOp::create(
b,
b.getZeroAttr(
b.getI32Type()));
2300 Value zeroFp = arith::ConstantOp::create(
b,
b.getZeroAttr(floatTy));
2302 arith::ConstantOp::create(
b,
b.getI32IntegerAttr(imageH - 1));
2304 arith::ConstantOp::create(
b,
b.getI32IntegerAttr(imageW - 1));
2306 Value inY = arith::IndexCastOp::create(
b,
b.getI32Type(), y);
2307 Value inX = arith::IndexCastOp::create(
b,
b.getI32Type(), x);
2309 SmallVector<int64_t> scale, offset, border;
2314 op,
"tosa.resize scale/offset/border should have compile time "
2315 "constant values.");
2318 Value yScaleN, yScaleD, xScaleN, xScaleD;
2319 yScaleN = arith::ConstantOp::create(
b,
b.getI32IntegerAttr(scale[0]));
2320 yScaleD = arith::ConstantOp::create(
b,
b.getI32IntegerAttr(scale[1]));
2321 xScaleN = arith::ConstantOp::create(
b,
b.getI32IntegerAttr(scale[2]));
2322 xScaleD = arith::ConstantOp::create(
b,
b.getI32IntegerAttr(scale[3]));
2324 Value yOffset, xOffset, yBorder, xBorder;
2325 yOffset = arith::ConstantOp::create(
b,
b.getI32IntegerAttr(offset[0]));
2326 xOffset = arith::ConstantOp::create(
b,
b.getI32IntegerAttr(offset[1]));
2327 yBorder = arith::ConstantOp::create(
b,
b.getI32IntegerAttr(border[0]));
2328 xBorder = arith::ConstantOp::create(
b,
b.getI32IntegerAttr(border[1]));
2331 auto getIndexAndDeltaFp = [&](Value &index, Value &delta, Value in,
2332 Value scaleN, Value scaleD, Value offset,
2333 int size, ImplicitLocOpBuilder &
b) {
2341 Value val = arith::MulIOp::create(
b, in, scaleD);
2342 val = arith::AddIOp::create(
b, val, offset);
2343 index = arith::FloorDivSIOp::create(
b, val, scaleN);
2346 Value scaledIndex = arith::MulIOp::create(
b, index, scaleN);
2347 Value r = arith::SubIOp::create(
b, val, scaledIndex);
2348 Value rFp = arith::SIToFPOp::create(
b, floatTy, r);
2351 Value scaleNfp = arith::UIToFPOp::create(
b, floatTy, scaleN);
2352 delta = arith::DivFOp::create(
b, rFp, scaleNfp);
2356 auto getIndexAndDeltaInt = [&](Value &index, Value &delta, Value in,
2357 Value scaleN, Value scaleD, Value offset,
2358 int size, ImplicitLocOpBuilder &
b) {
2367 Value val = arith::MulIOp::create(
b, in, scaleD);
2368 val = arith::AddIOp::create(
b, val, offset);
2369 index = arith::FloorDivSIOp::create(
b, val, scaleN);
2370 delta = arith::MulIOp::create(
b, index, scaleN);
2371 delta = arith::SubIOp::create(
b, val, delta);
2374 Value ix, iy, dx, dy;
2375 if (floatingPointMode) {
2376 getIndexAndDeltaFp(iy, dy, inY, yScaleN, yScaleD, yOffset, imageH,
b);
2377 getIndexAndDeltaFp(ix, dx, inX, xScaleN, xScaleD, xOffset, imageW,
b);
2379 getIndexAndDeltaInt(iy, dy, inY, yScaleN, yScaleD, yOffset, imageH,
b);
2380 getIndexAndDeltaInt(ix, dx, inX, xScaleN, xScaleD, xOffset, imageW,
b);
2383 if (op.getMode() == ResizeMode::NEAREST_NEIGHBOR) {
2384 auto one = arith::ConstantOp::create(
b,
b.getI32IntegerAttr(1));
2386 auto getNearestIndexAndClamp = [&](Value val, Value dval, Value scale,
2387 Value
max,
int size,
2388 ImplicitLocOpBuilder &
b) -> Value {
2394 if (floatingPointMode) {
2396 arith::ConstantOp::create(
b,
b.getFloatAttr(floatTy, 0.5f));
2397 pred = arith::CmpFOp::create(
b, arith::CmpFPredicate::OGE, dval, h);
2399 Value dvalDouble = arith::ShLIOp::create(
b, dval, one);
2400 pred = arith::CmpIOp::create(
b, arith::CmpIPredicate::sge,
2404 auto offset = arith::SelectOp::create(
b, pred, one, zeroI32);
2405 val = arith::AddIOp::create(
b, val, offset);
2407 return arith::IndexCastOp::create(
b,
b.getIndexType(), val);
2410 iy = getNearestIndexAndClamp(iy, dy, yScaleN, hMax, imageH,
b);
2411 ix = getNearestIndexAndClamp(ix, dx, xScaleN, wMax, imageW,
b);
2413 Value
result = tensor::ExtractOp::create(
2416 linalg::YieldOp::create(
b,
result);
2419 assert(op.getMode() == ResizeMode::BILINEAR);
2421 auto oneVal = arith::ConstantOp::create(
b,
b.getI32IntegerAttr(1));
2423 auto getClampedIdxs = [&](Value &val0, Value &val1,
int size, Value in,
2424 Value
max, ImplicitLocOpBuilder &
b) {
2426 val1 = arith::AddIOp::create(
b, val0, oneVal);
2431 val0 = arith::IndexCastOp::create(
b,
b.getIndexType(), val0);
2432 val1 = arith::IndexCastOp::create(
b,
b.getIndexType(), val1);
2440 Value x0, x1, y0, y1;
2441 getClampedIdxs(y0, y1, imageH, iy, hMax,
b);
2442 getClampedIdxs(x0, x1, imageW, ix, wMax,
b);
2444 Value y0x0 = tensor::ExtractOp::create(
2446 Value y0x1 = tensor::ExtractOp::create(
2448 Value y1x0 = tensor::ExtractOp::create(
2450 Value y1x1 = tensor::ExtractOp::create(
2453 if (floatingPointMode) {
2455 arith::ConstantOp::create(
b,
b.getFloatAttr(floatTy, 1.0f));
2456 auto interpolate = [&](Value val0, Value val1, Value delta,
2458 ImplicitLocOpBuilder &
b) -> Value {
2461 Value oneMinusDelta = arith::SubFOp::create(
b, oneVal, delta);
2462 Value mul0 = arith::MulFOp::create(
b, val0, oneMinusDelta);
2463 Value mul1 = arith::MulFOp::create(
b, val1, delta);
2464 return arith::AddFOp::create(
b, mul0, mul1);
2470 Value topAcc = interpolate(y0x0, y0x1, dx, imageW,
b);
2475 Value bottomAcc = interpolate(y1x0, y1x1, dx, imageW,
b);
2479 Value
result = interpolate(topAcc, bottomAcc, dy, imageH,
b);
2480 linalg::YieldOp::create(
b,
result);
2483 y0x0 = arith::ExtSIOp::create(
b, resultETy, y0x0);
2484 y0x1 = arith::ExtSIOp::create(
b, resultETy, y0x1);
2485 y1x0 = arith::ExtSIOp::create(
b, resultETy, y1x0);
2486 y1x1 = arith::ExtSIOp::create(
b, resultETy, y1x1);
2489 if (resultETy.getIntOrFloatBitWidth() > deltaBitwidth) {
2490 dx = arith::ExtSIOp::create(
b, resultETy, dx);
2491 dy = arith::ExtSIOp::create(
b, resultETy, dy);
2494 Value yScaleNExt = yScaleN;
2495 Value xScaleNExt = xScaleN;
2497 const int64_t scaleBitwidth =
2499 if (resultETy.getIntOrFloatBitWidth() > scaleBitwidth) {
2500 yScaleNExt = arith::ExtSIOp::create(
b, resultETy, yScaleN);
2501 xScaleNExt = arith::ExtSIOp::create(
b, resultETy, xScaleN);
2504 auto interpolate = [](Value val0, Value val1, Value weight1,
2505 Value scale,
int inputSize,
2506 ImplicitLocOpBuilder &
b) -> Value {
2508 return arith::MulIOp::create(
b, val0, scale);
2509 Value weight0 = arith::SubIOp::create(
b, scale, weight1);
2510 Value mul0 = arith::MulIOp::create(
b, val0, weight0);
2511 Value mul1 = arith::MulIOp::create(
b, val1, weight1);
2512 return arith::AddIOp::create(
b, mul0, mul1);
2515 Value topAcc = interpolate(y0x0, y0x1, dx, xScaleNExt, imageW,
b);
2516 Value bottomAcc = interpolate(y1x0, y1x1, dx, xScaleNExt, imageW,
b);
2518 interpolate(topAcc, bottomAcc, dy, yScaleNExt, imageH,
b);
2519 linalg::YieldOp::create(
b,
result);
2532template <
typename SrcOp>
2535 using OpRewritePattern<SrcOp>::OpRewritePattern;
2537 LogicalResult matchAndRewrite(SrcOp op,
2538 PatternRewriter &rewriter)
const final {
2539 rewriter.
replaceOp(op, op.getOperation()->getOperands());
2544template <
typename SrcOp>
2547 ReduceConverter(MLIRContext *context,
bool allowNonFinites)
2548 : OpRewritePattern<SrcOp>(context), allowNonFinites(allowNonFinites) {}
2550 LogicalResult matchAndRewrite(SrcOp reduceOp,
2551 PatternRewriter &rewriter)
const final {
2557 bool allowNonFinites;
2562 using OpRewritePattern<tosa::ReverseOp>::OpRewritePattern;
2564 LogicalResult matchAndRewrite(tosa::ReverseOp op,
2565 PatternRewriter &rewriter)
const final {
2566 auto loc = op.getLoc();
2567 Value input = op.getInput1();
2568 auto inputTy = cast<ShapedType>(input.
getType());
2569 auto resultTy = cast<ShapedType>(op.getType());
2570 auto axis = op.getAxis();
2572 SmallVector<Value> dynDims;
2573 for (
int i = 0; i < inputTy.getRank(); i++) {
2574 if (inputTy.isDynamicDim(i)) {
2575 dynDims.push_back(tensor::DimOp::create(rewriter, loc, input, i));
2579 Value axisDimSize = tensor::DimOp::create(rewriter, loc, input, axis);
2582 auto emptyTensor = tensor::EmptyOp::create(
2583 rewriter, loc, inputTy.getShape(),
2584 inputTy.getElementType(), ArrayRef<Value>({dynDims}))
2586 SmallVector<AffineMap, 2> affineMaps = {
2590 op, resultTy, ArrayRef<Value>({}),
ValueRange{emptyTensor}, affineMaps,
2592 [&](OpBuilder &nestedBuilder, Location nestedLoc,
ValueRange args) {
2593 llvm::SmallVector<Value>
indices;
2594 for (
unsigned int i = 0; i < inputTy.getRank(); i++) {
2596 linalg::IndexOp::create(rewriter, nestedLoc, i).getResult();
2600 arith::SubIOp::create(rewriter, nestedLoc, axisDimSize, one);
2601 index = arith::SubIOp::create(rewriter, nestedLoc, sizeMinusOne,
2608 auto extract = tensor::ExtractOp::create(nestedBuilder, nestedLoc,
2610 linalg::YieldOp::create(nestedBuilder, op.getLoc(),
2611 extract.getResult());
2621struct TileConverter :
public OpConversionPattern<tosa::TileOp> {
2622 using OpConversionPattern<tosa::TileOp>::OpConversionPattern;
2625 matchAndRewrite(tosa::TileOp op, OpAdaptor adaptor,
2626 ConversionPatternRewriter &rewriter)
const override {
2627 auto loc = op.getLoc();
2628 auto input = op.getInput1();
2629 auto inputTy = cast<ShapedType>(input.
getType());
2630 auto inputShape = inputTy.getShape();
2631 auto resultTy = cast<ShapedType>(op.getType());
2632 auto elementTy = inputTy.getElementType();
2633 int64_t rank = inputTy.getRank();
2635 SmallVector<int64_t> multiples;
2636 if (
failed(op.getConstantMultiples(multiples)))
2640 SmallVector<int64_t, 2> genericShape;
2641 for (
int i = 0; i < rank; i++) {
2642 int64_t dim = multiples[i];
2643 genericShape.push_back(dim == -1 ? ShapedType::kDynamic : dim);
2644 genericShape.push_back(inputShape[i]);
2647 SmallVector<Value> dynDims;
2648 for (
int i = 0; i < inputTy.getRank(); i++) {
2649 if (inputTy.isDynamicDim(i) || multiples[i] == -1) {
2650 dynDims.push_back(tensor::DimOp::create(rewriter, loc, input, i));
2654 auto emptyTensor = tensor::EmptyOp::create(
2655 rewriter, op.getLoc(), genericShape, elementTy, dynDims);
2658 SmallVector<AffineExpr, 4> dimExprs;
2659 dimExprs.reserve(rank);
2660 for (
unsigned i = 0; i < rank; ++i)
2661 dimExprs.push_back(rewriter.getAffineDimExpr(i * 2 + 1));
2663 auto readAffineMap =
2665 rewriter.getContext());
2667 SmallVector<AffineMap, 2> affineMaps = {
2668 readAffineMap, rewriter.getMultiDimIdentityMap(genericShape.size())};
2670 auto genericOp = linalg::GenericOp::create(
2671 rewriter, loc, RankedTensorType::get(genericShape, elementTy), input,
2674 [&](OpBuilder &nestedBuilder, Location nestedLoc,
ValueRange args) {
2675 linalg::YieldOp::create(nestedBuilder, op.getLoc(), *args.begin());
2680 rewriter.replaceOpWithNewOp<tosa::ReshapeOp>(
2681 op, resultTy, genericOp.getResult(0), shapeValue);
2701 ArgMaxConverter(MLIRContext *context,
bool allowNonFinites)
2702 : OpRewritePattern<tosa::ArgMaxOp>(context),
2703 allowNonFinites(allowNonFinites) {}
2705 LogicalResult matchAndRewrite(tosa::ArgMaxOp argmaxOp,
2706 PatternRewriter &rewriter)
const final {
2707 auto loc = argmaxOp.getLoc();
2708 Value input = argmaxOp.getInput();
2709 auto inputTy = cast<ShapedType>(input.
getType());
2710 auto resultTy = cast<ShapedType>(argmaxOp.getOutput().getType());
2711 auto inElementTy = inputTy.getElementType();
2712 auto outElementTy = resultTy.getElementType();
2713 int axis = argmaxOp.getAxis();
2714 auto resultMaxTy = RankedTensorType::get(resultTy.getShape(), inElementTy);
2716 if (!isa<IntegerType>(outElementTy))
2717 return rewriter.notifyMatchFailure(
2719 "tosa.arg_max to linalg.* requires integer-like result type");
2721 SmallVector<Value> dynDims;
2722 for (
int i = 0; i < inputTy.getRank(); i++) {
2723 if (inputTy.isDynamicDim(i) && i != axis) {
2724 dynDims.push_back(tensor::DimOp::create(rewriter, loc, input, i));
2729 auto emptyTensorIdx =
2730 tensor::EmptyOp::create(rewriter, loc, resultTy.getShape(),
2731 outElementTy, dynDims)
2733 auto fillValueIdx = arith::ConstantOp::create(
2734 rewriter, loc, rewriter.getIntegerAttr(outElementTy, 0));
2735 auto filledTensorIdx =
2736 linalg::FillOp::create(rewriter, loc,
ValueRange{fillValueIdx},
2741 auto emptyTensorMax =
2742 tensor::EmptyOp::create(rewriter, loc, resultTy.getShape(), inElementTy,
2746 argmaxOp, inElementTy, rewriter, allowNonFinites);
2748 if (!fillValueMaxAttr)
2749 return rewriter.notifyMatchFailure(
2750 argmaxOp,
"unsupported tosa.argmax element type");
2753 arith::ConstantOp::create(rewriter, loc, fillValueMaxAttr);
2754 auto filledTensorMax =
2755 linalg::FillOp::create(rewriter, loc,
ValueRange{fillValueMax},
2761 SmallVector<utils::IteratorType, 4> iteratorTypes;
2762 iteratorTypes.resize(inputTy.getRank(), utils::IteratorType::parallel);
2763 iteratorTypes[axis] = utils::IteratorType::reduction;
2765 SmallVector<AffineExpr, 2> srcExprs;
2766 SmallVector<AffineExpr, 2> dstExprs;
2767 for (
int i = 0, rank = inputTy.getRank(); i != rank; ++i) {
2773 bool didEncounterError =
false;
2775 rewriter.getContext());
2776 auto linalgOp = linalg::GenericOp::create(
2777 rewriter, loc, ArrayRef<Type>({resultTy, resultMaxTy}), input,
2778 ValueRange({filledTensorIdx, filledTensorMax}), maps, iteratorTypes,
2779 [&](OpBuilder &nestedBuilder, Location nestedLoc,
2781 auto newValue = blockArgs[0];
2782 auto oldIndex = blockArgs[1];
2783 auto oldValue = blockArgs[2];
2785 Value newIndex = arith::IndexCastOp::create(
2786 rewriter, nestedLoc, oldIndex.getType(),
2787 linalg::IndexOp::create(rewriter, loc, axis));
2790 if (isa<FloatType>(inElementTy)) {
2791 if (argmaxOp.getNanMode() == NanPropagationMode::IGNORE) {
2794 predicate = arith::CmpFOp::create(rewriter, nestedLoc,
2795 arith::CmpFPredicate::OGT,
2796 newValue, oldValue);
2801 Value gt = arith::CmpFOp::create(rewriter, nestedLoc,
2802 arith::CmpFPredicate::UGT,
2803 newValue, oldValue);
2804 Value oldNonNaN = arith::CmpFOp::create(rewriter, nestedLoc,
2805 arith::CmpFPredicate::ORD,
2806 oldValue, oldValue);
2807 predicate = arith::AndIOp::create(
2808 rewriter, nestedLoc, rewriter.getI1Type(), gt, oldNonNaN);
2810 }
else if (isa<IntegerType>(inElementTy)) {
2811 predicate = arith::CmpIOp::create(rewriter, nestedLoc,
2812 arith::CmpIPredicate::sgt,
2813 newValue, oldValue);
2815 didEncounterError =
true;
2819 auto resultMax = arith::SelectOp::create(
2820 rewriter, nestedLoc, predicate, newValue, oldValue);
2821 auto resultIndex = arith::SelectOp::create(
2822 rewriter, nestedLoc, predicate, newIndex, oldIndex);
2823 linalg::YieldOp::create(nestedBuilder, nestedLoc,
2827 if (didEncounterError)
2828 return rewriter.notifyMatchFailure(
2829 argmaxOp,
"unsupported tosa.argmax element type");
2831 rewriter.replaceOp(argmaxOp, linalgOp.getResult(0));
2836 bool allowNonFinites;
2839class GatherConverter :
public OpConversionPattern<tosa::GatherOp> {
2841 using OpConversionPattern<tosa::GatherOp>::OpConversionPattern;
2843 matchAndRewrite(tosa::GatherOp op, OpAdaptor adaptor,
2844 ConversionPatternRewriter &rewriter)
const final {
2845 auto input = adaptor.getOperands()[0];
2846 auto indices = adaptor.getOperands()[1];
2848 auto valuesTy = dyn_cast<RankedTensorType>(op.getValues().getType());
2849 auto resultTy = dyn_cast<RankedTensorType>(op.getType());
2850 if (!valuesTy || !resultTy)
2851 return rewriter.notifyMatchFailure(op,
"unranked tensors not supported");
2853 auto dynamicDims = inferDynamicDimsForGather(
2854 rewriter, op.getLoc(), adaptor.getValues(), adaptor.getIndices());
2856 auto resultElementTy = resultTy.getElementType();
2858 auto loc = op.getLoc();
2860 tensor::EmptyOp::create(rewriter, loc, resultTy.getShape(),
2861 resultElementTy, dynamicDims)
2864 SmallVector<AffineMap, 2> affineMaps = {
2866 resultTy.getRank(), 0,
2867 {rewriter.getAffineDimExpr(0), rewriter.getAffineDimExpr(1)},
2868 rewriter.getContext()),
2869 rewriter.getMultiDimIdentityMap(resultTy.getRank())};
2871 auto genericOp = linalg::GenericOp::create(
2875 [&](OpBuilder &
b, Location loc,
ValueRange args) {
2876 auto indexValue = args[0];
2877 auto index0 = linalg::IndexOp::create(rewriter, loc, 0);
2878 Value index1 = arith::IndexCastOp::create(
2879 rewriter, loc, rewriter.getIndexType(), indexValue);
2880 auto index2 = linalg::IndexOp::create(rewriter, loc, 2);
2881 Value extract = tensor::ExtractOp::create(
2882 rewriter, loc, input,
ValueRange{index0, index1, index2});
2883 linalg::YieldOp::create(rewriter, loc, extract);
2885 rewriter.replaceOp(op, genericOp.getResult(0));
2889 static llvm::SmallVector<Value> inferDynamicDimsForGather(OpBuilder &builder,
2893 llvm::SmallVector<Value> results;
2895 auto addDynamicDimension = [&](Value source, int64_t dim) {
2897 if (
auto dimValue = llvm::dyn_cast_if_present<Value>(sz))
2898 results.push_back(dimValue);
2901 addDynamicDimension(values, 0);
2902 addDynamicDimension(
indices, 1);
2903 addDynamicDimension(values, 2);
2908class RowGatherConverter :
public OpConversionPattern<tosa::RowGatherOp> {
2910 using OpConversionPattern<tosa::RowGatherOp>::OpConversionPattern;
2912 matchAndRewrite(tosa::RowGatherOp op, OpAdaptor adaptor,
2913 ConversionPatternRewriter &rewriter)
const final {
2914 auto valuesTy = dyn_cast<RankedTensorType>(adaptor.getValues().getType());
2915 auto indicesTy = dyn_cast<RankedTensorType>(adaptor.getIndices().getType());
2917 dyn_cast<RankedTensorType>(adaptor.getRowCount().getType());
2918 auto resultTy = dyn_cast<RankedTensorType>(op.getType());
2919 if (!valuesTy || !indicesTy || !rowCountTy || !resultTy)
2920 return rewriter.notifyMatchFailure(op,
"unranked tensors not supported");
2922 Location loc = op.getLoc();
2924 Value rowCount = tensor::ExtractOp::create(
2925 rewriter, loc, adaptor.getRowCount(),
ValueRange{zero});
2926 Value rowCountIndex = arith::IndexCastOp::create(
2927 rewriter, loc, rewriter.getIndexType(), rowCount);
2929 SmallVector<Value> dynamicDims;
2930 if (resultTy.isDynamicDim(0))
2931 dynamicDims.push_back(
2932 tensor::DimOp::create(rewriter, loc, adaptor.getValues(), 0));
2933 if (resultTy.isDynamicDim(1)) {
2934 Value indicesWidth =
2935 tensor::DimOp::create(rewriter, loc, adaptor.getIndices(), 1);
2936 dynamicDims.push_back(
2937 arith::MulIOp::create(rewriter, loc, indicesWidth, rowCountIndex));
2939 if (resultTy.isDynamicDim(2))
2940 dynamicDims.push_back(
2941 tensor::DimOp::create(rewriter, loc, adaptor.getValues(), 2));
2944 tensor::EmptyOp::create(rewriter, loc, resultTy.getShape(),
2945 resultTy.getElementType(), dynamicDims);
2946 SmallVector<AffineMap> affineMaps = {
2947 rewriter.getMultiDimIdentityMap(resultTy.getRank())};
2949 auto genericOp = linalg::GenericOp::create(
2950 rewriter, loc, ArrayRef<Type>{resultTy},
ValueRange{},
2953 [&](OpBuilder &builder, Location nestedLoc,
ValueRange) {
2954 Value batch = linalg::IndexOp::create(builder, nestedLoc, 0);
2955 Value outputRow = linalg::IndexOp::create(builder, nestedLoc, 1);
2956 Value channel = linalg::IndexOp::create(builder, nestedLoc, 2);
2957 Value indexSlot = arith::DivUIOp::create(builder, nestedLoc,
2958 outputRow, rowCountIndex);
2959 Value rowOffset = arith::RemUIOp::create(builder, nestedLoc,
2960 outputRow, rowCountIndex);
2961 Value index = tensor::ExtractOp::create(builder, nestedLoc,
2962 adaptor.getIndices(),
2964 Value row = arith::IndexCastOp::create(builder, nestedLoc,
2965 builder.getIndexType(), index);
2966 row = arith::AddIOp::create(builder, nestedLoc, row, rowOffset);
2968 tensor::ExtractOp::create(builder, nestedLoc, adaptor.getValues(),
2970 linalg::YieldOp::create(builder, nestedLoc,
result);
2972 rewriter.replaceOp(op, genericOp.getResult(0));
2982 using OpRewritePattern<tosa::TableOp>::OpRewritePattern;
2984 LogicalResult matchAndRewrite(tosa::TableOp op,
2985 PatternRewriter &rewriter)
const final {
2986 auto loc = op.getLoc();
2987 Value input = op.getInput1();
2988 Value table = op.getTable();
2989 auto inputTy = cast<ShapedType>(input.
getType());
2990 auto tableTy = cast<ShapedType>(table.
getType());
2991 auto resultTy = cast<ShapedType>(op.getType());
2993 auto inputElementTy = inputTy.getElementType();
2994 auto tableElementTy = tableTy.getElementType();
2995 auto resultElementTy = resultTy.getElementType();
2997 SmallVector<Value> dynDims;
2998 for (
int i = 0; i < resultTy.getRank(); ++i) {
2999 if (inputTy.isDynamicDim(i)) {
3001 tensor::DimOp::create(rewriter, loc, op.getOperand(0), i));
3006 tensor::EmptyOp::create(rewriter, loc, resultTy.getShape(),
3007 resultElementTy, dynDims)
3010 SmallVector<AffineMap, 2> affineMaps = {
3011 rewriter.getMultiDimIdentityMap(resultTy.getRank()),
3012 rewriter.getMultiDimIdentityMap(resultTy.getRank())};
3014 auto genericOp = linalg::GenericOp::create(
3017 rewriter.replaceOp(op, genericOp.getResult(0));
3020 OpBuilder::InsertionGuard regionGuard(rewriter);
3021 Block *block = rewriter.createBlock(
3022 &genericOp.getRegion(), genericOp.getRegion().end(),
3023 TypeRange({inputElementTy, resultElementTy}), {loc, loc});
3026 rewriter.setInsertionPointToStart(block);
3027 if (inputElementTy.isInteger(8) && tableElementTy.isInteger(8) &&
3028 resultElementTy.isInteger(8)) {
3029 Value index = arith::IndexCastOp::create(
3030 rewriter, loc, rewriter.getIndexType(), inputValue);
3032 index = arith::AddIOp::create(rewriter, loc, rewriter.getIndexType(),
3035 tensor::ExtractOp::create(rewriter, loc, table,
ValueRange{index});
3036 linalg::YieldOp::create(rewriter, loc, extract);
3040 if (inputElementTy.isInteger(16) && tableElementTy.isInteger(16) &&
3041 resultElementTy.isInteger(32)) {
3042 Value extend = arith::ExtSIOp::create(
3043 rewriter, loc, rewriter.getI32Type(), inputValue);
3045 auto offset = arith::ConstantOp::create(
3046 rewriter, loc, rewriter.getI32IntegerAttr(32768));
3047 auto seven = arith::ConstantOp::create(rewriter, loc,
3048 rewriter.getI32IntegerAttr(7));
3049 auto one = arith::ConstantOp::create(rewriter, loc,
3050 rewriter.getI32IntegerAttr(1));
3051 auto b1111111 = arith::ConstantOp::create(
3052 rewriter, loc, rewriter.getI32IntegerAttr(127));
3058 auto extendAdd = arith::AddIOp::create(rewriter, loc, extend, offset);
3059 Value index = arith::ShRUIOp::create(rewriter, loc, extendAdd, seven);
3061 arith::AndIOp::create(rewriter, loc, extendAdd, b1111111);
3066 Value indexPlusOne = arith::AddIOp::create(rewriter, loc, index, one);
3068 index = arith::IndexCastOp::create(rewriter, loc,
3069 rewriter.getIndexType(), index);
3070 indexPlusOne = arith::IndexCastOp::create(
3071 rewriter, loc, rewriter.getIndexType(), indexPlusOne);
3074 tensor::ExtractOp::create(rewriter, loc, table,
ValueRange{index});
3075 Value
next = tensor::ExtractOp::create(rewriter, loc, table,
3079 arith::ExtSIOp::create(rewriter, loc, rewriter.getI32Type(), base);
3081 arith::ExtSIOp::create(rewriter, loc, rewriter.getI32Type(), next);
3085 Value baseScaled = arith::ShLIOp::create(rewriter, loc, base, seven);
3086 Value diff = arith::SubIOp::create(rewriter, loc, next, base);
3087 Value diffScaled = arith::MulIOp::create(rewriter, loc, diff, fraction);
3089 arith::AddIOp::create(rewriter, loc, baseScaled, diffScaled);
3091 linalg::YieldOp::create(rewriter, loc,
result);
3097 return rewriter.notifyMatchFailure(
3098 op,
"unable to create body for tosa.table op");
3103 using OpRewritePattern<RFFT2dOp>::OpRewritePattern;
3105 static bool isRankedTensor(Type type) {
return isa<RankedTensorType>(type); }
3107 static OpFoldResult halfPlusOne(OpBuilder &builder, Location loc,
3113 auto divBy2 = builder.
createOrFold<arith::DivUIOp>(loc, value, two);
3114 auto plusOne = builder.
createOrFold<arith::AddIOp>(loc, divBy2, one);
3118 static RankedTensorType
3119 computeOutputShape(OpBuilder &builder, Location loc, Value input,
3120 llvm::SmallVectorImpl<Value> &dynamicSizes) {
3126 dims[2] = halfPlusOne(builder, loc, dims[2]);
3128 llvm::SmallVector<int64_t, 3> staticSizes;
3131 auto elementType = cast<RankedTensorType>(input.
getType()).getElementType();
3132 return RankedTensorType::get(staticSizes, elementType);
3135 static Value createZeroTensor(PatternRewriter &rewriter, Location loc,
3136 RankedTensorType type,
3137 llvm::ArrayRef<Value> dynamicSizes) {
3139 tensor::EmptyOp::create(rewriter, loc, type, dynamicSizes);
3140 auto fillValueAttr = rewriter.
getZeroAttr(type.getElementType());
3141 auto fillValue = arith::ConstantOp::create(rewriter, loc, fillValueAttr);
3143 linalg::FillOp::create(rewriter, loc,
ValueRange{fillValue},
3146 return filledTensor;
3149 static Value castIndexToFloat(OpBuilder &builder, Location loc,
3150 FloatType type, Value value) {
3151 auto integerVal = arith::IndexCastUIOp::create(
3153 type.getIntOrFloatBitWidth() > 32 ? builder.
getI64Type()
3157 return arith::UIToFPOp::create(builder, loc, type, integerVal);
3160 static Value createLinalgIndex(OpBuilder &builder, Location loc,
3161 FloatType type, int64_t index) {
3162 auto indexVal = linalg::IndexOp::create(builder, loc, index);
3163 return castIndexToFloat(builder, loc, type, indexVal);
3166 template <
typename... Args>
3167 static llvm::SmallVector<AffineExpr, 4> affineDimsExpr(OpBuilder &builder,
3172 LogicalResult matchAndRewrite(RFFT2dOp rfft2d,
3173 PatternRewriter &rewriter)
const override {
3174 if (!llvm::all_of(rfft2d->getOperandTypes(), isRankedTensor) ||
3175 !llvm::all_of(rfft2d->getResultTypes(), isRankedTensor)) {
3177 "only supports ranked tensors");
3180 auto loc = rfft2d.getLoc();
3181 auto input = rfft2d.getInputReal();
3183 dyn_cast<FloatType>(cast<ShapedType>(input.
getType()).getElementType());
3186 "only supports float element types");
3189 llvm::SmallVector<Value> dynamicSizes;
3190 auto outputType = computeOutputShape(rewriter, loc, input, dynamicSizes);
3193 llvm::SmallVector<utils::IteratorType, 5> iteratorTypes = {
3194 utils::IteratorType::parallel, utils::IteratorType::parallel,
3195 utils::IteratorType::parallel, utils::IteratorType::reduction,
3196 utils::IteratorType::reduction};
3199 llvm::SmallVector<Value> genericOpInputs = {input};
3200 llvm::SmallVector<Value> genericOpOutputs = {
3201 createZeroTensor(rewriter, loc, outputType, dynamicSizes),
3202 createZeroTensor(rewriter, loc, outputType, dynamicSizes)};
3206 llvm::ArrayRef{affineDimsExpr(rewriter, 0, 3, 4),
3207 affineDimsExpr(rewriter, 0, 1, 2),
3208 affineDimsExpr(rewriter, 0, 1, 2)},
3212 auto dimH = rewriter.
createOrFold<tensor::DimOp>(loc, input, 1);
3213 auto dimW = rewriter.
createOrFold<tensor::DimOp>(loc, input, 2);
3216 auto zeroFloat = arith::ConstantOp::create(
3217 rewriter, loc, rewriter.
getZeroAttr(elementType));
3218 auto twoPiAttr = rewriter.
getFloatAttr(elementType, 6.283185307179586);
3219 auto twoPi = arith::ConstantOp::create(rewriter, loc, twoPiAttr);
3224 auto constH = castIndexToFloat(rewriter, loc, elementType, dimH);
3225 auto constW = castIndexToFloat(rewriter, loc, elementType, dimW);
3226 auto halfH = index::DivUOp::create(rewriter, loc, dimH, twoIndex);
3227 auto halfW = index::DivUOp::create(rewriter, loc, dimW, twoIndex);
3229 auto buildBody = [&](OpBuilder &builder, Location loc,
ValueRange args) {
3230 Value valReal = args[0];
3231 Value sumReal = args[1];
3232 Value sumImag = args[2];
3235 Value oy = linalg::IndexOp::create(builder, loc, 1);
3236 Value ox = linalg::IndexOp::create(builder, loc, 2);
3237 Value iy = linalg::IndexOp::create(builder, loc, 3);
3238 Value ix = linalg::IndexOp::create(builder, loc, 4);
3243 auto iyXoy = index::MulOp::create(builder, loc, iy, oy);
3244 auto ixXox = index::MulOp::create(builder, loc, ix, ox);
3246 auto iyRem = index::RemUOp::create(builder, loc, iyXoy, dimH);
3247 auto ixRem = index::RemUOp::create(builder, loc, ixXox, dimW);
3249 auto iyRemFloat = castIndexToFloat(builder, loc, elementType, iyRem);
3250 auto ixRemFloat = castIndexToFloat(builder, loc, elementType, ixRem);
3252 auto yComponent = arith::DivFOp::create(builder, loc, iyRemFloat, constH);
3253 auto xComponent = arith::DivFOp::create(builder, loc, ixRemFloat, constW);
3254 auto sumXY = arith::AddFOp::create(builder, loc, yComponent, xComponent);
3255 auto angle = arith::MulFOp::create(builder, loc, twoPi, sumXY);
3262 auto iyIs0 = arith::CmpIOp::create(builder, loc, arith::CmpIPredicate::eq,
3264 auto iyIsHalfH = arith::CmpIOp::create(
3265 builder, loc, arith::CmpIPredicate::eq, iyRem, halfH);
3266 auto ixIs0 = arith::CmpIOp::create(builder, loc, arith::CmpIPredicate::eq,
3268 auto ixIsHalfW = arith::CmpIOp::create(
3269 builder, loc, arith::CmpIPredicate::eq, ixRem, halfW);
3271 auto iyIsSinSkippable =
3272 arith::OrIOp::create(builder, loc, iyIs0, iyIsHalfH);
3273 auto ixIsSinSkippable =
3274 arith::OrIOp::create(builder, loc, ixIs0, ixIsHalfW);
3275 auto shouldSkipSin = arith::AndIOp::create(builder, loc, iyIsSinSkippable,
3280 auto cosAngle = math::CosOp::create(builder, loc, angle);
3281 auto sinAngle = math::SinOp::create(builder, loc, angle);
3282 auto imagWeight = arith::SelectOp::create(builder, loc, shouldSkipSin,
3283 zeroFloat, sinAngle);
3284 auto realComponent =
3285 arith::MulFOp::create(builder, loc, valReal, cosAngle);
3286 auto imagComponent =
3287 arith::MulFOp::create(builder, loc, valReal, imagWeight);
3292 arith::AddFOp::create(builder, loc, sumReal, realComponent);
3294 arith::SubFOp::create(builder, loc, sumImag, imagComponent);
3296 linalg::YieldOp::create(builder, loc,
ValueRange{outReal, outImag});
3300 rfft2d, rfft2d.getResultTypes(), genericOpInputs, genericOpOutputs,
3301 indexingMaps, iteratorTypes, buildBody);
3310 LogicalResult matchAndRewrite(FFT2dOp fft2d,
3311 PatternRewriter &rewriter)
const override {
3312 if (!llvm::all_of(fft2d->getOperandTypes(),
3313 RFFT2dConverter::isRankedTensor) ||
3314 !llvm::all_of(fft2d->getResultTypes(),
3315 RFFT2dConverter::isRankedTensor)) {
3319 Location loc = fft2d.getLoc();
3320 Value input_real = fft2d.getInputReal();
3321 Value input_imag = fft2d.getInputImag();
3322 BoolAttr inverse = fft2d.getInverseAttr();
3324 auto real_el_ty = cast<FloatType>(
3325 cast<ShapedType>(input_real.
getType()).getElementType());
3326 [[maybe_unused]]
auto imag_el_ty = cast<FloatType>(
3327 cast<ShapedType>(input_imag.
getType()).getElementType());
3329 assert(real_el_ty == imag_el_ty);
3332 SmallVector<Value> dynamicSizes;
3337 SmallVector<int64_t, 3> staticSizes;
3340 auto outputType = RankedTensorType::get(staticSizes, real_el_ty);
3343 SmallVector<utils::IteratorType, 5> iteratorTypes = {
3344 utils::IteratorType::parallel, utils::IteratorType::parallel,
3345 utils::IteratorType::parallel, utils::IteratorType::reduction,
3346 utils::IteratorType::reduction};
3349 SmallVector<Value> genericOpInputs = {input_real, input_imag};
3350 SmallVector<Value> genericOpOutputs = {
3351 RFFT2dConverter::createZeroTensor(rewriter, loc, outputType,
3353 RFFT2dConverter::createZeroTensor(rewriter, loc, outputType,
3358 ArrayRef{RFFT2dConverter::affineDimsExpr(rewriter, 0, 3, 4),
3359 RFFT2dConverter::affineDimsExpr(rewriter, 0, 3, 4),
3360 RFFT2dConverter::affineDimsExpr(rewriter, 0, 1, 2),
3361 RFFT2dConverter::affineDimsExpr(rewriter, 0, 1, 2)},
3365 auto dimH = rewriter.
createOrFold<tensor::DimOp>(loc, input_real, 1);
3366 auto dimW = rewriter.
createOrFold<tensor::DimOp>(loc, input_real, 2);
3369 auto twoPiAttr = rewriter.
getFloatAttr(real_el_ty, 6.283185307179586);
3370 auto twoPi = arith::ConstantOp::create(rewriter, loc, twoPiAttr);
3372 RFFT2dConverter::castIndexToFloat(rewriter, loc, real_el_ty, dimH);
3374 RFFT2dConverter::castIndexToFloat(rewriter, loc, real_el_ty, dimW);
3376 auto buildBody = [&](OpBuilder &builder, Location loc,
ValueRange args) {
3377 Value valReal = args[0];
3378 Value valImag = args[1];
3379 Value sumReal = args[2];
3380 Value sumImag = args[3];
3383 Value oy = linalg::IndexOp::create(builder, loc, 1);
3384 Value ox = linalg::IndexOp::create(builder, loc, 2);
3385 Value iy = linalg::IndexOp::create(builder, loc, 3);
3386 Value ix = linalg::IndexOp::create(builder, loc, 4);
3390 auto iyXoy = index::MulOp::create(builder, loc, iy, oy);
3391 auto ixXox = index::MulOp::create(builder, loc, ix, ox);
3393 auto iyRem = index::RemUOp::create(builder, loc, iyXoy, dimH);
3394 auto ixRem = index::RemUOp::create(builder, loc, ixXox, dimW);
3397 RFFT2dConverter::castIndexToFloat(builder, loc, real_el_ty, iyRem);
3399 RFFT2dConverter::castIndexToFloat(builder, loc, real_el_ty, ixRem);
3401 auto yComponent = arith::DivFOp::create(builder, loc, iyRemFloat, constH);
3402 auto xComponent = arith::DivFOp::create(builder, loc, ixRemFloat, constW);
3404 auto sumXY = arith::AddFOp::create(builder, loc, yComponent, xComponent);
3405 auto angle = arith::MulFOp::create(builder, loc, twoPi, sumXY);
3408 angle = arith::MulFOp::create(
3409 builder, loc, angle,
3410 arith::ConstantOp::create(rewriter, loc,
3416 auto cosAngle = math::CosOp::create(builder, loc, angle);
3417 auto sinAngle = math::SinOp::create(builder, loc, angle);
3419 auto rcos = arith::MulFOp::create(builder, loc, valReal, cosAngle);
3420 auto rsin = arith::MulFOp::create(builder, loc, valImag, sinAngle);
3421 auto realComponent = arith::AddFOp::create(builder, loc, rcos, rsin);
3423 auto icos = arith::MulFOp::create(builder, loc, valImag, cosAngle);
3424 auto isin = arith::MulFOp::create(builder, loc, valReal, sinAngle);
3426 auto imagComponent = arith::SubFOp::create(builder, loc, icos, isin);
3431 arith::AddFOp::create(builder, loc, sumReal, realComponent);
3433 arith::AddFOp::create(builder, loc, sumImag, imagComponent);
3435 linalg::YieldOp::create(builder, loc,
ValueRange{outReal, outImag});
3439 fft2d, fft2d.getResultTypes(), genericOpInputs, genericOpOutputs,
3440 indexingMaps, iteratorTypes, buildBody);
3453 patterns->
add<GenericResizeConverter>(patterns->
getContext(),
3457 patterns->
add<MaterializeResizeBroadcast>(patterns->
getContext(),
3462 PointwiseConverter<tosa::AddOp>,
3463 PointwiseConverter<tosa::SubOp>,
3464 PointwiseConverter<tosa::MulOp>,
3465 PointwiseConverter<tosa::IntDivOp>,
3466 PointwiseConverter<tosa::NegateOp>,
3467 PointwiseConverter<tosa::PowOp>,
3468 PointwiseConverter<tosa::ReciprocalOp>,
3469 PointwiseConverter<tosa::RsqrtOp>,
3470 PointwiseConverter<tosa::LogOp>,
3471 PointwiseConverter<tosa::ExpOp>,
3472 PointwiseConverter<tosa::AbsOp>,
3473 PointwiseConverter<tosa::SinOp>,
3474 PointwiseConverter<tosa::CosOp>,
3475 PointwiseConverter<tosa::TanhOp>,
3476 PointwiseConverter<tosa::ErfOp>,
3477 PointwiseConverter<tosa::BitwiseAndOp>,
3478 PointwiseConverter<tosa::BitwiseOrOp>,
3479 PointwiseConverter<tosa::BitwiseNotOp>,
3480 PointwiseConverter<tosa::BitwiseXorOp>,
3481 PointwiseConverter<tosa::LogicalAndOp>,
3482 PointwiseConverter<tosa::LogicalNotOp>,
3483 PointwiseConverter<tosa::LogicalOrOp>,
3484 PointwiseConverter<tosa::LogicalXorOp>,
3485 PointwiseConverter<tosa::CastOp>,
3486 PointwiseConverter<tosa::LogicalLeftShiftOp>,
3487 PointwiseConverter<tosa::LogicalRightShiftOp>,
3488 PointwiseConverter<tosa::ArithmeticRightShiftOp>,
3489 PointwiseConverter<tosa::ClzOp>,
3490 PointwiseConverter<tosa::SelectOp>,
3491 PointwiseConverter<tosa::GreaterOp>,
3492 PointwiseConverter<tosa::GreaterEqualOp>,
3493 PointwiseConverter<tosa::EqualOp>,
3494 PointwiseConverter<tosa::MaximumOp>,
3495 PointwiseConverter<tosa::MinimumOp>,
3496 PointwiseConverter<tosa::CeilOp>,
3497 PointwiseConverter<tosa::FloorOp>,
3498 PointwiseConverter<tosa::ClampOp>,
3499 PointwiseConverter<tosa::SigmoidOp>
3502 patterns->
add<QuantizedMatMulTConverter>(converter, patterns->
getContext());
3505 IdentityNConverter<tosa::IdentityOp>,
3518 ReduceConverter<tosa::ReduceAllOp>,
3519 ReduceConverter<tosa::ReduceAnyOp>,
3520 ReduceConverter<tosa::ReduceMinOp>,
3521 ReduceConverter<tosa::ReduceMaxOp>,
3522 ReduceConverter<tosa::ReduceSumOp>,
3523 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 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 ValueRange getScalarOperands(Operation *operation, ValueRange operands)
static Value broadcastDynamicDimensions(PatternRewriter &rewriter, Location loc, IndexPool &indexPool, Value operand, ArrayRef< OpFoldResult > targetShape, ArrayRef< Value > masterOperands)
static Value broadcastDynamicDimension(PatternRewriter &rewriter, Location loc, IndexPool &indexPool, Value operand, int64_t dim, OpFoldResult targetSize, Value masterOperand)
static LogicalResult elementwiseMatchAndRewriteHelper(Operation *operation, ValueRange operands, ConversionPatternRewriter &rewriter, const TypeConverter &converter)
static Value 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 LogicalResult emitElementwiseComputation(ConversionPatternRewriter &rewriter, Location loc, Operation *operation, ValueRange operands, ValueRange scalarOperands, ArrayRef< OpFoldResult > targetShape, const TypeConverter &converter)
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 SmallVector< Value > slice(OpBuilder &b, Location loc, Value value, VectorType nativeType)
static const llvm::fltSemantics * getFloatSemantics(TruncfSrcElemTypes etype)
Float semantics the element type attributes of xevm.truncf and xevm.extf stand for.
A multi-dimensional affine map Affine map's are immutable like Type's, and they are uniqued.
static AffineMap get(MLIRContext *context)
Returns a zero result affine map with no dimensions or symbols: () -> ().
static SmallVector< AffineMap, 4 > inferFromExprList(ArrayRef< ArrayRef< AffineExpr > > exprsList, MLIRContext *context)
Returns a vector of AffineMaps; each with as many results as exprs.size(), as many dims as the larges...
BlockArgument getArgument(unsigned i)
bool getValue() const
Return the boolean value of this attribute.
IntegerAttr getIndexAttr(int64_t value)
IntegerAttr getI32IntegerAttr(int32_t value)
IntegerAttr getIntegerAttr(Type type, int64_t value)
AffineMap getMultiDimIdentityMap(unsigned rank)
FloatAttr getFloatAttr(Type type, double value)
AffineExpr getAffineConstantExpr(int64_t constant)
IntegerType getIntegerType(unsigned width)
Ty getType(Args &&...args)
Get or construct an instance of the type Ty with provided arguments.
BoolAttr getBoolAttr(bool value)
TypedAttr getZeroAttr(Type type)
AffineExpr getAffineDimExpr(unsigned position)
MLIRContext * getContext() const
IntegerAttr getI8IntegerAttr(int8_t value)
An attribute that represents a reference to a dense vector or tensor object.
auto getValues() const
Return the held element values as a range of the given type.
An attribute that represents a reference to a dense integer vector or tensor object.
static DenseIntElementsAttr get(const ShapedType &type, Arg &&arg)
Get an instance of a DenseIntElementsAttr with the given arguments.
ImplicitLocOpBuilder maintains a 'current location', allowing use of the create<> method without spec...
This class defines the main interface for locations in MLIR and acts as a non-nullable wrapper around...
This class helps build Operations.
void createOrFold(SmallVectorImpl< Value > &results, Location location, Args &&...args)
Create an operation of specific op type at the current insertion point, and immediately try to fold i...
This class represents a single result from folding an operation.
Operation is the basic unit of execution within MLIR.
Value getOperand(unsigned idx)
Location getLoc()
The source location the operation was defined or derived from.
unsigned getNumOperands()
result_type_range getResultTypes()
operand_range getOperands()
Returns an iterator on the underlying Value's.
result_range getResults()
unsigned getNumResults()
Return the number of results held by this operation.
A special type of RewriterBase that coordinates the application of a rewrite pattern on the current I...
MLIRContext * getContext() const
RewritePatternSet & add(ConstructorArg &&arg, ConstructorArgs &&...args)
Add an instance of each of the pattern types 'Ts' to the pattern list with the given arguments.
virtual void replaceOp(Operation *op, ValueRange newValues)
Replace the results of the given (original) operation with the specified list of values (replacements...
std::enable_if_t<!std::is_convertible< CallbackT, Twine >::value, LogicalResult > notifyMatchFailure(Location loc, CallbackT &&reasonCallback)
Used to notify the listener that the IR failed to be rewritten because of a match failure,...
OpTy replaceOpWithNewOp(Operation *op, Args &&...args)
Replace the results of the given (original) op with a new op that is created without verification (re...
This class provides an abstraction over the various different ranges of value types.
Instances of the Type class are uniqued, have an immutable identifier and an optional mutable compone...
bool isUnsignedInteger() const
Return true if this is an unsigned integer type (with the specified width).
bool isInteger() const
Return true if this is an integer type (with the specified width).
bool isIntOrFloat() const
Return true if this is an integer (of any signedness) or a float type.
unsigned getIntOrFloatBitWidth() const
Return the bit width of an integer or a float type, assert failure on other types.
This class provides an abstraction over the different types of ranges over Values.
type_range getType() const
Type front()
Return first type in the range.
This class represents an instance of an SSA value in the MLIR system, representing a computable value...
Type getType() const
Return the type of this value.
static ConstantIndexOp create(OpBuilder &builder, Location location, int64_t value)
static ConstantIntOp create(OpBuilder &builder, Location location, int64_t value, unsigned width)
InFlightDiagnostic & next(InFlightDiagnostic &diag)
Starts a new message part in an in-flight diagnostic.
Value createCanonicalRankReducingExtractSliceOp(OpBuilder &b, Location loc, Value tensor, RankedTensorType targetType)
Create a rank-reducing ExtractSliceOp @[0 .
OpFoldResult getMixedSize(OpBuilder &builder, Location loc, Value value, int64_t dim)
Return the dimension of the given tensor value.
SmallVector< OpFoldResult > getMixedSizes(OpBuilder &builder, Location loc, Value value)
Return the dimensions of the given tensor value.
Value clampFloatHelper(Location loc, Value arg, Value min, Value max, OpBuilder &rewriter)
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...