26#include "llvm/ADT/SmallVectorExtras.h"
27#include "llvm/ADT/TypeSwitch.h"
41static std::optional<linalg::ElementwiseKind>
43 using linalg::ElementwiseKind;
44 const bool isFloat = isa<FloatType>(elementTy);
46 const bool isBoolean = elementTy.
isInteger(1);
50 if (isa<tosa::AbsOp>(op))
51 return ElementwiseKind::abs;
52 if (isa<tosa::CeilOp>(op))
53 return ElementwiseKind::ceil;
54 if (isa<tosa::FloorOp>(op))
55 return ElementwiseKind::floor;
56 if (isa<tosa::ExpOp>(op))
57 return ElementwiseKind::exp;
58 if (isa<tosa::LogOp>(op))
59 return ElementwiseKind::log;
60 if (isa<tosa::RsqrtOp>(op))
61 return ElementwiseKind::rsqrt;
62 if (isa<tosa::SinOp>(op))
63 return ElementwiseKind::sin;
64 if (isa<tosa::CosOp>(op))
65 return ElementwiseKind::cos;
66 if (isa<tosa::TanhOp>(op))
67 return ElementwiseKind::tanh;
68 if (isa<tosa::ErfOp>(op))
69 return ElementwiseKind::erf;
70 if (isa<tosa::ReciprocalOp>(op))
71 return ElementwiseKind::reciprocal;
72 if (isa<tosa::PowOp>(op))
73 return ElementwiseKind::powf;
77 if (isFloat || (isSignlessInt && !isBoolean)) {
78 if (isa<tosa::AddOp>(op))
79 return ElementwiseKind::add;
80 if (isa<tosa::SubOp>(op))
81 return ElementwiseKind::sub;
85 if (isSignlessInt && !isBoolean && isa<tosa::IntDivOp>(op))
86 return ElementwiseKind::div;
92 if (
auto mulOp = dyn_cast<tosa::MulOp>(op)) {
94 const bool zeroShift =
97 const bool matchingTypes =
98 cast<ShapedType>(mulOp.getInput1().getType()).getElementType() ==
100 cast<ShapedType>(mulOp.getInput2().getType()).getElementType() ==
102 if ((isFloat || (isSignlessInt && !isBoolean)) && zeroShift &&
104 return ElementwiseKind::mul;
109 if (
auto negateOp = dyn_cast<tosa::NegateOp>(op)) {
111 FailureOr<int64_t> inZp = negateOp.getInput1ZeroPoint();
112 FailureOr<int64_t> outZp = negateOp.getOutputZeroPoint();
113 if (succeeded(inZp) && succeeded(outZp) && *inZp == 0 && *outZp == 0)
114 return ElementwiseKind::negf;
121 auto nanModeIsPropagate = [](
auto tosaOp) {
122 return tosaOp.getNanMode() == NanPropagationMode::PROPAGATE;
124 if (isFloat || (isSignlessInt && !isBoolean)) {
125 if (
auto maxOp = dyn_cast<tosa::MaximumOp>(op)) {
126 if (!isFloat || nanModeIsPropagate(maxOp))
127 return ElementwiseKind::max_signed;
129 if (
auto minOp = dyn_cast<tosa::MinimumOp>(op)) {
130 if (!isFloat || nanModeIsPropagate(minOp))
131 return ElementwiseKind::min_signed;
136 if ((isFloat || isSignlessInt || isBoolean) && isa<tosa::SelectOp>(op))
137 return ElementwiseKind::select;
142std::optional<linalg::ElementwiseKind>
149 auto operandTy = dyn_cast<RankedTensorType>(operand.getType());
150 if (!operandTy || !operandTy.hasStaticShape())
154 if (!resultTy || !resultTy.hasStaticShape())
161 TypedAttr padAttr,
OpBuilder &rewriter) {
163 if (llvm::all_of(pad, [](
int64_t p) {
return p == 0; }))
166 ShapedType inputTy = cast<ShapedType>(input.
getType());
167 Type inputETy = inputTy.getElementType();
168 auto inputShape = inputTy.getShape();
170 assert((inputShape.size() * 2) == pad.size());
175 for (
size_t i : llvm::seq(inputShape.size())) {
176 auto lowPad = pad[i * 2];
177 auto highPad = pad[i * 2 + 1];
178 if (ShapedType::isDynamic(inputShape[i]))
179 paddedShape.push_back(inputShape[i]);
181 paddedShape.push_back(inputShape[i] + highPad + lowPad);
186 Value padValue = arith::ConstantOp::create(rewriter, loc, padAttr);
188 return tensor::PadOp::create(rewriter, loc,
189 RankedTensorType::get(paddedShape, inputETy),
190 input, lowIndices, highIndices, padValue);
197 ShapedType resultTy = cast<ShapedType>(conv.
getType());
198 return linalg::GenericOp::create(
202 Value biasVal = args[0];
203 Type resType = args[1].getType();
204 if (resType != biasVal.
getType()) {
206 arith::ExtSIOp::create(builder, loc, resType, biasVal);
209 arith::AddIOp::create(builder, loc, biasVal, args[1]);
210 linalg::YieldOp::create(builder, loc, added);
219 ShapedType resultTy = cast<ShapedType>(
result.getType());
220 ShapedType sourceTy = cast<ShapedType>(source.
getType());
221 const int64_t resultRank = resultTy.getRank();
222 const int64_t sourceRank = sourceTy.getRank();
230 assert(sourceTy.hasStaticShape() &&
231 "Dynamic broadcasting shapes not supported!");
232 if (sourceRank == 1 && sourceTy.getDimSize(0) == 1) {
235 for (
auto dim : llvm::seq<int64_t>(0, sourceRank)) {
237 sourceDims.push_back(expr);
251 ShapedType resultTy = cast<ShapedType>(
result.getType());
252 const int64_t resultRank = resultTy.getRank();
259 return linalg::GenericOp::create(
263 Value biasVal = args[0];
264 Type resType = args[1].getType();
265 if (resType != biasVal.
getType()) {
267 resultTy.getElementType().isFloat()
268 ? arith::ExtFOp::create(
270 ValueRange{biasVal}, arith::ExtFOp::Properties{})
272 : arith::ExtSIOp::create(builder, loc, resType,
276 linalg::YieldOp::create(builder, loc, biasVal);
297 auto one = arith::ConstantOp::create(rewriter, loc,
298 IntegerAttr::get(inputDim.
getType(), 1));
300 Value paddedBefore = arith::AddIOp::create(builder, inputDim, padBefore);
302 Value paddedAfter = arith::AddIOp::create(builder, paddedBefore, padAfter);
304 Value subOne = arith::SubIOp::create(builder, kernelDim, one);
306 Value dilated = arith::MulIOp::create(builder, dilation, subOne);
307 Value addOne = arith::AddIOp::create(builder, dilated, one);
309 Value subtract = arith::SubIOp::create(builder, paddedAfter, addOne);
311 Value divide = arith::DivUIOp::create(builder, subtract, stride);
312 return arith::AddIOp::create(builder, divide, one);
321 ShapedType inputTy = cast<ShapedType>(input.
getType());
322 int64_t inputRank = inputTy.getRank();
325 dynDims.resize(resultTy.getRank());
327 for (uint32_t i = 0, s = inputSizeDims.size(); i < s; ++i) {
328 int64_t inputDim = inputSizeDims[i];
329 int64_t kernelDim = kernelSizeDims[i];
330 if (resultTy.isDynamicDim(inputDim)) {
331 auto padTop = padAttr[i * 2];
332 auto padBottom = padAttr[i * 2 + 1];
333 auto stride = strideAttr[i];
334 auto dilation = dilationAttr[i];
335 Value initDynDim = tensor::DimOp::create(rewriter, loc, input, inputDim);
337 tensor::DimOp::create(rewriter, loc, weight, kernelDim);
341 kernelDynDim, stride, dilation, rewriter);
346 for (
int i = 0; i < inputRank; i++) {
347 if (resultTy.isDynamicDim(i) && !dynDims[i])
348 dynDims[i] = tensor::DimOp::create(rewriter, loc, input, i);
360 reassociationMap.resize(outputRank);
361 for (
int i = 0; i < outputRank; i++) {
364 reassociationMap[outputRank - 1].push_back(
370template <
typename TosaConvOp,
typename LinalgConvOp,
typename LinalgConvQOp>
371class ConvConverter :
public OpConversionPattern<TosaConvOp> {
373 using OpConversionPattern<TosaConvOp>::OpConversionPattern;
375 matchAndRewrite(TosaConvOp op,
typename TosaConvOp::Adaptor adaptor,
376 ConversionPatternRewriter &rewriter)
const final {
377 Location loc = op->getLoc();
378 Value input = op->getOperand(0);
379 Value weight = op->getOperand(1);
380 Value bias = op->getOperand(2);
382 ShapedType inputTy = cast<ShapedType>(input.
getType());
383 ShapedType weightTy = cast<ShapedType>(weight.
getType());
384 ShapedType biasTy = cast<ShapedType>(bias.
getType());
385 ShapedType resultTy = cast<ShapedType>(op->getResult(0).getType());
387 Type inputETy = inputTy.getElementType();
393 Type accETy = op.getAccType();
394 Type accTy = RankedTensorType::get(resultTy.getShape(), accETy);
397 FailureOr<int64_t> maybeIZp = op.getInputZeroPoint();
399 return rewriter.notifyMatchFailure(
400 op,
"input zero point cannot be statically determined");
402 FailureOr<int64_t> maybeWZp = op.getWeightZeroPoint();
404 return rewriter.notifyMatchFailure(
405 op,
"weight zero point cannot be statically determined");
407 const int64_t inputZpVal = *maybeIZp;
408 const int64_t weightZpVal = *maybeWZp;
410 if (op.verifyInputZeroPoint(inputZpVal).failed())
411 return rewriter.notifyMatchFailure(
412 op,
"input zero point must be zero for non-int8 integer types");
414 if (op.verifyWeightZeroPoint(weightZpVal).failed())
415 return rewriter.notifyMatchFailure(
416 op,
"weight zero point must be zero for non-int8 integer types");
418 bool hasZp = (inputZpVal != 0) || (weightZpVal != 0);
420 if (!weightTy.hasStaticShape() || !biasTy.hasStaticShape())
421 return rewriter.notifyMatchFailure(
422 op,
"tosa.conv ops require static shapes for weight and bias");
425 return rewriter.notifyMatchFailure(
426 op,
"tosa.conv ops does not support unsigned integer input");
428 llvm::SmallVector<int64_t> inputSizeDims;
429 llvm::SmallVector<int64_t> kernelSizeDims;
430 for (
int i = 1; i < resultTy.getRank() - 1; i++) {
431 inputSizeDims.push_back(i);
432 kernelSizeDims.push_back(i);
436 loc, input, weight, resultTy, padAttr.
asArrayRef(),
438 inputSizeDims, kernelSizeDims, rewriter);
440 auto weightShape = weightTy.getShape();
443 TypedAttr zeroAttr = rewriter.getZeroAttr(inputETy);
452 if (inputZpVal < intMin || inputZpVal > intMax)
453 return rewriter.notifyMatchFailure(
454 op,
"tosa.conv op quantization has zp outside of input range");
456 zeroAttr = rewriter.getIntegerAttr(inputETy, inputZpVal);
459 llvm::SmallVector<int64_t> pad;
461 llvm::append_range(pad, padAttr.
asArrayRef());
462 pad.resize(pad.size() + 2, 0);
463 input =
applyPad(loc, input, pad, zeroAttr, rewriter);
465 if (4 == inputTy.getRank()) {
469 hasZp ? std::is_same_v<LinalgConvQOp, linalg::Conv2DNhwcHwcfQOp>
470 : std::is_same_v<LinalgConvOp, linalg::Conv2DNhwcHwcfOp>;
476 SmallVector<int32_t> weightPerm;
477 for (
int i = 1; i < resultTy.getRank(); i++)
478 weightPerm.push_back(i);
479 weightPerm.push_back(0);
481 SmallVector<int64_t> newWeightShape;
482 for (
auto dim : weightPerm)
483 newWeightShape.push_back(weightShape[dim]);
484 auto weightPermAttr = rewriter.getDenseI32ArrayAttr(weightPerm);
486 RankedTensorType::get(newWeightShape, weightTy.getElementType());
487 weight = tosa::TransposeOp::create(rewriter, loc, newWeightTy, weight,
495 if (5 == inputTy.getRank()) {
498 SmallVector<int32_t> weightPerm;
499 for (
int i = 1; i < resultTy.getRank(); i++)
500 weightPerm.push_back(i);
501 weightPerm.push_back(0);
503 SmallVector<int64_t> newWeightShape;
504 for (
auto dim : weightPerm)
505 newWeightShape.push_back(weightShape[dim]);
506 auto weightPermAttr = rewriter.getDenseI32ArrayAttr(weightPerm);
508 RankedTensorType::get(newWeightShape, weightTy.getElementType());
509 weight = tosa::TransposeOp::create(rewriter, loc, newWeightTy, weight,
514 ArrayRef<int64_t> stride = strideTosaAttr;
515 ArrayRef<int64_t> dilation = dilationTosaAttr;
518 auto strideAttr = rewriter.getI64TensorAttr(stride);
519 auto dilationAttr = rewriter.getI64TensorAttr(dilation);
521 Value biasEmptyTensor = tensor::EmptyOp::create(
522 rewriter, loc, resultTy.getShape(), accETy, filteredDims);
524 Value broadcastBias =
528 auto iZp = rewriter.getI32IntegerAttr(inputZpVal);
529 auto kZp = rewriter.getI32IntegerAttr(weightZpVal);
531 auto iZpVal = arith::ConstantOp::create(rewriter, loc, iZp);
532 auto kZpVal = arith::ConstantOp::create(rewriter, loc, kZp);
534 Value conv = LinalgConvQOp::create(
535 rewriter, loc, resultTy,
537 ValueRange{broadcastBias}, strideAttr, dilationAttr)
540 rewriter.replaceOp(op, conv);
544 Value conv = LinalgConvOp::create(
545 rewriter, loc, accTy,
ValueRange{input, weight},
546 ValueRange{broadcastBias}, strideAttr, dilationAttr)
551 if (resultTy != accTy)
552 conv = tosa::CastOp::create(rewriter, loc, resultTy, conv);
554 rewriter.replaceOp(op, conv);
559class DepthwiseConvConverter
560 :
public OpConversionPattern<tosa::DepthwiseConv2DOp> {
562 using OpConversionPattern<tosa::DepthwiseConv2DOp>::OpConversionPattern;
564 matchAndRewrite(tosa::DepthwiseConv2DOp op, OpAdaptor adaptor,
565 ConversionPatternRewriter &rewriter)
const final {
566 Location loc = op->getLoc();
567 Value input = op->getOperand(0);
568 Value weight = op->getOperand(1);
569 Value bias = op->getOperand(2);
571 ShapedType inputTy = cast<ShapedType>(input.
getType());
572 ShapedType weightTy = cast<ShapedType>(weight.
getType());
573 ShapedType biasTy = cast<ShapedType>(bias.
getType());
574 ShapedType resultTy = cast<ShapedType>(op->getResult(0).getType());
575 int64_t resultRank = resultTy.getRank();
577 Type inputETy = inputTy.getElementType();
578 Type resultETy = resultTy.getElementType();
580 auto padAttr = op.getPadAttr();
581 auto strideTosaAttr = op.getStrideAttr();
582 auto dilationTosaAttr = op.getDilationAttr();
584 Type accETy = op.getAccType();
586 if (!weightTy.hasStaticShape() || !biasTy.hasStaticShape())
587 return rewriter.notifyMatchFailure(
588 op,
"tosa.depthwise_conv ops require static shapes");
592 loc, input, weight, resultTy, padAttr.
asArrayRef(),
599 FailureOr<int64_t> maybeIZp = op.getInputZeroPoint();
600 FailureOr<int64_t> maybeWZp = op.getWeightZeroPoint();
602 return rewriter.notifyMatchFailure(
603 op,
"input zero point cannot be statically determined");
605 return rewriter.notifyMatchFailure(
606 op,
"weight zero point cannot be statically determined");
608 const int64_t inputZpVal = *maybeIZp;
609 const int64_t weightZpVal = *maybeWZp;
611 if (op.verifyInputZeroPoint(inputZpVal).failed())
612 return rewriter.notifyMatchFailure(
613 op,
"input zero point must be zero for non-int8 integer types");
615 if (op.verifyWeightZeroPoint(weightZpVal).failed())
616 return rewriter.notifyMatchFailure(
617 op,
"weight zero point must be zero for non-int8 integer types");
619 bool hasNullZps = (inputZpVal == 0) && (weightZpVal == 0);
620 auto weightShape = weightTy.getShape();
621 auto resultShape = resultTy.getShape();
624 TypedAttr zeroAttr = rewriter.getZeroAttr(inputETy);
633 if (inputZpVal < intMin || inputZpVal > intMax)
634 return rewriter.notifyMatchFailure(
635 op,
"tosa.depthwise_conv op quantization has zp outside of input "
638 zeroAttr = rewriter.getIntegerAttr(inputETy, inputZpVal);
641 llvm::SmallVector<int64_t> pad;
643 llvm::append_range(pad, padAttr.
asArrayRef());
644 pad.resize(pad.size() + 2, 0);
646 input =
applyPad(loc, input, pad, zeroAttr, rewriter);
649 ArrayRef<int64_t> stride = strideTosaAttr;
650 ArrayRef<int64_t> dilation = dilationTosaAttr;
653 auto strideAttr = rewriter.getI64TensorAttr(stride);
654 auto dilationAttr = rewriter.getI64TensorAttr(dilation);
655 ShapedType linalgConvTy =
656 RankedTensorType::get({resultShape[0], resultShape[1], resultShape[2],
657 weightShape[2], weightShape[3]},
660 auto resultZeroAttr = rewriter.getZeroAttr(accETy);
661 Value emptyTensor = tensor::EmptyOp::create(
662 rewriter, loc, linalgConvTy.getShape(), accETy, filteredDims);
663 Value zero = arith::ConstantOp::create(rewriter, loc, resultZeroAttr);
664 Value zeroTensor = linalg::FillOp::create(rewriter, loc,
ValueRange{zero},
668 Value biasEmptyTensor = tensor::EmptyOp::create(
669 rewriter, loc, resultTy.getShape(), resultETy, filteredDims);
672 SmallVector<AffineMap, 4> indexingMaps;
674 indexingMaps.push_back(rewriter.getMultiDimIdentityMap(resultRank));
675 indexingMaps.push_back(rewriter.getMultiDimIdentityMap(resultRank));
678 Value conv = linalg::DepthwiseConv2DNhwcHwcmOp::create(
679 rewriter, loc, linalgConvTy,
ValueRange{input, weight},
680 ValueRange{zeroTensor}, strideAttr, dilationAttr)
685 if (accETy != resultETy)
686 conv = tosa::CastOp::create(
688 RankedTensorType::get(cast<ShapedType>(conv.
getType()).getShape(),
692 SmallVector<ReassociationExprs, 4> reassociationMap;
694 Value convReshape = tensor::CollapseShapeOp::create(
695 rewriter, loc, resultTy, conv, reassociationMap);
698 linalg::GenericOp::create(
699 rewriter, loc, resultTy,
ValueRange({bias, convReshape}),
701 [&](OpBuilder &nestedBuilder, Location nestedLoc,
704 if (llvm::isa<FloatType>(inputETy))
705 added = arith::AddFOp::create(nestedBuilder, loc, args[0],
708 added = arith::AddIOp::create(nestedBuilder, loc, args[0],
710 linalg::YieldOp::create(nestedBuilder, nestedLoc, added);
713 rewriter.replaceOp(op,
result);
715 IntegerAttr iZp = rewriter.getI32IntegerAttr(inputZpVal);
716 IntegerAttr wZp = rewriter.getI32IntegerAttr(weightZpVal);
717 auto iZpVal = arith::ConstantOp::create(rewriter, loc, iZp);
718 auto kZpVal = arith::ConstantOp::create(rewriter, loc, wZp);
719 Value conv = linalg::DepthwiseConv2DNhwcHwcmQOp::create(
720 rewriter, loc, linalgConvTy,
722 ValueRange{zeroTensor}, strideAttr, dilationAttr)
724 SmallVector<ReassociationExprs, 4> reassociationMap;
726 Value convReshape = tensor::CollapseShapeOp::create(
727 rewriter, loc, resultTy, conv, reassociationMap);
729 rewriter, loc, bias, convReshape, biasEmptyTensor, indexingMaps);
730 rewriter.replaceOp(op,
result);
741template <
typename TosaOp>
742class ElementwiseConverter :
public OpConversionPattern<TosaOp> {
744 using OpConversionPattern<TosaOp>::OpConversionPattern;
745 using typename OpConversionPattern<TosaOp>::OpAdaptor;
748 matchAndRewrite(TosaOp op, OpAdaptor adaptor,
749 ConversionPatternRewriter &rewriter)
const final {
750 std::optional<linalg::ElementwiseKind> kind =
753 return rewriter.notifyMatchFailure(
754 op,
"op has no linalg.elementwise counterpart");
756 Location loc = op.getLoc();
757 auto resultTy = cast<RankedTensorType>(op.getType());
758 const int64_t rank = resultTy.getRank();
760 const unsigned arity =
765 ValueRange inputs = adaptor.getOperands().take_front(arity);
769 SmallVector<AffineMap> indexingMaps;
770 for (Value operand : inputs) {
771 ArrayRef<int64_t> shape = cast<ShapedType>(operand.getType()).getShape();
772 SmallVector<AffineExpr> exprs;
773 for (
const auto &dim : llvm::enumerate(shape)) {
774 bool requiresBroadcast =
775 dim.value() == 1 && resultTy.getDimSize(dim.index()) != 1;
776 exprs.push_back(requiresBroadcast
777 ? rewriter.getAffineConstantExpr(0)
778 : rewriter.getAffineDimExpr(dim.index()));
781 rewriter.getContext()));
783 indexingMaps.push_back(rewriter.getMultiDimIdentityMap(rank));
786 Value emptyTensor = tensor::EmptyOp::create(
787 rewriter, loc, resultTy.getShape(), resultTy.getElementType());
789 rewriter.replaceOpWithNewOp<linalg::ElementwiseOp>(
791 rewriter.getAffineMapArrayAttr(indexingMaps));
797getMatMulBatchCollapseReassociation(
int64_t rank) {
799 for (
int64_t dim = 0; dim < rank - 2; ++dim)
801 reassociation[1].push_back(rank - 2);
802 reassociation[2].push_back(rank - 1);
803 return reassociation;
806class MatMulConverter :
public OpConversionPattern<tosa::MatMulOp> {
808 using OpConversionPattern<tosa::MatMulOp>::OpConversionPattern;
810 matchAndRewrite(tosa::MatMulOp op, OpAdaptor adaptor,
811 ConversionPatternRewriter &rewriter)
const final {
812 Location loc = op.getLoc();
813 auto aType = dyn_cast<RankedTensorType>(adaptor.getA().getType());
814 auto bType = dyn_cast<RankedTensorType>(adaptor.getB().getType());
815 auto outputType = dyn_cast<RankedTensorType>(op.getType());
816 if (!aType || !bType || !outputType)
817 return rewriter.notifyMatchFailure(op,
"only supports ranked tensors");
819 FailureOr<int64_t> maybeAZp = op.getAZeroPoint();
820 FailureOr<int64_t> maybeBZp = op.getBZeroPoint();
822 return rewriter.notifyMatchFailure(
823 op,
"input A zero point cannot be statically determined");
825 return rewriter.notifyMatchFailure(
826 op,
"input B zero point cannot be statically determined");
828 const int64_t aZpVal = *maybeAZp;
829 const int64_t bZpVal = *maybeBZp;
831 if (op.verifyAZeroPoint(aZpVal).failed())
832 return rewriter.notifyMatchFailure(
833 op,
"input A zero point must be zero for non-int8 integer types");
835 if (op.verifyBZeroPoint(bZpVal).failed())
836 return rewriter.notifyMatchFailure(
837 op,
"input B zero point must be zero for non-int8 integer types");
839 const int64_t outputRank = outputType.getRank();
840 auto [a,
b, zeroTensor] =
845 const bool hasNonzeroZeroPoint = aZpVal != 0 || bZpVal != 0;
846 if (hasNonzeroZeroPoint) {
847 aZp = arith::ConstantOp::create(rewriter, loc,
848 rewriter.getI32IntegerAttr(aZpVal));
849 bZp = arith::ConstantOp::create(rewriter, loc,
850 rewriter.getI32IntegerAttr(bZpVal));
853 if (outputRank == 2) {
854 if (!hasNonzeroZeroPoint) {
855 rewriter.replaceOpWithNewOp<linalg::MatmulOp>(op,
TypeRange{outputType},
859 rewriter.replaceOpWithNewOp<linalg::QuantizedMatmulOp>(
866 if (outputRank == 3) {
867 if (!hasNonzeroZeroPoint) {
868 rewriter.replaceOpWithNewOp<linalg::BatchMatmulOp>(
872 rewriter.replaceOpWithNewOp<linalg::QuantizedBatchMatmulOp>(
879 SmallVector<ReassociationIndices> reassociation =
880 getMatMulBatchCollapseReassociation(outputRank);
881 auto collapse = [&](Value value) {
882 auto type = cast<RankedTensorType>(value.getType());
884 tensor::CollapseShapeOp::inferCollapsedType(type, reassociation);
885 return tensor::CollapseShapeOp::create(rewriter, loc, collapsedType,
886 value, reassociation);
889 Value collapsedA = collapse(a);
890 Value collapsedB = collapse(
b);
891 Value collapsedInit = collapse(zeroTensor);
892 auto collapsedOutputType = cast<RankedTensorType>(collapsedInit.
getType());
893 Value collapsedResult;
895 if (!hasNonzeroZeroPoint) {
897 linalg::BatchMatmulOp::create(
898 rewriter, loc,
TypeRange{collapsedOutputType},
902 collapsedResult = linalg::QuantizedBatchMatmulOp::create(
903 rewriter, loc,
TypeRange{collapsedOutputType},
909 Value
result = tensor::ExpandShapeOp::create(
910 rewriter, loc, outputType, collapsedResult, reassociation,
912 rewriter.replaceOp(op,
result);
917class MatMulTConverter :
public OpConversionPattern<tosa::MatMulTOp> {
919 using OpConversionPattern<tosa::MatMulTOp>::OpConversionPattern;
921 matchAndRewrite(tosa::MatMulTOp op, OpAdaptor adaptor,
922 ConversionPatternRewriter &rewriter)
const final {
923 Location loc = op.getLoc();
924 auto aType = dyn_cast<RankedTensorType>(adaptor.getA().getType());
925 auto bType = dyn_cast<RankedTensorType>(adaptor.getB().getType());
926 auto outputType = dyn_cast<RankedTensorType>(op.getType());
927 if (!aType || !bType || !outputType)
928 return rewriter.notifyMatchFailure(op,
"only supports ranked tensors");
930 FailureOr<int64_t> maybeAZp = op.getAZeroPoint();
931 FailureOr<int64_t> maybeBZp = op.getBZeroPoint();
933 return rewriter.notifyMatchFailure(
934 op,
"input A zero point cannot be statically determined");
936 return rewriter.notifyMatchFailure(
937 op,
"input B zero point cannot be statically determined");
939 const int64_t aZpVal = *maybeAZp;
940 const int64_t bZpVal = *maybeBZp;
942 if (aZpVal != 0 || bZpVal != 0)
943 return rewriter.notifyMatchFailure(
944 op,
"nonzero zero points require the generic MATMUL_T lowering");
946 const int64_t outputRank = outputType.getRank();
947 auto [a,
b, zeroTensor] =
951 if (outputRank == 2) {
952 rewriter.replaceOpWithNewOp<linalg::MatmulTransposeBOp>(
957 if (outputRank == 3) {
958 rewriter.replaceOpWithNewOp<linalg::BatchMatmulTransposeBOp>(
963 SmallVector<ReassociationIndices> reassociation =
964 getMatMulBatchCollapseReassociation(outputRank);
965 auto collapse = [&](Value value) {
966 auto type = cast<RankedTensorType>(value.getType());
968 tensor::CollapseShapeOp::inferCollapsedType(type, reassociation);
969 return tensor::CollapseShapeOp::create(rewriter, loc, collapsedType,
970 value, reassociation);
972 Value collapsedA = collapse(a);
973 Value collapsedB = collapse(
b);
974 Value collapsedInit = collapse(zeroTensor);
975 auto collapsedOutputType = cast<RankedTensorType>(collapsedInit.
getType());
976 Value collapsedResult =
978 rewriter, loc,
TypeRange{collapsedOutputType},
981 Value
result = tensor::ExpandShapeOp::create(
982 rewriter, loc, outputType, collapsedResult, reassociation,
984 rewriter.replaceOp(op,
result);
989class MaxPool2dConverter :
public OpConversionPattern<tosa::MaxPool2dOp> {
991 MaxPool2dConverter(
const TypeConverter &typeConverter, MLIRContext *context,
992 bool allowNonFinites)
993 : OpConversionPattern(typeConverter, context),
994 allowNonFinites(allowNonFinites) {}
997 static SmallVector<Value>
998 computeDynamicOutputSizes(tosa::MaxPool2dOp op, OpAdaptor adaptor,
999 ConversionPatternRewriter &rewriter) {
1000 TensorType resultTy = op.getType();
1001 Location loc = op.getLoc();
1003 Value input = adaptor.getInput();
1004 ArrayRef<int64_t> kernel = op.getKernel();
1005 ArrayRef<int64_t> pad = op.getPad();
1006 ArrayRef<int64_t> stride = op.getStride();
1008 SmallVector<Value> dynamicDims;
1011 if (resultTy.isDynamicDim(0))
1012 dynamicDims.push_back(tensor::DimOp::create(rewriter, loc, input, 0));
1015 for (int64_t dim : {1, 2}) {
1016 if (!resultTy.isDynamicDim(dim))
1020 int64_t index = dim - 1;
1023 Value ihw = tensor::DimOp::create(rewriter, loc, input, dim);
1030 pad[index * 2 + 1], khw, stride[index],
1032 dynamicDims.push_back(ohw);
1036 if (resultTy.isDynamicDim(3))
1037 dynamicDims.push_back(tensor::DimOp::create(rewriter, loc, input, 3));
1043 matchAndRewrite(tosa::MaxPool2dOp op, OpAdaptor adaptor,
1044 ConversionPatternRewriter &rewriter)
const final {
1045 Location loc = op.getLoc();
1046 Value input = adaptor.getInput();
1047 ShapedType inputTy = cast<ShapedType>(input.
getType());
1049 bool isUnsigned = op.getType().getElementType().isUnsignedInteger();
1050 ShapedType resultTy =
1051 getTypeConverter()->convertType<ShapedType>(op.getType());
1053 return rewriter.notifyMatchFailure(op,
"failed to convert type");
1054 Type resultETy = inputTy.getElementType();
1055 NanPropagationMode nanMode = op.getNanMode();
1057 SmallVector<Value> dynamicDims =
1058 computeDynamicOutputSizes(op, adaptor, rewriter);
1061 TypedAttr initialAttr;
1063 const llvm::fltSemantics &semantics =
1064 cast<FloatType>(resultETy).getFloatSemantics();
1065 if (llvm::APFloat::semanticsHasNaN(semantics))
1066 initialAttr = rewriter.getFloatAttr(
1068 nanMode == NanPropagationMode::IGNORE
1069 ? APFloat::getNaN(semantics)
1071 true, allowNonFinites));
1074 else if (isUnsigned)
1075 initialAttr = rewriter.getIntegerAttr(
1077 else if (isa<IntegerType>(resultETy))
1078 initialAttr = rewriter.getIntegerAttr(
1083 return rewriter.notifyMatchFailure(
1084 op,
"Unsupported initial value for tosa.maxpool_2d op");
1087 llvm::SmallVector<int64_t> pad;
1089 llvm::append_range(pad, op.getPad());
1090 pad.resize(pad.size() + 2, 0);
1095 Value paddedInput =
applyPad(loc, input, pad, initialAttr, rewriter);
1097 Value initialValue = arith::ConstantOp::create(rewriter, loc, initialAttr);
1099 ArrayRef<int64_t> kernel = op.getKernel();
1100 ArrayRef<int64_t> stride = op.getStride();
1102 Attribute strideAttr = rewriter.getI64VectorAttr(stride);
1103 Attribute dilationAttr = rewriter.getI64VectorAttr({1, 1});
1107 tensor::EmptyOp::create(rewriter, loc, resultTy.getShape(),
1108 resultTy.getElementType(), dynamicDims);
1110 Value filledEmptyTensor =
1111 linalg::FillOp::create(rewriter, loc, initialValue, emptyTensor)
1114 Value fakeWindowDims =
1115 tensor::EmptyOp::create(rewriter, loc, kernel, resultETy);
1118 rewriter.replaceOpWithNewOp<linalg::PoolingNhwcMaxUnsignedOp>(
1119 op, ArrayRef<Type>{resultTy},
ValueRange{paddedInput, fakeWindowDims},
1120 filledEmptyTensor, strideAttr, dilationAttr);
1121 return llvm::success();
1124 auto resultOp = linalg::PoolingNhwcMaxOp::create(
1125 rewriter, op->getLoc(), ArrayRef<Type>{resultTy},
1126 ValueRange{paddedInput, fakeWindowDims}, filledEmptyTensor, strideAttr,
1129 rewriter.replaceOp(op, resultOp);
1143 if (nanMode == NanPropagationMode::IGNORE) {
1144 auto genericOp = linalg::GenericOp::create(
1145 rewriter, loc, resultOp.getType(0), resultOp.getInputs(),
1146 resultOp.getOutputs(), resultOp.getIndexingMapsArray(),
1147 resultOp.getIteratorTypesArray(),
1148 [&](OpBuilder &opBuilder, Location loc,
ValueRange blockArgs) {
1150 auto oldBlock = resultOp.getRegion().begin();
1151 auto oldArgs = oldBlock->getArguments();
1152 auto &oldMaxOp = *resultOp.getBlock()->begin();
1153 map.map(oldArgs, blockArgs);
1154 auto *newOp = opBuilder.clone(oldMaxOp, map);
1156 arith::CmpFOp::create(opBuilder, loc, arith::CmpFPredicate::UNO,
1157 blockArgs.front(), blockArgs.front());
1158 Value accumulatorIsNaN =
1159 arith::CmpFOp::create(opBuilder, loc, arith::CmpFPredicate::UNO,
1160 blockArgs.back(), blockArgs.back());
1161 Value finiteMaximum =
1162 arith::SelectOp::create(opBuilder, loc, accumulatorIsNaN,
1163 blockArgs.front(), newOp->getResult(0));
1164 Value result = arith::SelectOp::create(
1165 opBuilder, loc, inputIsNaN, blockArgs.back(), finiteMaximum);
1166 linalg::YieldOp::create(opBuilder, loc, result);
1168 rewriter.replaceOp(resultOp, genericOp);
1175 bool allowNonFinites;
1180 using OpRewritePattern<tosa::AvgPool2dOp>::OpRewritePattern;
1182 LogicalResult matchAndRewrite(tosa::AvgPool2dOp op,
1183 PatternRewriter &rewriter)
const final {
1184 Location loc = op.getLoc();
1185 Value input = op.getInput();
1186 ShapedType inputTy = cast<ShapedType>(input.
getType());
1187 Type inElementTy = inputTy.getElementType();
1189 ShapedType resultTy = cast<ShapedType>(op.getType());
1190 Type resultETy = cast<ShapedType>(op.getType()).getElementType();
1192 Type accETy = op.getAccType();
1193 ShapedType accTy = resultTy.clone(accETy);
1195 auto dynamicDimsOr =
1197 if (!dynamicDimsOr.has_value())
1199 SmallVector<Value> dynamicDims = *dynamicDimsOr;
1201 FailureOr<int64_t> maybeIZp = op.getInputZeroPoint();
1202 FailureOr<int64_t> maybeOZp = op.getOutputZeroPoint();
1204 return rewriter.notifyMatchFailure(
1205 op,
"input zero point could not be statically determined");
1207 return rewriter.notifyMatchFailure(
1208 op,
"output zero point could not be statically determined");
1210 const int64_t inputZpVal = *maybeIZp;
1211 const int64_t outputZpVal = *maybeOZp;
1214 llvm::SmallVector<int64_t> pad;
1216 llvm::append_range(pad, op.getPad());
1217 pad.resize(pad.size() + 2, 0);
1218 TypedAttr padAttr = rewriter.getZeroAttr(inElementTy);
1222 Value paddedInput =
applyPad(loc, input, pad, padAttr, rewriter);
1224 auto initialAttr = rewriter.getZeroAttr(accETy);
1225 Value initialValue = arith::ConstantOp::create(rewriter, loc, initialAttr);
1227 ArrayRef<int64_t> kernel = op.getKernel();
1228 ArrayRef<int64_t> stride = op.getStride();
1230 Attribute strideAttr = rewriter.getI64VectorAttr(stride);
1231 Attribute dilationAttr = rewriter.getI64VectorAttr({1, 1});
1234 Value poolEmptyTensor = tensor::EmptyOp::create(
1235 rewriter, loc, accTy.getShape(), accETy, dynamicDims);
1237 Value filledEmptyTensor =
1238 linalg::FillOp::create(rewriter, loc,
ValueRange{initialValue},
1242 Value fakeWindowDims =
1243 tensor::EmptyOp::create(rewriter, loc, kernel, accETy);
1246 Value poolingOp = linalg::PoolingNhwcSumOp::create(
1247 rewriter, loc, ArrayRef<Type>{accTy},
1249 filledEmptyTensor, strideAttr, dilationAttr)
1254 Value iH = tensor::DimOp::create(rewriter, loc, poolingOp, 1);
1255 Value iW = tensor::DimOp::create(rewriter, loc, poolingOp, 2);
1258 iH = arith::SubIOp::create(rewriter, loc, iH, one);
1259 iW = arith::SubIOp::create(rewriter, loc, iW, one);
1261 Value genericEmptyTensor = tensor::EmptyOp::create(
1262 rewriter, loc, resultTy.getShape(), resultETy, dynamicDims);
1264 auto affineMap = rewriter.getMultiDimIdentityMap(resultTy.getRank());
1265 auto genericOp = linalg::GenericOp::create(
1266 rewriter, loc, ArrayRef<Type>({resultTy}),
ValueRange{poolingOp},
1268 ArrayRef<AffineMap>({affineMap, affineMap}),
1270 [&](OpBuilder &
b, Location loc,
ValueRange args) {
1275 auto padFn = [&](Value valid, Value pos, int64_t pad) -> Value {
1280 Value dpos = arith::SubIOp::create(rewriter, loc, pos, padVal);
1282 Value offset = arith::MinSIOp::create(rewriter, loc, dpos, zero);
1283 return arith::AddIOp::create(rewriter, loc, valid, offset)
1287 auto coverageFn = [&](int64_t i, Value isize) -> Value {
1294 Value left = linalg::IndexOp::create(rewriter, loc, i);
1295 Value right = arith::SubIOp::create(rewriter, loc, isize, left);
1296 left = arith::MulIOp::create(rewriter, loc, left, strideVal);
1297 right = arith::MulIOp::create(rewriter, loc, right, strideVal);
1300 val = padFn(val, left, pad[i * 2]);
1301 val = padFn(val, right, pad[i * 2 + 1]);
1302 return arith::MaxSIOp::create(rewriter, loc, one, val);
1306 Value kH3 = coverageFn(1, iH);
1307 Value kW3 = coverageFn(2, iW);
1310 auto count = arith::IndexCastOp::create(
1311 rewriter, loc, rewriter.getI32Type(),
1312 arith::MulIOp::create(rewriter, loc, kH3, kW3));
1317 Value poolVal = args[0];
1318 if (isa<FloatType>(accETy)) {
1319 auto countF = arith::SIToFPOp::create(rewriter, loc, accETy, count);
1320 poolVal = arith::DivFOp::create(rewriter, loc, poolVal, countF)
1325 arith::TruncFOp::create(rewriter, loc, resultETy, poolVal);
1330 if (inputZpVal != 0) {
1331 auto inputZp = arith::ConstantOp::create(
1332 rewriter, loc,
b.getIntegerAttr(accETy, inputZpVal));
1334 arith::MulIOp::create(rewriter, loc, accETy, count, inputZp);
1336 arith::SubIOp::create(rewriter, loc, accETy, poolVal, offset);
1340 Value one32 = arith::ConstantOp::create(
1341 rewriter, loc, rewriter.getI32IntegerAttr(1));
1342 Value thirtyTwo32 = arith::ConstantOp::create(
1343 rewriter, loc, rewriter.getI32IntegerAttr(32));
1346 arith::SubIOp::create(rewriter, loc, count, one32);
1347 Value leadingZeros =
1348 math::CountLeadingZerosOp::create(rewriter, loc, countSubOne);
1350 arith::SubIOp::create(rewriter, loc, thirtyTwo32, leadingZeros);
1354 arith::ExtUIOp::create(rewriter, loc, rewriter.getI64Type(), k);
1355 Value thirtyShiftPlusOne = arith::ConstantOp::create(
1356 rewriter, loc, rewriter.getI64IntegerAttr((1 << 30) + 1));
1358 arith::ShLIOp::create(rewriter, loc, thirtyShiftPlusOne, k64);
1361 Value count64 = arith::ExtUIOp::create(
1362 rewriter, loc, rewriter.getI64Type(), count);
1364 arith::DivUIOp::create(rewriter, loc, numerator, count64);
1365 multiplier = arith::TruncIOp::create(
1366 rewriter, loc, rewriter.getI32Type(), multiplier);
1370 arith::TruncIOp::create(rewriter, loc, rewriter.getI8Type(), k);
1371 Value thirty8 = arith::ConstantOp::create(
1372 rewriter, loc, rewriter.getI8IntegerAttr(30));
1373 Value shift = arith::AddIOp::create(rewriter, loc, k8, thirty8);
1375 auto roundingAttr = RoundingModeAttr::get(
1376 rewriter.getContext(), RoundingMode::SINGLE_ROUND);
1378 auto scaled = tosa::ApplyScaleOp::create(
1379 rewriter, loc, rewriter.getI32Type(), poolVal,
1380 multiplier, shift, roundingAttr)
1385 if (outputZpVal != 0) {
1386 auto outputZp = arith::ConstantOp::create(
1388 b.getIntegerAttr(scaled.getType(), outputZpVal));
1389 scaled = arith::AddIOp::create(rewriter, loc, scaled, outputZp)
1397 rewriter, loc, accETy,
1398 APInt::getSignedMinValue(outBitwidth).getSExtValue());
1400 rewriter, loc, accETy,
1401 APInt::getSignedMaxValue(outBitwidth).getSExtValue());
1409 arith::TruncIOp::create(rewriter, loc, resultETy, poolVal);
1413 linalg::YieldOp::create(rewriter, loc, poolVal);
1416 rewriter.replaceOp(op, genericOp.getResult(0));
1423 using OpRewritePattern<tosa::TransposeOp>::OpRewritePattern;
1425 LogicalResult matchAndRewrite(tosa::TransposeOp op,
1426 PatternRewriter &rewriter)
const final {
1427 const llvm::ArrayRef<int32_t> constantPerms = op.getPerms();
1429 Location loc = op.getLoc();
1433 SmallVector<OpFoldResult> inputSizes =
1435 auto permutedSizes =
1439 tensor::EmptyOp::create(rewriter, loc, permutedSizes,
1440 op.getInput1().getType().getElementType());
1441 rewriter.replaceOpWithNewOp<linalg::TransposeOp>(
1442 op, op.getInput1(), permutedInit,
1443 llvm::map_to_vector(constantPerms,
1444 [](int32_t v) -> int64_t {
return v; }));
1453 if (
options.preferConv2DKernelLayoutHWCF) {
1454 patterns->
add<ConvConverter<tosa::Conv2DOp, linalg::Conv2DNhwcHwcfOp,
1455 linalg::Conv2DNhwcHwcfQOp>>(
1458 patterns->
add<ConvConverter<tosa::Conv2DOp, linalg::Conv2DNhwcFhwcOp,
1459 linalg::Conv2DNhwcFhwcQOp>>(
1464 ConvConverter<tosa::Conv3DOp, linalg::Conv3DNdhwcDhwcfOp, linalg::Conv3DNdhwcDhwcfQOp>,
1465 DepthwiseConvConverter,
1473 ElementwiseConverter<tosa::AbsOp>,
1474 ElementwiseConverter<tosa::CeilOp>,
1475 ElementwiseConverter<tosa::FloorOp>,
1476 ElementwiseConverter<tosa::ExpOp>,
1477 ElementwiseConverter<tosa::LogOp>,
1478 ElementwiseConverter<tosa::RsqrtOp>,
1479 ElementwiseConverter<tosa::SinOp>,
1480 ElementwiseConverter<tosa::CosOp>,
1481 ElementwiseConverter<tosa::TanhOp>,
1482 ElementwiseConverter<tosa::ErfOp>,
1483 ElementwiseConverter<tosa::ReciprocalOp>,
1484 ElementwiseConverter<tosa::PowOp>,
1485 ElementwiseConverter<tosa::AddOp>,
1486 ElementwiseConverter<tosa::SubOp>,
1487 ElementwiseConverter<tosa::IntDivOp>,
1488 ElementwiseConverter<tosa::MulOp>,
1489 ElementwiseConverter<tosa::NegateOp>,
1490 ElementwiseConverter<tosa::MaximumOp>,
1491 ElementwiseConverter<tosa::MinimumOp>,
1492 ElementwiseConverter<tosa::SelectOp>
memberIdxs push_back(ArrayAttr::get(parser.getContext(), values))
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 AffineMap getBroadcastingMap(PatternRewriter &rewriter, Value source, Value result)
static mlir::Value applyPad(Location loc, Value input, ArrayRef< int64_t > pad, TypedAttr padAttr, OpBuilder &rewriter)
static void createDepthwiseConvCollapseMap(int64_t outputRank, SmallVector< ReassociationExprs, 4 > &reassociationMap, OpBuilder &rewriter)
static mlir::Value linalgIntBroadcastExtSIAdd(PatternRewriter &rewriter, Location loc, Value bias, Value conv, Value result, ArrayRef< AffineMap > indexingMaps)
static mlir::Value getConvOrPoolOutputDim(Location loc, Value inputDim, int64_t padBeforeAttr, int64_t padAfterAttr, Value kernelDim, int64_t strideAttr, int64_t dilationAttr, OpBuilder &rewriter)
static mlir::Value linalgBroadcastAndMaybeExt(PatternRewriter &rewriter, Location loc, Value source, Value result)
static mlir::Value reifyConstantDim(int64_t attr, ImplicitLocOpBuilder &builder)
static SmallVector< Value > inferDynamicDimsForConv(Location loc, Value input, Value weight, ShapedType resultTy, ArrayRef< int64_t > padAttr, ArrayRef< int64_t > strideAttr, ArrayRef< int64_t > dilationAttr, ArrayRef< int64_t > inputSizeDims, ArrayRef< int64_t > kernelSizeDims, OpBuilder &rewriter)
static std::optional< linalg::ElementwiseKind > getElementwiseKind(Operation *op, Type elementTy)
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: () -> ().
IntegerAttr getIndexAttr(int64_t value)
AffineMap getMultiDimIdentityMap(unsigned rank)
AffineExpr getAffineConstantExpr(int64_t constant)
AffineExpr getAffineDimExpr(unsigned position)
MLIRContext * getContext() const
An attribute that represents a reference to a dense vector or tensor object.
auto getValues() const
Return the held element values as a range of the given type.
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.
Operation is the basic unit of execution within MLIR.
OpResult getResult(unsigned idx)
Get the 'idx'th result of this operation.
operand_range getOperands()
Returns an iterator on the underlying Value's.
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.
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 isSignlessInteger() const
Return true if this is a signless integer type (with the specified width).
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).
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.
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)
ArrayRef< T > asArrayRef() const
static BatchMatmulTransposeBOp create(OpBuilder &builder, Location location, ValueRange inputs, ValueRange outputs, ArrayRef< NamedAttribute > attributes={})
ArityGroupAndKind getArityGroupAndKind(ElementwiseKind kind)
SmallVector< OpFoldResult > getMixedSizes(OpBuilder &builder, Location loc, Value value)
Return the dimensions of the given tensor value.
SmallVector< T > applyTOSAPermutation(ArrayRef< T > input, ArrayRef< int32_t > perms)
SmallVector< utils::IteratorType > getNParallelLoopsAttrs(unsigned nParallelLoops)
SmallVector< Value > condenseValues(const SmallVector< Value > &values)
std::optional< SmallVector< Value > > checkHasDynamicBatchDims(PatternRewriter &rewriter, Op op, ArrayRef< Value > params)
MatMulTOperands prepareMatMulOperands(PatternRewriter &rewriter, Location loc, Value a, Value b, RankedTensorType outputType, bool transposeB)
Broadcast the batch dimensions of the ranked operands and create a zero-filled result tensor for eith...
APFloat getFloatMinMaxIdentity(const llvm::fltSemantics &semantics, bool negative, bool allowNonFinites)
Value clampIntHelper(Location loc, Value arg, Value min, Value max, OpBuilder &rewriter, bool isUnsigned)
void populateTosaToLinalgNamedConversionPatterns(const TypeConverter &converter, RewritePatternSet *patterns, const TosaToLinalgNamedOptions &options)
Populates conversion passes from TOSA dialect to Linalg named operations.
std::optional< linalg::ElementwiseKind > isConvertibleToLinalgElementwise(Operation *op)
Returns the linalg.elementwise kind that the TosaToLinalgNamed conversion lowers op to,...
Include the generated interface declarations.
bool matchPattern(Value value, const Pattern &pattern)
Entry point for matching a pattern over a Value.
detail::DenseArrayAttrImpl< int64_t > DenseI64ArrayAttr
Type getElementTypeOrSelf(Type type)
Return the element type or return the type itself.
detail::constant_op_matcher m_Constant()
Matches a constant foldable operation.
OpRewritePattern is a wrapper around RewritePattern that allows for matching and rewriting against an...
ElementwiseArityGroup arityGroup