25#include "llvm/ADT/SmallVectorExtras.h"
35 if (llvm::all_of(pad, [](
int64_t p) {
return p == 0; }))
38 ShapedType inputTy = cast<ShapedType>(input.
getType());
39 Type inputETy = inputTy.getElementType();
40 auto inputShape = inputTy.getShape();
42 assert((inputShape.size() * 2) == pad.size());
47 for (
size_t i : llvm::seq(inputShape.size())) {
48 auto lowPad = pad[i * 2];
49 auto highPad = pad[i * 2 + 1];
50 if (ShapedType::isDynamic(inputShape[i]))
51 paddedShape.push_back(inputShape[i]);
53 paddedShape.push_back(inputShape[i] + highPad + lowPad);
58 Value padValue = arith::ConstantOp::create(rewriter, loc, padAttr);
60 return tensor::PadOp::create(rewriter, loc,
61 RankedTensorType::get(paddedShape, inputETy),
62 input, lowIndices, highIndices, padValue);
69 ShapedType resultTy = cast<ShapedType>(conv.
getType());
70 return linalg::GenericOp::create(
74 Value biasVal = args[0];
75 Type resType = args[1].getType();
76 if (resType != biasVal.
getType()) {
78 arith::ExtSIOp::create(builder, loc, resType, biasVal);
81 arith::AddIOp::create(builder, loc, biasVal, args[1]);
82 linalg::YieldOp::create(builder, loc, added);
91 ShapedType resultTy = cast<ShapedType>(
result.getType());
92 ShapedType sourceTy = cast<ShapedType>(source.
getType());
93 const int64_t resultRank = resultTy.getRank();
94 const int64_t sourceRank = sourceTy.getRank();
102 assert(sourceTy.hasStaticShape() &&
103 "Dynamic broadcasting shapes not supported!");
104 if (sourceRank == 1 && sourceTy.getDimSize(0) == 1) {
107 for (
auto dim : llvm::seq<int64_t>(0, sourceRank)) {
109 sourceDims.push_back(expr);
123 ShapedType resultTy = cast<ShapedType>(
result.getType());
124 const int64_t resultRank = resultTy.getRank();
131 return linalg::GenericOp::create(
135 Value biasVal = args[0];
136 Type resType = args[1].getType();
137 if (resType != biasVal.
getType()) {
139 resultTy.getElementType().isFloat()
140 ? arith::ExtFOp::create(
142 ValueRange{biasVal}, arith::ExtFOp::Properties{})
144 : arith::ExtSIOp::create(builder, loc, resType,
148 linalg::YieldOp::create(builder, loc, biasVal);
169 auto one = arith::ConstantOp::create(rewriter, loc,
170 IntegerAttr::get(inputDim.
getType(), 1));
172 Value paddedBefore = arith::AddIOp::create(builder, inputDim, padBefore);
174 Value paddedAfter = arith::AddIOp::create(builder, paddedBefore, padAfter);
176 Value subOne = arith::SubIOp::create(builder, kernelDim, one);
178 Value dilated = arith::MulIOp::create(builder, dilation, subOne);
179 Value addOne = arith::AddIOp::create(builder, dilated, one);
181 Value subtract = arith::SubIOp::create(builder, paddedAfter, addOne);
183 Value divide = arith::DivUIOp::create(builder, subtract, stride);
184 return arith::AddIOp::create(builder, divide, one);
193 ShapedType inputTy = cast<ShapedType>(input.
getType());
194 int64_t inputRank = inputTy.getRank();
197 dynDims.resize(resultTy.getRank());
199 for (uint32_t i = 0, s = inputSizeDims.size(); i < s; ++i) {
200 int64_t inputDim = inputSizeDims[i];
201 int64_t kernelDim = kernelSizeDims[i];
202 if (resultTy.isDynamicDim(inputDim)) {
203 auto padTop = padAttr[i * 2];
204 auto padBottom = padAttr[i * 2 + 1];
205 auto stride = strideAttr[i];
206 auto dilation = dilationAttr[i];
207 Value initDynDim = tensor::DimOp::create(rewriter, loc, input, inputDim);
209 tensor::DimOp::create(rewriter, loc, weight, kernelDim);
213 kernelDynDim, stride, dilation, rewriter);
218 for (
int i = 0; i < inputRank; i++) {
219 if (resultTy.isDynamicDim(i) && !dynDims[i])
220 dynDims[i] = tensor::DimOp::create(rewriter, loc, input, i);
232 reassociationMap.resize(outputRank);
233 for (
int i = 0; i < outputRank; i++) {
236 reassociationMap[outputRank - 1].push_back(
242template <
typename TosaConvOp,
typename LinalgConvOp,
typename LinalgConvQOp>
243class ConvConverter :
public OpConversionPattern<TosaConvOp> {
245 using OpConversionPattern<TosaConvOp>::OpConversionPattern;
247 matchAndRewrite(TosaConvOp op,
typename TosaConvOp::Adaptor adaptor,
248 ConversionPatternRewriter &rewriter)
const final {
249 Location loc = op->getLoc();
250 Value input = op->getOperand(0);
251 Value weight = op->getOperand(1);
252 Value bias = op->getOperand(2);
254 ShapedType inputTy = cast<ShapedType>(input.
getType());
255 ShapedType weightTy = cast<ShapedType>(weight.
getType());
256 ShapedType biasTy = cast<ShapedType>(bias.
getType());
257 ShapedType resultTy = cast<ShapedType>(op->getResult(0).getType());
259 Type inputETy = inputTy.getElementType();
265 Type accETy = op.getAccType();
266 Type accTy = RankedTensorType::get(resultTy.getShape(), accETy);
269 FailureOr<int64_t> maybeIZp = op.getInputZeroPoint();
271 return rewriter.notifyMatchFailure(
272 op,
"input zero point cannot be statically determined");
274 FailureOr<int64_t> maybeWZp = op.getWeightZeroPoint();
276 return rewriter.notifyMatchFailure(
277 op,
"weight zero point cannot be statically determined");
279 const int64_t inputZpVal = *maybeIZp;
280 const int64_t weightZpVal = *maybeWZp;
282 if (op.verifyInputZeroPoint(inputZpVal).failed())
283 return rewriter.notifyMatchFailure(
284 op,
"input zero point must be zero for non-int8 integer types");
286 if (op.verifyWeightZeroPoint(weightZpVal).failed())
287 return rewriter.notifyMatchFailure(
288 op,
"weight zero point must be zero for non-int8 integer types");
290 bool hasZp = (inputZpVal != 0) || (weightZpVal != 0);
292 if (!weightTy.hasStaticShape() || !biasTy.hasStaticShape())
293 return rewriter.notifyMatchFailure(
294 op,
"tosa.conv ops require static shapes for weight and bias");
297 return rewriter.notifyMatchFailure(
298 op,
"tosa.conv ops does not support unsigned integer input");
300 llvm::SmallVector<int64_t> inputSizeDims;
301 llvm::SmallVector<int64_t> kernelSizeDims;
302 for (
int i = 1; i < resultTy.getRank() - 1; i++) {
303 inputSizeDims.push_back(i);
304 kernelSizeDims.push_back(i);
308 loc, input, weight, resultTy, padAttr.
asArrayRef(),
310 inputSizeDims, kernelSizeDims, rewriter);
312 auto weightShape = weightTy.getShape();
315 TypedAttr zeroAttr = rewriter.getZeroAttr(inputETy);
324 if (inputZpVal < intMin || inputZpVal > intMax)
325 return rewriter.notifyMatchFailure(
326 op,
"tosa.conv op quantization has zp outside of input range");
328 zeroAttr = rewriter.getIntegerAttr(inputETy, inputZpVal);
331 llvm::SmallVector<int64_t> pad;
333 llvm::append_range(pad, padAttr.
asArrayRef());
334 pad.resize(pad.size() + 2, 0);
335 input =
applyPad(loc, input, pad, zeroAttr, rewriter);
337 if (4 == inputTy.getRank()) {
341 hasZp ? std::is_same_v<LinalgConvQOp, linalg::Conv2DNhwcHwcfQOp>
342 : std::is_same_v<LinalgConvOp, linalg::Conv2DNhwcHwcfOp>;
348 SmallVector<int32_t> weightPerm;
349 for (
int i = 1; i < resultTy.getRank(); i++)
350 weightPerm.push_back(i);
351 weightPerm.push_back(0);
353 SmallVector<int64_t> newWeightShape;
354 for (
auto dim : weightPerm)
355 newWeightShape.push_back(weightShape[dim]);
356 auto weightPermAttr = rewriter.getDenseI32ArrayAttr(weightPerm);
358 RankedTensorType::get(newWeightShape, weightTy.getElementType());
359 weight = tosa::TransposeOp::create(rewriter, loc, newWeightTy, weight,
367 if (5 == inputTy.getRank()) {
370 SmallVector<int32_t> weightPerm;
371 for (
int i = 1; i < resultTy.getRank(); i++)
372 weightPerm.push_back(i);
373 weightPerm.push_back(0);
375 SmallVector<int64_t> newWeightShape;
376 for (
auto dim : weightPerm)
377 newWeightShape.push_back(weightShape[dim]);
378 auto weightPermAttr = rewriter.getDenseI32ArrayAttr(weightPerm);
380 RankedTensorType::get(newWeightShape, weightTy.getElementType());
381 weight = tosa::TransposeOp::create(rewriter, loc, newWeightTy, weight,
386 ArrayRef<int64_t> stride = strideTosaAttr;
387 ArrayRef<int64_t> dilation = dilationTosaAttr;
390 auto strideAttr = rewriter.getI64TensorAttr(stride);
391 auto dilationAttr = rewriter.getI64TensorAttr(dilation);
393 Value biasEmptyTensor = tensor::EmptyOp::create(
394 rewriter, loc, resultTy.getShape(), accETy, filteredDims);
396 Value broadcastBias =
400 auto iZp = rewriter.getI32IntegerAttr(inputZpVal);
401 auto kZp = rewriter.getI32IntegerAttr(weightZpVal);
403 auto iZpVal = arith::ConstantOp::create(rewriter, loc, iZp);
404 auto kZpVal = arith::ConstantOp::create(rewriter, loc, kZp);
406 Value conv = LinalgConvQOp::create(
407 rewriter, loc, resultTy,
409 ValueRange{broadcastBias}, strideAttr, dilationAttr)
412 rewriter.replaceOp(op, conv);
416 Value conv = LinalgConvOp::create(
417 rewriter, loc, accTy,
ValueRange{input, weight},
418 ValueRange{broadcastBias}, strideAttr, dilationAttr)
423 if (resultTy != accTy)
424 conv = tosa::CastOp::create(rewriter, loc, resultTy, conv);
426 rewriter.replaceOp(op, conv);
431class DepthwiseConvConverter
432 :
public OpConversionPattern<tosa::DepthwiseConv2DOp> {
434 using OpConversionPattern<tosa::DepthwiseConv2DOp>::OpConversionPattern;
436 matchAndRewrite(tosa::DepthwiseConv2DOp op, OpAdaptor adaptor,
437 ConversionPatternRewriter &rewriter)
const final {
438 Location loc = op->getLoc();
439 Value input = op->getOperand(0);
440 Value weight = op->getOperand(1);
441 Value bias = op->getOperand(2);
443 ShapedType inputTy = cast<ShapedType>(input.
getType());
444 ShapedType weightTy = cast<ShapedType>(weight.
getType());
445 ShapedType biasTy = cast<ShapedType>(bias.
getType());
446 ShapedType resultTy = cast<ShapedType>(op->getResult(0).getType());
447 int64_t resultRank = resultTy.getRank();
449 Type inputETy = inputTy.getElementType();
450 Type resultETy = resultTy.getElementType();
452 auto padAttr = op.getPadAttr();
453 auto strideTosaAttr = op.getStrideAttr();
454 auto dilationTosaAttr = op.getDilationAttr();
456 Type accETy = op.getAccType();
458 if (!weightTy.hasStaticShape() || !biasTy.hasStaticShape())
459 return rewriter.notifyMatchFailure(
460 op,
"tosa.depthwise_conv ops require static shapes");
464 loc, input, weight, resultTy, padAttr.
asArrayRef(),
471 FailureOr<int64_t> maybeIZp = op.getInputZeroPoint();
472 FailureOr<int64_t> maybeWZp = op.getWeightZeroPoint();
474 return rewriter.notifyMatchFailure(
475 op,
"input zero point cannot be statically determined");
477 return rewriter.notifyMatchFailure(
478 op,
"weight zero point cannot be statically determined");
480 const int64_t inputZpVal = *maybeIZp;
481 const int64_t weightZpVal = *maybeWZp;
483 if (op.verifyInputZeroPoint(inputZpVal).failed())
484 return rewriter.notifyMatchFailure(
485 op,
"input zero point must be zero for non-int8 integer types");
487 if (op.verifyWeightZeroPoint(weightZpVal).failed())
488 return rewriter.notifyMatchFailure(
489 op,
"weight zero point must be zero for non-int8 integer types");
491 bool hasNullZps = (inputZpVal == 0) && (weightZpVal == 0);
492 auto weightShape = weightTy.getShape();
493 auto resultShape = resultTy.getShape();
496 TypedAttr zeroAttr = rewriter.getZeroAttr(inputETy);
505 if (inputZpVal < intMin || inputZpVal > intMax)
506 return rewriter.notifyMatchFailure(
507 op,
"tosa.depthwise_conv op quantization has zp outside of input "
510 zeroAttr = rewriter.getIntegerAttr(inputETy, inputZpVal);
513 llvm::SmallVector<int64_t> pad;
515 llvm::append_range(pad, padAttr.
asArrayRef());
516 pad.resize(pad.size() + 2, 0);
518 input =
applyPad(loc, input, pad, zeroAttr, rewriter);
521 ArrayRef<int64_t> stride = strideTosaAttr;
522 ArrayRef<int64_t> dilation = dilationTosaAttr;
525 auto strideAttr = rewriter.getI64TensorAttr(stride);
526 auto dilationAttr = rewriter.getI64TensorAttr(dilation);
527 ShapedType linalgConvTy =
528 RankedTensorType::get({resultShape[0], resultShape[1], resultShape[2],
529 weightShape[2], weightShape[3]},
532 auto resultZeroAttr = rewriter.getZeroAttr(accETy);
533 Value emptyTensor = tensor::EmptyOp::create(
534 rewriter, loc, linalgConvTy.getShape(), accETy, filteredDims);
535 Value zero = arith::ConstantOp::create(rewriter, loc, resultZeroAttr);
536 Value zeroTensor = linalg::FillOp::create(rewriter, loc,
ValueRange{zero},
540 Value biasEmptyTensor = tensor::EmptyOp::create(
541 rewriter, loc, resultTy.getShape(), resultETy, filteredDims);
544 SmallVector<AffineMap, 4> indexingMaps;
546 indexingMaps.push_back(rewriter.getMultiDimIdentityMap(resultRank));
547 indexingMaps.push_back(rewriter.getMultiDimIdentityMap(resultRank));
550 Value conv = linalg::DepthwiseConv2DNhwcHwcmOp::create(
551 rewriter, loc, linalgConvTy,
ValueRange{input, weight},
552 ValueRange{zeroTensor}, strideAttr, dilationAttr)
557 if (accETy != resultETy)
558 conv = tosa::CastOp::create(
560 RankedTensorType::get(cast<ShapedType>(conv.
getType()).getShape(),
564 SmallVector<ReassociationExprs, 4> reassociationMap;
566 Value convReshape = tensor::CollapseShapeOp::create(
567 rewriter, loc, resultTy, conv, reassociationMap);
570 linalg::GenericOp::create(
571 rewriter, loc, resultTy,
ValueRange({bias, convReshape}),
573 [&](OpBuilder &nestedBuilder, Location nestedLoc,
576 if (llvm::isa<FloatType>(inputETy))
577 added = arith::AddFOp::create(nestedBuilder, loc, args[0],
580 added = arith::AddIOp::create(nestedBuilder, loc, args[0],
582 linalg::YieldOp::create(nestedBuilder, nestedLoc, added);
585 rewriter.replaceOp(op,
result);
587 IntegerAttr iZp = rewriter.getI32IntegerAttr(inputZpVal);
588 IntegerAttr wZp = rewriter.getI32IntegerAttr(weightZpVal);
589 auto iZpVal = arith::ConstantOp::create(rewriter, loc, iZp);
590 auto kZpVal = arith::ConstantOp::create(rewriter, loc, wZp);
591 Value conv = linalg::DepthwiseConv2DNhwcHwcmQOp::create(
592 rewriter, loc, linalgConvTy,
594 ValueRange{zeroTensor}, strideAttr, dilationAttr)
596 SmallVector<ReassociationExprs, 4> reassociationMap;
598 Value convReshape = tensor::CollapseShapeOp::create(
599 rewriter, loc, resultTy, conv, reassociationMap);
601 rewriter, loc, bias, convReshape, biasEmptyTensor, indexingMaps);
602 rewriter.replaceOp(op,
result);
608class MatMulConverter :
public OpConversionPattern<tosa::MatMulOp> {
610 using OpConversionPattern<tosa::MatMulOp>::OpConversionPattern;
612 matchAndRewrite(tosa::MatMulOp op, OpAdaptor adaptor,
613 ConversionPatternRewriter &rewriter)
const final {
614 Location loc = op.getLoc();
616 auto outputTy = cast<ShapedType>(op.getType());
617 auto outputElementTy = outputTy.getElementType();
619 SmallVector<Value> dynDims;
620 dynDims.resize(cast<ShapedType>(op->getResult(0).getType()).getRank());
622 if (!outputTy.hasRank() || outputTy.isDynamicDim(0)) {
623 dynDims[0] = tensor::DimOp::create(rewriter, loc, op->getOperand(0), 0);
626 if (!outputTy.hasRank() || outputTy.isDynamicDim(1)) {
627 dynDims[1] = tensor::DimOp::create(rewriter, loc, op->getOperand(0), 1);
630 if (!outputTy.hasRank() || outputTy.isDynamicDim(2)) {
631 dynDims[2] = tensor::DimOp::create(rewriter, loc, op->getOperand(1), 2);
636 auto zeroAttr = rewriter.getZeroAttr(outputElementTy);
637 Value zero = arith::ConstantOp::create(rewriter, loc, zeroAttr);
639 tensor::EmptyOp::create(rewriter, loc, outputTy.getShape(),
640 outputTy.getElementType(), filteredDims);
641 Value zeroTensor = linalg::FillOp::create(rewriter, loc,
ValueRange{zero},
645 FailureOr<int64_t> maybeAZp = op.getAZeroPoint();
646 FailureOr<int64_t> maybeBZp = op.getBZeroPoint();
648 return rewriter.notifyMatchFailure(
649 op,
"input a zero point cannot be statically determined");
651 return rewriter.notifyMatchFailure(
652 op,
"input b zero point cannot be statically determined");
654 const int64_t aZpVal = *maybeAZp;
655 const int64_t bZpVal = *maybeBZp;
657 if (op.verifyAZeroPoint(aZpVal).failed())
658 return rewriter.notifyMatchFailure(
659 op,
"input a zero point must be zero for non-int8 integer types");
661 if (op.verifyBZeroPoint(bZpVal).failed())
662 return rewriter.notifyMatchFailure(
663 op,
"input b zero point must be zero for non-int8 integer types");
665 if (aZpVal == 0 && bZpVal == 0) {
666 rewriter.replaceOpWithNewOp<linalg::BatchMatmulOp>(
672 auto aZp = arith::ConstantOp::create(rewriter, loc,
673 rewriter.getI32IntegerAttr(aZpVal));
674 auto bZp = arith::ConstantOp::create(rewriter, loc,
675 rewriter.getI32IntegerAttr(bZpVal));
676 rewriter.replaceOpWithNewOp<linalg::QuantizedBatchMatmulOp>(
678 ValueRange{adaptor.getA(), adaptor.getB(), aZp, bZp}, zeroTensor);
685getMatMulTBatchCollapseReassociation(
int64_t rank) {
687 for (
int64_t dim = 0; dim < rank - 2; ++dim)
689 reassociation[1].push_back(rank - 2);
690 reassociation[2].push_back(rank - 1);
691 return reassociation;
694class MatMulTConverter :
public OpConversionPattern<tosa::MatMulTOp> {
696 using OpConversionPattern<tosa::MatMulTOp>::OpConversionPattern;
698 matchAndRewrite(tosa::MatMulTOp op, OpAdaptor adaptor,
699 ConversionPatternRewriter &rewriter)
const final {
700 Location loc = op.getLoc();
701 auto aType = dyn_cast<RankedTensorType>(adaptor.getA().getType());
702 auto bType = dyn_cast<RankedTensorType>(adaptor.getB().getType());
703 auto outputType = dyn_cast<RankedTensorType>(op.getType());
704 if (!aType || !bType || !outputType)
705 return rewriter.notifyMatchFailure(op,
"only supports ranked tensors");
707 FailureOr<int64_t> maybeAZp = op.getAZeroPoint();
708 FailureOr<int64_t> maybeBZp = op.getBZeroPoint();
710 return rewriter.notifyMatchFailure(
711 op,
"input A zero point cannot be statically determined");
713 return rewriter.notifyMatchFailure(
714 op,
"input B zero point cannot be statically determined");
716 const int64_t aZpVal = *maybeAZp;
717 const int64_t bZpVal = *maybeBZp;
719 if (aZpVal != 0 || bZpVal != 0)
720 return rewriter.notifyMatchFailure(
721 op,
"nonzero zero points require the generic MATMUL_T lowering");
723 const int64_t outputRank = outputType.getRank();
725 rewriter, loc, adaptor.getA(), adaptor.getB(), outputType);
727 if (outputRank == 2) {
728 rewriter.replaceOpWithNewOp<linalg::MatmulTransposeBOp>(
733 if (outputRank == 3) {
734 rewriter.replaceOpWithNewOp<linalg::BatchMatmulTransposeBOp>(
739 SmallVector<ReassociationIndices> reassociation =
740 getMatMulTBatchCollapseReassociation(outputRank);
741 auto collapse = [&](Value value) {
742 auto type = cast<RankedTensorType>(value.getType());
744 tensor::CollapseShapeOp::inferCollapsedType(type, reassociation);
745 return tensor::CollapseShapeOp::create(rewriter, loc, collapsedType,
746 value, reassociation);
748 Value collapsedA = collapse(a);
749 Value collapsedB = collapse(
b);
750 Value collapsedInit = collapse(zeroTensor);
751 auto collapsedOutputType = cast<RankedTensorType>(collapsedInit.
getType());
752 Value collapsedResult =
754 rewriter, loc,
TypeRange{collapsedOutputType},
757 Value
result = tensor::ExpandShapeOp::create(
758 rewriter, loc, outputType, collapsedResult, reassociation,
760 rewriter.replaceOp(op,
result);
765class MaxPool2dConverter :
public OpConversionPattern<tosa::MaxPool2dOp> {
767 MaxPool2dConverter(
const TypeConverter &typeConverter, MLIRContext *context,
768 bool allowNonFinites)
769 : OpConversionPattern(typeConverter, context),
770 allowNonFinites(allowNonFinites) {}
773 static SmallVector<Value>
774 computeDynamicOutputSizes(tosa::MaxPool2dOp op, OpAdaptor adaptor,
775 ConversionPatternRewriter &rewriter) {
776 TensorType resultTy = op.getType();
777 Location loc = op.getLoc();
779 Value input = adaptor.getInput();
780 ArrayRef<int64_t> kernel = op.getKernel();
781 ArrayRef<int64_t> pad = op.getPad();
782 ArrayRef<int64_t> stride = op.getStride();
784 SmallVector<Value> dynamicDims;
787 if (resultTy.isDynamicDim(0))
788 dynamicDims.push_back(tensor::DimOp::create(rewriter, loc, input, 0));
791 for (int64_t dim : {1, 2}) {
792 if (!resultTy.isDynamicDim(dim))
796 int64_t index = dim - 1;
799 Value ihw = tensor::DimOp::create(rewriter, loc, input, dim);
806 pad[index * 2 + 1], khw, stride[index],
808 dynamicDims.push_back(ohw);
812 if (resultTy.isDynamicDim(3))
813 dynamicDims.push_back(tensor::DimOp::create(rewriter, loc, input, 3));
819 matchAndRewrite(tosa::MaxPool2dOp op, OpAdaptor adaptor,
820 ConversionPatternRewriter &rewriter)
const final {
821 Location loc = op.getLoc();
822 Value input = adaptor.getInput();
823 ShapedType inputTy = cast<ShapedType>(input.
getType());
825 bool isUnsigned = op.getType().getElementType().isUnsignedInteger();
826 ShapedType resultTy =
827 getTypeConverter()->convertType<ShapedType>(op.getType());
829 return rewriter.notifyMatchFailure(op,
"failed to convert type");
830 Type resultETy = inputTy.getElementType();
831 NanPropagationMode nanMode = op.getNanMode();
833 SmallVector<Value> dynamicDims =
834 computeDynamicOutputSizes(op, adaptor, rewriter);
837 TypedAttr initialAttr;
839 const llvm::fltSemantics &semantics =
840 cast<FloatType>(resultETy).getFloatSemantics();
841 if (llvm::APFloat::semanticsHasNaN(semantics))
842 initialAttr = rewriter.getFloatAttr(
844 nanMode == NanPropagationMode::IGNORE
845 ? APFloat::getNaN(semantics)
847 true, allowNonFinites));
851 initialAttr = rewriter.getIntegerAttr(
853 else if (isa<IntegerType>(resultETy))
854 initialAttr = rewriter.getIntegerAttr(
859 return rewriter.notifyMatchFailure(
860 op,
"Unsupported initial value for tosa.maxpool_2d op");
863 llvm::SmallVector<int64_t> pad;
865 llvm::append_range(pad, op.getPad());
866 pad.resize(pad.size() + 2, 0);
871 Value paddedInput =
applyPad(loc, input, pad, initialAttr, rewriter);
873 Value initialValue = arith::ConstantOp::create(rewriter, loc, initialAttr);
875 ArrayRef<int64_t> kernel = op.getKernel();
876 ArrayRef<int64_t> stride = op.getStride();
878 Attribute strideAttr = rewriter.getI64VectorAttr(stride);
879 Attribute dilationAttr = rewriter.getI64VectorAttr({1, 1});
883 tensor::EmptyOp::create(rewriter, loc, resultTy.getShape(),
884 resultTy.getElementType(), dynamicDims);
886 Value filledEmptyTensor =
887 linalg::FillOp::create(rewriter, loc, initialValue, emptyTensor)
890 Value fakeWindowDims =
891 tensor::EmptyOp::create(rewriter, loc, kernel, resultETy);
894 rewriter.replaceOpWithNewOp<linalg::PoolingNhwcMaxUnsignedOp>(
895 op, ArrayRef<Type>{resultTy},
ValueRange{paddedInput, fakeWindowDims},
896 filledEmptyTensor, strideAttr, dilationAttr);
897 return llvm::success();
900 auto resultOp = linalg::PoolingNhwcMaxOp::create(
901 rewriter, op->getLoc(), ArrayRef<Type>{resultTy},
902 ValueRange{paddedInput, fakeWindowDims}, filledEmptyTensor, strideAttr,
905 rewriter.replaceOp(op, resultOp);
919 if (nanMode == NanPropagationMode::IGNORE) {
920 auto genericOp = linalg::GenericOp::create(
921 rewriter, loc, resultOp.getType(0), resultOp.getInputs(),
922 resultOp.getOutputs(), resultOp.getIndexingMapsArray(),
923 resultOp.getIteratorTypesArray(),
924 [&](OpBuilder &opBuilder, Location loc,
ValueRange blockArgs) {
926 auto oldBlock = resultOp.getRegion().begin();
927 auto oldArgs = oldBlock->getArguments();
928 auto &oldMaxOp = *resultOp.getBlock()->begin();
929 map.map(oldArgs, blockArgs);
930 auto *newOp = opBuilder.clone(oldMaxOp, map);
932 arith::CmpFOp::create(opBuilder, loc, arith::CmpFPredicate::UNO,
933 blockArgs.front(), blockArgs.front());
934 Value accumulatorIsNaN =
935 arith::CmpFOp::create(opBuilder, loc, arith::CmpFPredicate::UNO,
936 blockArgs.back(), blockArgs.back());
937 Value finiteMaximum =
938 arith::SelectOp::create(opBuilder, loc, accumulatorIsNaN,
939 blockArgs.front(), newOp->getResult(0));
940 Value result = arith::SelectOp::create(
941 opBuilder, loc, inputIsNaN, blockArgs.back(), finiteMaximum);
942 linalg::YieldOp::create(opBuilder, loc, result);
944 rewriter.replaceOp(resultOp, genericOp);
951 bool allowNonFinites;
956 using OpRewritePattern<tosa::AvgPool2dOp>::OpRewritePattern;
958 LogicalResult matchAndRewrite(tosa::AvgPool2dOp op,
959 PatternRewriter &rewriter)
const final {
960 Location loc = op.getLoc();
961 Value input = op.getInput();
962 ShapedType inputTy = cast<ShapedType>(input.
getType());
963 Type inElementTy = inputTy.getElementType();
965 ShapedType resultTy = cast<ShapedType>(op.getType());
966 Type resultETy = cast<ShapedType>(op.getType()).getElementType();
968 Type accETy = op.getAccType();
969 ShapedType accTy = resultTy.clone(accETy);
973 if (!dynamicDimsOr.has_value())
975 SmallVector<Value> dynamicDims = *dynamicDimsOr;
977 FailureOr<int64_t> maybeIZp = op.getInputZeroPoint();
978 FailureOr<int64_t> maybeOZp = op.getOutputZeroPoint();
980 return rewriter.notifyMatchFailure(
981 op,
"input zero point could not be statically determined");
983 return rewriter.notifyMatchFailure(
984 op,
"output zero point could not be statically determined");
986 const int64_t inputZpVal = *maybeIZp;
987 const int64_t outputZpVal = *maybeOZp;
990 llvm::SmallVector<int64_t> pad;
992 llvm::append_range(pad, op.getPad());
993 pad.resize(pad.size() + 2, 0);
994 TypedAttr padAttr = rewriter.getZeroAttr(inElementTy);
998 Value paddedInput =
applyPad(loc, input, pad, padAttr, rewriter);
1000 auto initialAttr = rewriter.getZeroAttr(accETy);
1001 Value initialValue = arith::ConstantOp::create(rewriter, loc, initialAttr);
1003 ArrayRef<int64_t> kernel = op.getKernel();
1004 ArrayRef<int64_t> stride = op.getStride();
1006 Attribute strideAttr = rewriter.getI64VectorAttr(stride);
1007 Attribute dilationAttr = rewriter.getI64VectorAttr({1, 1});
1010 Value poolEmptyTensor = tensor::EmptyOp::create(
1011 rewriter, loc, accTy.getShape(), accETy, dynamicDims);
1013 Value filledEmptyTensor =
1014 linalg::FillOp::create(rewriter, loc,
ValueRange{initialValue},
1018 Value fakeWindowDims =
1019 tensor::EmptyOp::create(rewriter, loc, kernel, accETy);
1022 Value poolingOp = linalg::PoolingNhwcSumOp::create(
1023 rewriter, loc, ArrayRef<Type>{accTy},
1025 filledEmptyTensor, strideAttr, dilationAttr)
1030 Value iH = tensor::DimOp::create(rewriter, loc, poolingOp, 1);
1031 Value iW = tensor::DimOp::create(rewriter, loc, poolingOp, 2);
1034 iH = arith::SubIOp::create(rewriter, loc, iH, one);
1035 iW = arith::SubIOp::create(rewriter, loc, iW, one);
1037 Value genericEmptyTensor = tensor::EmptyOp::create(
1038 rewriter, loc, resultTy.getShape(), resultETy, dynamicDims);
1040 auto affineMap = rewriter.getMultiDimIdentityMap(resultTy.getRank());
1041 auto genericOp = linalg::GenericOp::create(
1042 rewriter, loc, ArrayRef<Type>({resultTy}),
ValueRange{poolingOp},
1044 ArrayRef<AffineMap>({affineMap, affineMap}),
1046 [&](OpBuilder &
b, Location loc,
ValueRange args) {
1051 auto padFn = [&](Value valid, Value pos, int64_t pad) -> Value {
1056 Value dpos = arith::SubIOp::create(rewriter, loc, pos, padVal);
1058 Value offset = arith::MinSIOp::create(rewriter, loc, dpos, zero);
1059 return arith::AddIOp::create(rewriter, loc, valid, offset)
1063 auto coverageFn = [&](int64_t i, Value isize) -> Value {
1070 Value left = linalg::IndexOp::create(rewriter, loc, i);
1071 Value right = arith::SubIOp::create(rewriter, loc, isize, left);
1072 left = arith::MulIOp::create(rewriter, loc, left, strideVal);
1073 right = arith::MulIOp::create(rewriter, loc, right, strideVal);
1076 val = padFn(val, left, pad[i * 2]);
1077 val = padFn(val, right, pad[i * 2 + 1]);
1078 return arith::MaxSIOp::create(rewriter, loc, one, val);
1082 Value kH3 = coverageFn(1, iH);
1083 Value kW3 = coverageFn(2, iW);
1086 auto count = arith::IndexCastOp::create(
1087 rewriter, loc, rewriter.getI32Type(),
1088 arith::MulIOp::create(rewriter, loc, kH3, kW3));
1093 Value poolVal = args[0];
1094 if (isa<FloatType>(accETy)) {
1095 auto countF = arith::SIToFPOp::create(rewriter, loc, accETy, count);
1096 poolVal = arith::DivFOp::create(rewriter, loc, poolVal, countF)
1101 arith::TruncFOp::create(rewriter, loc, resultETy, poolVal);
1106 if (inputZpVal != 0) {
1107 auto inputZp = arith::ConstantOp::create(
1108 rewriter, loc,
b.getIntegerAttr(accETy, inputZpVal));
1110 arith::MulIOp::create(rewriter, loc, accETy, count, inputZp);
1112 arith::SubIOp::create(rewriter, loc, accETy, poolVal, offset);
1116 Value one32 = arith::ConstantOp::create(
1117 rewriter, loc, rewriter.getI32IntegerAttr(1));
1118 Value thirtyTwo32 = arith::ConstantOp::create(
1119 rewriter, loc, rewriter.getI32IntegerAttr(32));
1122 arith::SubIOp::create(rewriter, loc, count, one32);
1123 Value leadingZeros =
1124 math::CountLeadingZerosOp::create(rewriter, loc, countSubOne);
1126 arith::SubIOp::create(rewriter, loc, thirtyTwo32, leadingZeros);
1130 arith::ExtUIOp::create(rewriter, loc, rewriter.getI64Type(), k);
1131 Value thirtyShiftPlusOne = arith::ConstantOp::create(
1132 rewriter, loc, rewriter.getI64IntegerAttr((1 << 30) + 1));
1134 arith::ShLIOp::create(rewriter, loc, thirtyShiftPlusOne, k64);
1137 Value count64 = arith::ExtUIOp::create(
1138 rewriter, loc, rewriter.getI64Type(), count);
1140 arith::DivUIOp::create(rewriter, loc, numerator, count64);
1141 multiplier = arith::TruncIOp::create(
1142 rewriter, loc, rewriter.getI32Type(), multiplier);
1146 arith::TruncIOp::create(rewriter, loc, rewriter.getI8Type(), k);
1147 Value thirty8 = arith::ConstantOp::create(
1148 rewriter, loc, rewriter.getI8IntegerAttr(30));
1149 Value shift = arith::AddIOp::create(rewriter, loc, k8, thirty8);
1151 auto roundingAttr = RoundingModeAttr::get(
1152 rewriter.getContext(), RoundingMode::SINGLE_ROUND);
1154 auto scaled = tosa::ApplyScaleOp::create(
1155 rewriter, loc, rewriter.getI32Type(), poolVal,
1156 multiplier, shift, roundingAttr)
1161 if (outputZpVal != 0) {
1162 auto outputZp = arith::ConstantOp::create(
1164 b.getIntegerAttr(scaled.getType(), outputZpVal));
1165 scaled = arith::AddIOp::create(rewriter, loc, scaled, outputZp)
1173 rewriter, loc, accETy,
1174 APInt::getSignedMinValue(outBitwidth).getSExtValue());
1176 rewriter, loc, accETy,
1177 APInt::getSignedMaxValue(outBitwidth).getSExtValue());
1185 arith::TruncIOp::create(rewriter, loc, resultETy, poolVal);
1189 linalg::YieldOp::create(rewriter, loc, poolVal);
1192 rewriter.replaceOp(op, genericOp.getResult(0));
1199 using OpRewritePattern<tosa::TransposeOp>::OpRewritePattern;
1201 LogicalResult matchAndRewrite(tosa::TransposeOp op,
1202 PatternRewriter &rewriter)
const final {
1203 const llvm::ArrayRef<int32_t> constantPerms = op.getPerms();
1205 Location loc = op.getLoc();
1209 SmallVector<OpFoldResult> inputSizes =
1211 auto permutedSizes =
1215 tensor::EmptyOp::create(rewriter, loc, permutedSizes,
1216 op.getInput1().getType().getElementType());
1217 rewriter.replaceOpWithNewOp<linalg::TransposeOp>(
1218 op, op.getInput1(), permutedInit,
1219 llvm::map_to_vector(constantPerms,
1220 [](int32_t v) -> int64_t {
return v; }));
1229 if (
options.preferConv2DKernelLayoutHWCF) {
1230 patterns->
add<ConvConverter<tosa::Conv2DOp, linalg::Conv2DNhwcHwcfOp,
1231 linalg::Conv2DNhwcHwcfQOp>>(
1234 patterns->
add<ConvConverter<tosa::Conv2DOp, linalg::Conv2DNhwcFhwcOp,
1235 linalg::Conv2DNhwcFhwcQOp>>(
1240 ConvConverter<tosa::Conv3DOp, linalg::Conv3DNdhwcDhwcfOp, linalg::Conv3DNdhwcDhwcfQOp>,
1241 DepthwiseConvConverter,
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)
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
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.
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 isUnsignedInteger() const
Return true if this is an unsigned 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={})
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)
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...
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.
Include the generated interface declarations.
detail::DenseArrayAttrImpl< int64_t > DenseI64ArrayAttr
Type getElementTypeOrSelf(Type type)
Return the element type or return the type itself.
OpRewritePattern is a wrapper around RewritePattern that allows for matching and rewriting against an...