29#include "llvm/ADT/APFloat.h"
30#include "llvm/ADT/SmallVectorExtras.h"
38#include "mlir/Dialect/Tosa/IR/TosaOpsDialect.cpp.inc"
45#include "mlir/Dialect/Tosa/IR/TosaDialectBytecode.cpp.inc"
46#include "mlir/Dialect/Tosa/IR/TosaEnums.cpp.inc"
47#include "mlir/Dialect/Tosa/IR/TosaInterfaces.cpp.inc"
53struct TosaInlinerInterface :
public DialectInlinerInterface {
54 using DialectInlinerInterface::DialectInlinerInterface;
62 IRMapping &map)
const final {
68 IRMapping &map)
const final {
69 return (isa<tosa::IfOp>(dest->getParentOp()) ||
70 isa<tosa::WhileOp>(dest->getParentOp()));
75struct TosaDialectBytecodeInterface :
public BytecodeDialectInterface {
76 TosaDialectBytecodeInterface(Dialect *dialect)
77 : BytecodeDialectInterface(dialect) {}
82 Attribute readAttribute(DialectBytecodeReader &reader)
const override {
86 LogicalResult writeAttribute(Attribute attr,
87 DialectBytecodeWriter &writer)
const override {
88 return ::writeAttribute(attr, writer);
94 Type readType(DialectBytecodeReader &reader)
const override {
98 LogicalResult writeType(Type type,
99 DialectBytecodeWriter &writer)
const override {
100 return ::writeType(type, writer);
103 void writeVersion(DialectBytecodeWriter &writer)
const final {
107 std::unique_ptr<DialectVersion>
108 readVersion(DialectBytecodeReader &reader)
const final {
110 reader.
emitError(
"Dialect does not support versioning");
114 LogicalResult upgradeFromVersion(Operation *topLevelOp,
115 const DialectVersion &version)
const final {
127 return {&getBodyGraph()};
136 return dim == -1 ? ShapedType::kDynamic : dim;
142 Type elementType = variableOp.getType();
145 return RankedTensorType::get(
shape, elementType);
152void TosaDialect::initialize() {
154#define GET_TYPEDEF_LIST
155#include "mlir/Dialect/Tosa/IR/TosaOpsTypesBase.cpp.inc"
159#include "mlir/Dialect/Tosa/IR/TosaOps.cpp.inc"
162#define GET_ATTRDEF_LIST
163#include "mlir/Dialect/Tosa/IR/TosaAttributes.cpp.inc"
165 addInterfaces<TosaDialectBytecodeInterface, TosaInlinerInterface>();
166 declarePromisedInterfaces<
167 shard::ShardingInterface, ClampOp, SigmoidOp, TanhOp, AddOp,
168 ArithmeticRightShiftOp, BitwiseAndOp, BitwiseOrOp, BitwiseXorOp, IntDivOp,
169 LogicalAndOp, LogicalLeftShiftOp, LogicalRightShiftOp, LogicalOrOp,
170 LogicalXorOp, MaximumOp, MinimumOp, MulOp, PowOp, SubOp, AbsOp,
171 BitwiseNotOp, CeilOp, ClzOp, ExpOp, FloorOp, LogOp, LogicalNotOp,
172 NegateOp, ReciprocalOp, RsqrtOp, SelectOp, EqualOp, GreaterOp,
173 GreaterEqualOp, MatMulOp>();
180 if (llvm::isa<shapeType>(type) && llvm::isa<DenseIntElementsAttr>(value)) {
181 return tosa::ConstShapeOp::create(builder, loc, type,
182 llvm::cast<DenseIntElementsAttr>(value));
184 if (llvm::isa<ElementsAttr>(value))
185 return tosa::ConstOp::create(builder, loc, type,
186 llvm::cast<ElementsAttr>(value));
196ParseResult getShapeAndElementType(
OpAsmParser &parser,
Type parsedType,
198 TypeAttr &typeAttr) {
199 if (
auto shapedType = dyn_cast<ShapedType>(parsedType)) {
200 if (!shapedType.hasRank())
202 <<
"expected ranked type";
204 auto elementType = shapedType.getElementType();
205 typeAttr = TypeAttr::get(elementType);
212 <<
"expected shaped type";
229 <<
"expected attribute";
231 if (
auto typedAttr = dyn_cast<TypedAttr>(initialValueAttr)) {
232 return getShapeAndElementType(parser, typedAttr.getType(), varShapeAttr,
236 <<
"expected Typed attr";
239 initialValueAttr =
nullptr;
243 <<
"expected type after colon";
245 return getShapeAndElementType(parser, parsedType, varShapeAttr, typeAttr);
250 TypeAttr typeAttr,
Attribute initialValueAttr) {
251 bool needsSpace =
false;
252 if (!dyn_cast_or_null<TypedAttr>(initialValueAttr)) {
255 Type elementType = typeAttr.getValue();
256 RankedTensorType tensorType =
258 auto tensorTypeAttr = TypeAttr::get(tensorType);
263 if (initialValueAttr) {
283 if (
auto quantType = llvm::dyn_cast<mlir::quant::QuantizedType>(srcType))
293 Value valZp, StringRef name) {
298 mlir::isa<IntegerType>(eType) && mlir::isa<IntegerType>(eZpType);
302 if (!bothInts || !sameBitWidth) {
304 <<
"expected " << name <<
" and " << name
305 <<
"_zp to both be integer of the same bitwidth, but got " << eType
306 <<
" vs. " << eZpType;
313 Value src, int32_t val) {
316 const auto padConstType = mlir::RankedTensorType::get({1}, srcType);
317 const auto padConstEType = mlir::RankedTensorType::get({1}, srcElemType);
318 const auto padConstAttr{
319 llvm::isa<FloatType>(srcElemType)
324 return tosa::ConstOp::create(builder, loc, padConstType, padConstAttr);
328 if (
auto blockScaledTy = dyn_cast<tosa::BlockScaledType>(type))
330 if (dyn_cast<tosa::mxint8Type>(type))
339 const StringRef operandName,
340 const StringRef dimName) {
341 if (ShapedType::isDynamic(currDim)) {
344 }
else if (ShapedType::isStatic(newDim) && currDim != newDim) {
346 << dimName <<
" of " << operandName <<
" to match size " << currDim
347 <<
", got " << newDim;
354 auto printDim = [&](
int64_t dim) {
355 if (ShapedType::isDynamic(dim))
361 llvm::interleaveComma(
shape,
diag, printDim);
367 StringRef outputName =
"output") {
368 assert(outputType.hasRank() &&
"expected output type to be ranked");
374 diag << outputName <<
" shape ";
376 diag <<
" to be compatible with inferred shape ";
384 const int64_t stride,
const int64_t dilation,
const llvm::StringRef dimName,
385 const llvm::StringRef dimAxis,
const llvm::StringRef padBeforeName,
386 const llvm::StringRef padAfterName) {
387 if (inputSize == ShapedType::kDynamic || kernelSize == ShapedType::kDynamic)
392 const std::optional<int64_t> calculatedOutSizeMinusOne =
idivCheck(
393 inputSize - 1 + padBefore + padAfter - (kernelSize - 1) * dilation,
395 if (!calculatedOutSizeMinusOne.has_value())
397 << dimName <<
" - 1 + pad_" << padBeforeName <<
" + pad_"
398 << padAfterName <<
" - (kernel_" << dimName <<
" - 1) * dilation_"
399 << dimAxis <<
" to be wholly divisible by stride_" << dimAxis
400 <<
", got (" << inputSize <<
" - 1 + " << padBefore <<
" + "
401 << padAfter <<
" - (" << kernelSize <<
" - 1) * " << dilation
404 const int64_t calculatedOutSize = calculatedOutSizeMinusOne.value() + 1;
405 if (outputSize != ShapedType::kDynamic && calculatedOutSize != outputSize)
407 << dimName <<
" did not match expected: "
408 <<
"calculated=" << calculatedOutSize <<
", expected=" << outputSize;
416size_t mlir::tosa::mxint8Type::getDenseElementBitSize()
const {
return 8; }
419mlir::tosa::mxint8Type::convertToAttribute(
ArrayRef<char> rawData)
const {
420 assert(rawData.size() == 1 &&
"expected 1 byte for tosa.mxint8 element");
421 const auto intType = IntegerType::get(
getContext(), 8);
422 return intType.convertToAttribute(rawData);
425LogicalResult mlir::tosa::mxint8Type::convertFromAttribute(
427 const auto intAttr = dyn_cast<IntegerAttr>(attr);
430 const Type attrType = intAttr.getType();
433 return cast<IntegerType>(attrType).convertFromAttribute(attr,
result);
442 bool allowScaleValues) {
443 const auto tensorType = llvm::cast<ShapedType>(type);
444 const BlockScaledType elemType =
445 llvm::dyn_cast<BlockScaledType>(tensorType.getElementType());
449 if (!allowScaleValues && elemType.hasScaleValues()) {
452 <<
"block scaled tensor type with scale values is not allowed";
456 if (!tensorType.hasRank())
459 if (tensorType.getRank() == 0) {
461 emitError() <<
"block scaled tensor type must have rank greater than "
467 const uint32_t blockSize =
468 BlockShapeAttr::getBlockShapeValue(elemType.getBlockShape());
470 if (allowScaleValues && elemType.hasScaleValues() &&
471 tensorType.hasStaticShape()) {
472 const size_t numBlocks = tensorType.getNumElements() / blockSize;
473 if (elemType.getScaleValues().size() != numBlocks) {
475 emitError() <<
"block scaled tensor type with scale values must have "
476 "scale values for each block, expected "
477 << numBlocks <<
", got "
478 << elemType.getScaleValues().size();
483 const int64_t blockedDimension = tensorShape.back();
484 if (ShapedType::isDynamic(blockedDimension))
487 if (blockedDimension % blockSize != 0) {
489 emitError() <<
"last dimension of block scaled tensor type ("
490 << blockedDimension <<
") must be divisible by block size ("
506 type, [ctx] {
return emitError(UnknownLoc::get(ctx)); })) &&
508 return ": " + message;
517 const auto parseScaleValue = [&]() -> ParseResult {
524 if (floatValue < 0.0)
525 return parser.
emitError(loc,
"scale value must be non-negative, got ")
528 Type attrType = scaleType;
532 if (attrType != scaleType)
533 return parser.
emitError(loc,
"parsed attribute type ")
534 << attrType <<
" does not match expected scale type " << scaleType;
536 scaleValues.push_back(FloatAttr::get(attrType, floatValue));
545 llvm::interleaveComma(scaleValues, printer, [&](
Attribute scaleValue) {
550size_t mlir::tosa::BlockScaledType::getDenseElementBitSize()
const {
552 if (isa<tosa::mxint8Type>(valueType))
558mlir::tosa::BlockScaledType::convertToAttribute(
ArrayRef<char> rawData)
const {
562 assert(rawData.size() == 1 &&
"expected 1 byte for block_scaled element");
564 if (
const auto mxint8Value = dyn_cast<tosa::mxint8Type>(valueType))
565 return mxint8Value.convertToAttribute(rawData);
566 if (!isa<FloatType>(valueType))
571LogicalResult mlir::tosa::BlockScaledType::convertFromAttribute(
574 if (
const auto mxint8Value = dyn_cast<tosa::mxint8Type>(valueType))
575 return mxint8Value.convertFromAttribute(attr,
result);
577 const auto floatAttr = dyn_cast<FloatAttr>(attr);
578 if (!floatAttr || floatAttr.getType() != valueType)
589template <
typename A, std::enable_if_t<std::is_same_v<A, ArgMaxOp::Adaptor> ||
590 std::is_same_v<A, ArgMinOp::Adaptor>,
593 MLIRContext *context, ::std::optional<Location> location, A adaptor,
596 IntegerAttr axis = adaptor.getProperties().axis;
597 int32_t axisVal = axis.getValue().getSExtValue();
604 const auto inputRank = inputShape.
getRank();
606 outShape.reserve(inputRank - 1);
607 for (
int i = 0, s = inputRank; i < s; i++) {
622 const ShapedType resultType = llvm::cast<ShapedType>(op.getType());
624 if (
const auto resultETy = resultType.getElementType();
625 !resultETy.isIntOrIndex())
626 return op.emitOpError(
"result tensor is not of integer type");
628 const auto inputType = llvm::cast<ShapedType>(op.getInput().getType());
629 if (!inputType.hasRank())
633 const int64_t axis = op.getAxisAttr().getInt();
634 if (((axis < 0) || axis >= inputType.getRank()))
635 return op.emitOpError(
"specified axis is outside the rank of the tensor");
637 if (!resultType.hasRank())
643 expectedOutputShape.erase(expectedOutputShape.begin() + axis);
645 return op.emitOpError(
"expected output shape '")
646 << expectedOutputShape <<
"', got '" << outputShape <<
"'";
653 const auto inputType = llvm::dyn_cast<TensorType>(op.getInput().getType());
654 const auto weightType = llvm::dyn_cast<TensorType>(op.getWeight().getType());
656 auto inputEType = inputType.getElementType();
657 auto weightEType = weightType.getElementType();
659 llvm::cast<ShapedType>(op.getBias().getType()).getElementType();
661 llvm::cast<ShapedType>(op.getResult().getType()).getElementType();
662 bool biasIsFloat = llvm::isa<FloatType>(biasEType);
663 bool resultIsFloat = llvm::isa<FloatType>(resultEType);
665 if (
auto quantType = llvm::dyn_cast<mlir::quant::QuantizedType>(inputEType))
668 if (
auto quantType = llvm::dyn_cast<mlir::quant::QuantizedType>(weightEType))
671 if (
auto quantType = llvm::dyn_cast<mlir::quant::QuantizedType>(biasEType))
674 if (
auto quantType = llvm::dyn_cast<mlir::quant::QuantizedType>(resultEType))
677 if (biasIsFloat && resultIsFloat && (biasEType != resultEType)) {
681 "expect both bias and result to have same element type, got ")
682 << biasEType <<
" and " << resultEType;
686 const bool isInputBlockScaled = llvm::isa<BlockScaledType>(inputEType);
687 const bool isWeightBlockScaled = llvm::isa<BlockScaledType>(weightEType);
688 const bool isInputFloat = llvm::isa<FloatType>(inputEType);
689 const bool isWeightFloat = llvm::isa<FloatType>(weightEType);
691 const bool isInputBSorFloat = isInputBlockScaled || isInputFloat;
692 const bool isWeightBSorFloat = isWeightBlockScaled || isWeightFloat;
695 if (isInputBSorFloat != isWeightBSorFloat) {
697 "expect both input and weight to be float or not together, got ")
698 << inputEType <<
" and " << weightEType;
703 if (!isInputBlockScaled && inputEType != inputZpEType) {
704 return op.emitOpError(
"expect both input and its zero point are the same "
705 "element type, got ")
706 << inputEType <<
" and " << inputZpEType;
708 if (isInputBlockScaled && !llvm::isa<Float32Type>(inputZpEType)) {
709 return op.emitOpError(
710 "expect block scaled input to have fp32 zero point, got ")
711 << inputEType <<
" and " << inputZpEType;
715 if (!isWeightBlockScaled && weightEType != weightZpEType) {
716 return op.emitOpError(
"expect both weight and its zero point are the same "
717 "element type, got ")
718 << weightEType <<
" and " << weightZpEType;
720 if (isWeightBlockScaled && !llvm::isa<Float32Type>(weightZpEType)) {
721 return op.emitOpError(
722 "expect block scaled weight to have fp32 zero point, got ")
723 << weightEType <<
" and " << weightZpEType;
726 FailureOr<int64_t> maybeIZp = op.getInputZeroPoint();
727 if (succeeded(maybeIZp) && op.verifyInputZeroPoint(*maybeIZp).failed())
730 FailureOr<int64_t> maybeWZp = op.getWeightZeroPoint();
731 if (succeeded(maybeWZp) && op.verifyWeightZeroPoint(*maybeWZp).failed())
737LogicalResult tosa::ConstOp::verify() {
739 auto attrType = llvm::dyn_cast<TensorType>(getValuesAttr().
getType());
740 auto outputType = llvm::dyn_cast<TensorType>(getOutput().
getType());
742 if (!attrType || !outputType) {
743 emitOpError(
"expected tensors for attr/result type");
747 const Type attrElemType = attrType.getElementType();
748 const Type resultElemType = outputType.getElementType();
751 llvm::dyn_cast<mlir::quant::QuantizedType>(resultElemType)) {
756 if (
auto attrBlockScaledType =
757 llvm::dyn_cast<mlir::tosa::BlockScaledType>(attrElemType)) {
758 if (!attrBlockScaledType.hasScaleValues())
760 "attribute block scaled type must have scale values");
762 const auto emitAttributeError = [&op]() {
763 return op.
emitOpError(
"attribute block scaled type is invalid: ");
769 const BlockScaledType resultBlockScaledType =
770 llvm::dyn_cast<mlir::tosa::BlockScaledType>(resultElemType);
771 if (!resultBlockScaledType)
773 "result type must be block scaled type if attribute is block "
776 if (attrBlockScaledType.getValueType() !=
777 resultBlockScaledType.getValueType() ||
778 attrBlockScaledType.getScaleType() !=
779 resultBlockScaledType.getScaleType() ||
780 attrBlockScaledType.getBlockShape() !=
781 resultBlockScaledType.getBlockShape())
783 "expected block scaled element type to be compatible "
784 "between attr and result, got ")
785 << attrBlockScaledType <<
" vs. " << resultBlockScaledType;
790 if (attrElemType != resultElemType)
791 return emitOpError(
"expected same attr/result element types");
804 if (llvm::any_of(padding, [](
int64_t p) {
return p < 0; }))
805 return op.emitOpError(
"expect all padding values to be >= 0, got ")
809 if (llvm::any_of(strides, [](
int64_t s) {
return s < 1; }))
810 return op.emitOpError(
"expect all stride values to be >= 1, got ")
814 if (llvm::any_of(dilations, [](
int64_t d) {
return d < 1; }))
815 return op.emitOpError(
"expect all dilation values to be >= 1, got ")
818 const RankedTensorType outputType =
819 llvm::dyn_cast<RankedTensorType>(op.getOutput().getType());
824 const RankedTensorType inputType =
825 llvm::dyn_cast<RankedTensorType>(op.getInput().getType());
826 const RankedTensorType weightType =
827 llvm::dyn_cast<RankedTensorType>(op.getWeight().getType());
829 if (inputType && weightType) {
831 if constexpr (std::is_same<T, tosa::Conv2DOp>::value) {
833 op, inputType.getDimSize(1), weightType.getDimSize(1),
834 outputType.getDimSize(1), padding[0], padding[1], strides[0],
835 dilations[0],
"height",
"y",
"top",
"bottom")))
839 op, inputType.getDimSize(2), weightType.getDimSize(2),
840 outputType.getDimSize(2), padding[2], padding[3], strides[1],
841 dilations[1],
"width",
"x",
"left",
"right")))
846 if constexpr (std::is_same<T, tosa::DepthwiseConv2DOp>::value) {
848 op, inputType.getDimSize(1), weightType.getDimSize(0),
849 outputType.getDimSize(1), padding[0], padding[1], strides[0],
850 dilations[0],
"height",
"y",
"top",
"bottom")))
854 op, inputType.getDimSize(2), weightType.getDimSize(1),
855 outputType.getDimSize(2), padding[2], padding[3], strides[1],
856 dilations[1],
"width",
"x",
"left",
"right")))
861 if constexpr (std::is_same<T, tosa::Conv3DOp>::value) {
863 op, inputType.getDimSize(1), weightType.getDimSize(1),
864 outputType.getDimSize(1), padding[0], padding[1], strides[0],
865 dilations[0],
"depth",
"d",
"front",
"back")))
869 op, inputType.getDimSize(2), weightType.getDimSize(2),
870 outputType.getDimSize(2), padding[2], padding[3], strides[1],
871 dilations[1],
"height",
"y",
"top",
"bottom")))
875 op, inputType.getDimSize(3), weightType.getDimSize(3),
876 outputType.getDimSize(3), padding[4], padding[5], strides[2],
877 dilations[2],
"width",
"x",
"left",
"right")))
882 const RankedTensorType biasType =
883 llvm::dyn_cast<RankedTensorType>(op.getBias().getType());
888 const int64_t biasChannels = biasType.getDimSize(0);
890 outputType.getDimSize(outputType.getRank() - 1);
891 if (biasChannels == ShapedType::kDynamic ||
892 outputChannels == ShapedType::kDynamic)
896 if (biasChannels != outputChannels && biasChannels != 1)
897 return op.emitOpError(
898 "bias channels expected to be equal to output channels (")
899 << outputChannels <<
") or 1, got " << biasChannels;
906 StringRef name1,
Type type2,
908 auto shapeType1 = dyn_cast<ShapedType>(type1);
909 auto shapeType2 = dyn_cast<ShapedType>(type2);
910 if (!shapeType1 || !shapeType2)
913 auto elemType1 = shapeType1.getElementType();
914 auto elemType2 = shapeType2.getElementType();
915 if (elemType1 != elemType2)
917 <<
"require same element type for " << name1 <<
" (" << elemType1
918 <<
") and " << name2 <<
" (" << elemType2 <<
")";
922 <<
"require same shapes for " << name1 <<
" (" << type1 <<
") and "
923 << name2 <<
" (" << type2 <<
")";
933 if (list1.size() != list2.size())
935 <<
"require same number of values in " << name1 <<
" ("
936 << list1.size() <<
") and " << name2 <<
" (" << list2.size() <<
")";
938 for (
auto [type1, type2] :
958 op->template getParentWithTrait<OpTrait::SymbolTable>();
965 const auto varOp = symTable.
lookup<tosa::VariableOp>(op.getName());
969 return op->emitOpError(
"'")
970 << op.getName() <<
"' has not been declared by 'tosa.variable'";
984 StringRef aName =
"input",
985 StringRef bName =
"output") {
986 auto aTType = llvm::dyn_cast<TensorType>(aType);
987 auto bTType = llvm::dyn_cast<TensorType>(bType);
989 op->
emitOpError(
"expect shaped tensor for") << aName <<
", got " << aType;
993 op->
emitOpError(
"expect shaped tensor for") << bName <<
", got" << bType;
996 auto aElementType = aTType.getElementType();
997 auto bElementType = bTType.getElementType();
999 llvm::dyn_cast<mlir::quant::UniformQuantizedType>(aElementType);
1001 llvm::dyn_cast<mlir::quant::UniformQuantizedType>(bElementType);
1002 if ((aElementType.isIntOrIndexOrFloat() || aQuantType) &&
1003 (bElementType.isIntOrIndexOrFloat() || bQuantType) &&
1004 aElementType != bElementType) {
1010 << aName <<
" and " << bName <<
" to have same element type, got "
1011 << aElementType <<
" and " << bElementType;
1017LogicalResult tosa::ArgMaxOp::verify() {
return argMaxMinVerify(*
this); }
1019LogicalResult tosa::ArgMinOp::verify() {
return argMaxMinVerify(*
this); }
1029 const bool hasKernel = kernel.size() > 0;
1030 const bool hasStrides = strides.size() > 0;
1031 const bool hasPad = padding.size() > 0;
1033 if (hasKernel && llvm::any_of(kernel, [](
int64_t s) {
return s < 1; }))
1034 return op->
emitOpError(
"expect all kernel values to be >= 1, got ")
1037 if (hasStrides && llvm::any_of(strides, [](
int64_t s) {
return s < 1; }))
1038 return op->
emitOpError(
"expect all stride values to be >= 1, got ")
1041 if (hasPad && llvm::any_of(padding, [](
int64_t p) {
return p < 0; }))
1042 return op->
emitOpError(
"expect all padding values to be >= 0, got ")
1045 if (hasKernel && hasPad) {
1047 const int64_t kernelX = kernel[1];
1048 const int64_t padLeft = padding[2];
1049 const int64_t padRight = padding[3];
1050 if (padRight >= kernelX || padLeft >= kernelX)
1051 return op->
emitOpError(
"expected left/right padding to be less than the "
1052 "width of the kernel, got pad_left=")
1053 << padLeft <<
", pad_right=" << padRight
1054 <<
", kernel_x=" << kernelX;
1056 const int64_t kernelY = kernel[0];
1057 const int64_t padTop = padding[0];
1058 const int64_t padBottom = padding[1];
1059 if (padTop >= kernelY || padBottom >= kernelY)
1060 return op->
emitOpError(
"expected top/bottom padding to be less than the "
1061 "height of the kernel, got pad_top=")
1062 << padTop <<
", pad_bottom=" << padBottom
1063 <<
", kernel_y=" << kernelY;
1066 const auto inputType = llvm::dyn_cast<RankedTensorType>(input.
getType());
1067 const auto outputType = llvm::dyn_cast<RankedTensorType>(output.
getType());
1068 if (!inputType || !outputType)
1071 if (hasKernel && hasStrides && hasPad) {
1072 const auto verifyOutputSize =
1076 const llvm::StringRef dimName,
const llvm::StringRef dimAxis,
1077 const llvm::StringRef padBeforeName,
1078 const llvm::StringRef padAfterName) -> LogicalResult {
1079 if (ShapedType::isDynamic(inputSize))
1082 const std::optional<int64_t> calculatedOutSizeMinusOne =
1083 idivCheck(inputSize + padBefore + padAfter - kernelSize, strideSize);
1084 if (!calculatedOutSizeMinusOne.has_value())
1086 << dimName <<
" + pad_" << padBeforeName <<
" + pad_"
1087 << padAfterName <<
" - kernel_" << dimAxis
1088 <<
" to be wholly divisible by stride_" << dimAxis <<
", got ("
1089 << inputSize <<
" + " << padBefore <<
" + " << padAfter <<
" - "
1090 << kernelSize <<
") / " << strideSize;
1092 const int64_t calculatedOutSize = calculatedOutSizeMinusOne.value() + 1;
1093 if (ShapedType::isStatic(outputSize) && calculatedOutSize != outputSize)
1095 << dimName <<
" did not match expected: " <<
"calculated="
1096 << calculatedOutSize <<
", expected=" << outputSize;
1101 if (failed(verifyOutputSize(inputType.getDimSize(1),
1102 outputType.getDimSize(1), kernel[0], strides[0],
1103 padding[0], padding[1],
"height",
"y",
"top",
1107 if (failed(verifyOutputSize(
1108 inputType.getDimSize(2), outputType.getDimSize(2), kernel[1],
1109 strides[1], padding[2], padding[3],
"width",
"x",
"left",
"right")))
1115template <
typename T>
1118 op.getPad(), op.getInput(), op.getOutput());
1121template <
typename T>
1125 const Type inputZpETy =
1127 const Type outputZpETy =
1130 auto accType = op.getAccType();
1131 if (llvm::isa<IntegerType>(inputETy) && !accType.isInteger(32))
1132 return op.emitOpError(
"accumulator type for integer tensor is not i32");
1134 if (inputETy.
isF16() && !(accType.isF16() || accType.isF32()))
1135 return op.emitOpError(
"accumulator type for f16 tensor is not f16/f32");
1137 if (inputETy.
isBF16() && !accType.isF32())
1138 return op.emitOpError(
"accumulator type for bf16 tensor is not f32");
1140 if (inputETy.
isF32() && !accType.isF32())
1141 return op.emitOpError(
"accumulator type for f32 tensor is not f32");
1143 if (inputETy != inputZpETy)
1144 return op.emitOpError(
"expect both input and its zero point are the same "
1145 "element type, got ")
1146 << inputETy <<
" and " << inputZpETy;
1148 if (resultETy != outputZpETy)
1149 return op.emitOpError(
"expect both output and its zero point are the same "
1150 "element type, got ")
1151 << resultETy <<
" and " << outputZpETy;
1153 FailureOr<int64_t> maybeIZp = op.getInputZeroPoint();
1154 if (succeeded(maybeIZp) && op.verifyInputZeroPoint(*maybeIZp).failed())
1157 FailureOr<int64_t> maybeOZp = op.getOutputZeroPoint();
1158 if (succeeded(maybeOZp) && op.verifyOutputZeroPoint(*maybeOZp).failed())
1165struct AdaptivePoolingConstShapeValues {
1166 llvm::SmallVector<int64_t> kernel;
1167 llvm::SmallVector<int64_t> stride;
1168 llvm::SmallVector<int64_t> pad;
1172template <
typename T>
1174 std::is_same_v<T, tosa::AvgPool2dAdaptiveOp> ||
1175 std::is_same_v<T, tosa::MaxPool2dAdaptiveOp>;
1177template <
typename T,
1178 typename std::enable_if<IsSupportedAdaptivePoolConstShapeVerifyOp<T>,
1181 T op, AdaptivePoolingConstShapeValues &values) {
1187LogicalResult tosa::AvgPool2dOp::verify() {
1195LogicalResult tosa::AvgPool2dAdaptiveOp::verify() {
1196 AdaptivePoolingConstShapeValues values;
1205 values.pad, getInput(), getOutput())))
1214LogicalResult tosa::ClampOp::verify() {
1216 llvm::cast<ShapedType>(getInput().
getType()).getElementType();
1217 if (
auto quantType =
1218 llvm::dyn_cast<mlir::quant::UniformQuantizedType>(inputETy)) {
1222 llvm::cast<ShapedType>(getOutput().
getType()).getElementType();
1223 if (
auto quantType =
1224 llvm::dyn_cast<mlir::quant::UniformQuantizedType>(outputETy)) {
1227 if (inputETy != outputETy)
1228 return emitOpError(
"input/output element types are incompatible.");
1230 auto maxValAttr = getMaxValAttr();
1231 auto minValAttr = getMinValAttr();
1235 if (inputETy.
isInteger(dataTypeBitWidth)) {
1239 auto intMaxValAttr = mlir::dyn_cast<mlir::IntegerAttr>(maxValAttr);
1240 auto intMinValAttr = mlir::dyn_cast<mlir::IntegerAttr>(minValAttr);
1241 if (!intMaxValAttr || !intMinValAttr ||
1242 (intMaxValAttr.getType() != intMinValAttr.getType()) ||
1243 (intMaxValAttr.getType() != inputETy))
1244 return emitOpError(
"min/max attributes types are incompatible with "
1245 "input/output element types.");
1248 const bool isBoolean = inputETy.
isInteger(1);
1249 const APInt minVal = intMinValAttr.getValue();
1250 const APInt maxVal = intMaxValAttr.getValue();
1251 if ((isUnsigned || isBoolean) ? maxVal.ult(minVal) : maxVal.slt(minVal))
1252 return emitOpError(
"expected min_val <= max_val, got min_val=")
1253 << minValAttr <<
", max_val=" << maxValAttr;
1258 auto floatMaxValAttr = mlir::dyn_cast<mlir::FloatAttr>(maxValAttr);
1259 auto floatMinValAttr = mlir::dyn_cast<mlir::FloatAttr>(minValAttr);
1260 if (!floatMaxValAttr || !floatMinValAttr ||
1261 (floatMaxValAttr.getType() != floatMinValAttr.getType()) ||
1262 (floatMaxValAttr.getType() != inputETy))
1263 return emitOpError(
"min/max attributes types are incompatible with "
1264 "input/output element types.");
1266 const APFloat minVal = floatMinValAttr.getValue();
1267 const APFloat maxVal = floatMaxValAttr.getValue();
1268 if (minVal.isNaN() || maxVal.isNaN())
1269 return emitOpError(
"min/max attributes should not be 'NaN', got min_val=")
1270 << minValAttr <<
", max_val=" << maxValAttr;
1272 if (maxVal < minVal)
1273 return emitOpError(
"expected min_val <= max_val, got min_val=")
1274 << minValAttr <<
", max_val=" << maxValAttr;
1294 result.addOperands({input, weight, bias, zps.first, zps.second});
1295 result.addAttribute(
"pad", pad);
1296 result.addAttribute(
"stride", stride);
1297 result.addAttribute(
"dilation", dilation);
1298 result.addAttribute(
"acc_type", accType);
1299 Type finalOutputType = outputType;
1305 result.addTypes(finalOutputType);
1316 result.addOperands({input, weight, bias, zps.first, zps.second});
1317 result.addAttribute(
"out_pad", outpad);
1318 result.addAttribute(
"stride", stride);
1319 result.addAttribute(
"acc_type", accType);
1320 Type finalOutputType = outputType;
1326 result.addTypes(finalOutputType);
1333 result.addOperands({a,
b, zps.first, zps.second});
1335 Type finalOutputType{outputType};
1338 auto inputBits = eType.getIntOrFloatBitWidth();
1340 auto outputShapedType = llvm::dyn_cast<ShapedType>(outputType);
1341 assert(outputShapedType &&
"Output must be a shaped type");
1343 IntegerType accElementType;
1344 if (inputBits == 16)
1349 finalOutputType = outputShapedType.clone(accElementType);
1351 result.addTypes(finalOutputType);
1372 DenseArrayAttr kernel, DenseArrayAttr stride,
1373 DenseArrayAttr pad, TypeAttr accType) {
1378 if (
auto quantAttr =
1380 inputZp = quantAttr.getInputZp();
1381 outputZp = quantAttr.getOutputZp();
1383 const std::optional<Value> inputZpOp =
1388 "Failed to create input zero point tensor for quantized AVG_POOL2D op");
1390 const std::optional<Value> outputZpOp =
1393 (
void)
emitError(loc,
"Failed to create output zero point tensor for "
1394 "quantized AVG_POOL2D op");
1397 if (inputZpOp && outputZpOp) {
1398 result.addOperands({input, inputZpOp.value(), outputZpOp.value()});
1403 result.addOperands({input});
1405 result.addAttribute(
"kernel", kernel);
1406 result.addAttribute(
"stride", stride);
1407 result.addAttribute(
"pad", pad);
1408 result.addAttribute(
"acc_type", accType);
1409 result.types.push_back(outputType);
1422 if (
auto quantAttr =
1424 inputZp = quantAttr.getInputZp();
1425 outputZp = quantAttr.getOutputZp();
1427 const std::optional<Value> inputZpOp =
1431 "Failed to create input zero point tensor for quantized "
1432 "AVG_POOL2D_ADAPTIVE op");
1434 const std::optional<Value> outputZpOp =
1437 (
void)
emitError(loc,
"Failed to create output zero point tensor for "
1438 "quantized AVG_POOL2D_ADAPTIVE op");
1441 if (inputZpOp && outputZpOp) {
1446 result.addOperands({input, inputZpOp.value(), outputZpOp.value(),
1447 kernelShape, strideShape, padShape});
1452 result.addOperands({input});
1454 result.addAttribute(
"acc_type", accType);
1455 result.types.push_back(outputType);
1469 input1Zp = quantAttr.getInputZp();
1470 outputZp = quantAttr.getOutputZp();
1472 const std::optional<Value> input1ZpOp =
1476 loc,
"Failed to create input1 zero point for quantized NEGATE op");
1479 const std::optional<Value> outputZpOp =
1483 loc,
"Failed to create output zero point for quantized NEGATE op");
1486 if (input1ZpOp && outputZpOp) {
1487 result.addOperands({input, input1ZpOp.value(), outputZpOp.value()});
1492 result.addOperands({input});
1495 result.types.push_back(outputType);
1508 zp =
static_cast<int32_t
>(quantAttr.getInputZp());
1511 result.addOperands({input, paddings, padConstOp});
1512 result.types.push_back(outputType);
1516 StringRef name,
Type variableType,
1521 auto shapedType = dyn_cast<ShapedType>(variableType);
1523 (
void)
emitError(loc,
"variable type must be a shaped type");
1526 if (!shapedType.hasRank()) {
1527 (
void)
emitError(loc,
"variable type must be a ranked type");
1531 auto elementType = shapedType.getElementType();
1532 auto elementTypeAttr = TypeAttr::get(elementType);
1536 result.addAttribute(
"sym_name", nameAttr);
1537 result.addAttribute(
"var_shape", varShapeAttr);
1538 result.addAttribute(
"type", elementTypeAttr);
1539 result.addAttribute(
"initial_value", initialValue);
1552 if (ShapedType::isStatic(dim1) && ShapedType::isStatic(dim2) && dim1 != dim2)
1556 return ShapedType::isDynamic(dim1) ? dim2 : dim1;
1562 for (
int i = 0, e = operands.size(); i != e; ++i) {
1564 if (!
shape.hasRank()) {
1569 outRank = std::max<int64_t>(outRank,
shape.getRank());
1572 outShape.resize(outRank, 1);
1574 for (
int i = 0, e = operands.size(); i != e; ++i) {
1576 auto rankDiff = outShape.size() -
shape.getRank();
1578 for (
size_t i = 0, e =
shape.getRank(); i < e; ++i) {
1579 auto dim1 = outShape[i + rankDiff];
1580 auto dim2 =
shape.getDimSize(i);
1582 const FailureOr<int64_t> maybeResolvedDim =
1584 if (failed(maybeResolvedDim))
1586 const int64_t resolvedDim = *maybeResolvedDim;
1587 outShape[i + rankDiff] = resolvedDim;
1594LogicalResult tosa::ArgMaxOp::inferReturnTypeComponents(
1595 MLIRContext *context, ::std::optional<Location> location,
1596 ArgMaxOp::Adaptor adaptor,
1599 inferredReturnShapes);
1602LogicalResult tosa::ArgMinOp::inferReturnTypeComponents(
1603 MLIRContext *context, ::std::optional<Location> location,
1604 ArgMinOp::Adaptor adaptor,
1607 inferredReturnShapes);
1610LogicalResult tosa::RFFT2dOp::inferReturnTypeComponents(
1611 MLIRContext *context, ::std::optional<Location> location,
1612 RFFT2dOp::Adaptor adaptor,
1614 ShapeAdaptor inputShape(adaptor.getInputReal().getType());
1616 if (!inputShape.hasRank())
1620 outputShape.resize(3, ShapedType::kDynamic);
1621 outputShape[0] = inputShape.getDimSize(0);
1622 outputShape[1] = inputShape.getDimSize(1);
1623 int64_t inWidth = inputShape.getDimSize(2);
1627 if (inWidth != ShapedType::kDynamic)
1628 outputShape[2] = inWidth / 2 + 1;
1637 const llvm::StringRef dimName) {
1638 const bool isPowerOfTwo = (dimSize & (dimSize - 1)) == 0 && dimSize > 0;
1641 << dimName <<
" to be a power of two, got " << dimSize;
1646LogicalResult tosa::RFFT2dOp::verify() {
1647 const auto outputTypes = getResultTypes();
1649 return emitOpError(
"expected output shapes to match, got ") << outputTypes;
1651 const auto inputType =
1652 llvm::dyn_cast<RankedTensorType>(getInputReal().
getType());
1656 const int64_t height = inputType.getDimSize(1);
1657 if (ShapedType::isStatic(height) &&
1661 const int64_t width = inputType.getDimSize(2);
1662 if (ShapedType::isStatic(width) &&
1666 const auto outputType = llvm::dyn_cast<RankedTensorType>(outputTypes[0]);
1672 outputType.getShape().drop_back())))
1673 return emitOpError(
"expected batch and height dimensions of input/output "
1674 "to match, got input=")
1675 << inputType <<
" output=" << outputType;
1678 const int64_t outputWidth = outputType.getDimSize(2);
1679 if (ShapedType::isStatic(width) && ShapedType::isStatic(outputWidth) &&
1680 (outputWidth != (width / 2) + 1))
1682 "expected output width to be equal to input_width / 2 + 1, got ")
1688LogicalResult tosa::FFT2dOp::inferReturnTypeComponents(
1689 MLIRContext *context, ::std::optional<Location> location,
1690 FFT2dOp::Adaptor adaptor,
1692 inferredReturnShapes.push_back(
1694 inferredReturnShapes.push_back(
1699LogicalResult tosa::FFT2dOp::verify() {
1700 const auto inputRealType =
1701 llvm::dyn_cast<RankedTensorType>(getInputReal().
getType());
1702 const auto inputImagType =
1703 llvm::dyn_cast<RankedTensorType>(getInputImag().
getType());
1704 if (!inputRealType || !inputImagType)
1707 const auto trySelectStaticDim = [](
const int64_t a,
const int64_t b) {
1708 return ShapedType::isDynamic(a) ? a :
b;
1711 const int64_t height = trySelectStaticDim(inputRealType.getDimSize(1),
1712 inputImagType.getDimSize(1));
1713 if (ShapedType::isStatic(height) &&
1717 const int64_t width = trySelectStaticDim(inputRealType.getDimSize(2),
1718 inputImagType.getDimSize(2));
1719 if (ShapedType::isStatic(width) &&
1726LogicalResult tosa::ConcatOp::inferReturnTypeComponents(
1727 MLIRContext *context, ::std::optional<Location> location,
1728 ConcatOp::Adaptor adaptor,
1731 const Properties &prop = adaptor.getProperties();
1732 int32_t axis = prop.axis.getValue().getSExtValue();
1734 bool hasRankedInput =
false;
1735 for (
auto operand : adaptor.getOperands()) {
1737 if (!operandShape.hasRank())
1741 if (!hasRankedInput)
1742 outputShape.resize(operandShape.getRank(), ShapedType::kDynamic);
1745 for (
int i = 0, s = operandShape.getRank(); i < s; i++) {
1746 if (i == axis || operandShape.isDynamicDim(i))
1748 if (outputShape[i] == ShapedType::kDynamic)
1749 outputShape[i] = operandShape.getDimSize(i);
1750 if (outputShape[i] != operandShape.getDimSize(i))
1752 "Cannot concat tensors with different sizes"
1753 " on the non-axis dimension ",
1757 hasRankedInput =
true;
1760 if (adaptor.getInput1().empty())
1764 llvm::cast<TensorType>(adaptor.getInput1().getType()[0]).getElementType();
1765 if (!hasRankedInput) {
1772 for (
auto operand : adaptor.getOperands()) {
1777 if (!operandShape.hasRank() || operandShape.isDynamicDim(axis)) {
1778 concatDimSize = ShapedType::kDynamic;
1782 concatDimSize += operandShape.getDimSize(axis);
1785 outputShape[axis] = concatDimSize;
1791LogicalResult tosa::ConcatOp::verify() {
1793 auto outType = getOutput().getType();
1797 if (inputList.empty())
1798 return emitOpError(
"expect at least one input");
1800 if (!llvm::all_of(inputList, [&](
auto input) {
1802 *
this, input.getType(), outType));
1807 const int32_t axis = getAxis();
1809 for (
const auto &input : inputList) {
1810 const Type inputType = input.getType();
1812 if (currShape.hasRank()) {
1813 firstRankedInputShape = currShape;
1815 if (axis < 0 || axis >= firstRankedInputShape.
getRank())
1816 return emitOpError(
"expect axis to be within range 0 < axis < "
1817 "rank(input1[firstRankedTensorIdx]), got ")
1823 const auto allOperandsHasRank = [](
const Value input) {
1826 if (llvm::all_of(inputList, allOperandsHasRank)) {
1829 for (
const auto &[
index, input] : llvm::enumerate(inputList.drop_front())) {
1831 const int64_t inputRank = inputShape.getRank();
1832 const size_t operandNum =
index + 1;
1835 if (inputRank != firstInputRank)
1837 "expect all operands to have the same rank, but got ")
1838 << firstInputRank <<
" vs " << inputRank <<
" on operands 0 and "
1842 for (
int i = 0; i < inputRank; i++) {
1843 const int64_t inputDim = inputShape.getDimSize(i);
1845 if (i == axis || firstRankedInputShape.
isDynamicDim(i) ||
1846 inputShape.isDynamicDim(i))
1848 if (inputDim != firstInputDim)
1849 return emitOpError(
"expect all operand shapes to have the same sizes "
1850 "on non-axis dimensions, but got ")
1851 << inputDim <<
" vs " << firstInputDim <<
" at index " << i
1852 <<
" on operands 0 and " << operandNum;
1857 if (outputShape.hasRank() && outputShape.getRank() != firstInputRank)
1858 return emitOpError(
"expect output rank to match inputs rank, got ")
1859 << outputShape.getRank() <<
" vs " << firstInputRank;
1863 for (
const auto &input : inputList) {
1865 if (inputShape.isDynamicDim(axis)) {
1870 axisSum += inputShape.getDimSize(axis);
1873 if (axisSum >= 0 && outputShape.hasRank() &&
1874 !outputShape.isDynamicDim(axis) &&
1875 axisSum != outputShape.getDimSize(axis))
1876 return emitOpError(
"requires sum of axis dimensions of input1 "
1877 "equal to output axis dimension, got ")
1878 << axisSum <<
" and " << outputShape.getDimSize(axis);
1884LogicalResult tosa::EqualOp::inferReturnTypeComponents(
1885 MLIRContext *context, ::std::optional<Location> location,
1889 auto elementType = IntegerType::get(context, 1);
1902 if (l.size() != r.size() || l.size() != 1)
1912 if (!
shape.hasRank())
1913 return ShapedType::kDynamic;
1914 const int64_t inputAxis = axis - (outputRank -
shape.getRank());
1915 return inputAxis < 0 ? 1 :
shape.getDimSize(inputAxis);
1918static FailureOr<SmallVector<int64_t>>
1920 int64_t outputRank,
bool transposeB) {
1921 if (outputRank < 2 ||
1929 for (
int64_t axis = 0; axis < outputRank - 2; ++axis) {
1933 if (failed(resolvedDim))
1935 outputShape[axis] = *resolvedDim;
1941 outputShape[outputRank - 1] =
1956 inferredReturnShapes.emplace_back();
1963 if (ShapedType::isStatic(aChannels) && ShapedType::isStatic(bChannels) &&
1964 aChannels != bChannels)
1968 FailureOr<SmallVector<int64_t>> outputShape =
1970 if (failed(outputShape))
1973 inferredReturnShapes.emplace_back(*outputShape);
1977LogicalResult tosa::MatMulOp::inferReturnTypeComponents(
1978 MLIRContext *context, ::std::optional<Location> location,
1979 MatMulOp::Adaptor adaptor,
1984 inferredReturnShapes);
1987template <
typename T>
1989 Type bElementType) {
1990 const auto aQuantizedEType =
1991 llvm::dyn_cast<quant::UniformQuantizedType>(aElementType);
1992 const auto bQuantizedEType =
1993 llvm::dyn_cast<quant::UniformQuantizedType>(bElementType);
1995 if (aQuantizedEType || bQuantizedEType) {
1996 if (!aQuantizedEType || !bQuantizedEType) {
1997 return op.emitOpError(
"expect operands to be both quantized or both not "
1999 << aElementType <<
" and " << bElementType;
2002 auto aQuantWidth = aQuantizedEType.getStorageTypeIntegralWidth();
2003 auto bQuantWidth = bQuantizedEType.getStorageTypeIntegralWidth();
2004 if (aQuantWidth != bQuantWidth) {
2005 return op.emitOpError(
"expect quantized operands to have same widths, "
2007 << aQuantWidth <<
" and " << bQuantWidth;
2014template <
typename T>
2016 StringRef inputName,
2021 Type expectedElementType = inputStorageElementType;
2023 if (isa<BlockScaledType>(inputElementType))
2024 expectedElementType = Float32Type::get(op.getContext());
2026 if (expectedElementType == zpElementType)
2030 diag << inputName <<
" and " << zpName;
2031 if (isa<BlockScaledType>(inputElementType))
2032 diag <<
" have compatible element types, got " << inputElementType
2033 <<
" and " << zpElementType;
2035 diag <<
" have the same element type, got " << inputStorageElementType
2036 <<
" and " << zpElementType;
2044 batchShape.reserve(
shape.getRank() - 2);
2045 for (
int64_t i = 0, e =
shape.getRank() - 2; i < e; ++i)
2046 batchShape.push_back(
shape.getDimSize(i));
2050template <
typename T>
2054 const auto outputType = cast<ShapedType>(op.getResult().getType());
2057 : ShapedType::kDynamic;
2065 const int64_t minimumOutputRank =
2068 if (outputType.hasRank() && outputType.getRank() < minimumOutputRank)
2069 return op.emitOpError(
"expected output rank of at least ")
2070 << minimumOutputRank <<
", got " << outputType.getRank();
2072 const bool bothInputsRanked = aShape.
hasRank() && bShape.
hasRank();
2073 if (!bothInputsRanked && !outputType.hasRank())
2078 const int64_t expectedOutputRank =
2079 bothInputsRanked ? minimumOutputRank : outputType.getRank();
2080 FailureOr<SmallVector<int64_t>> expectedOutputShape =
2082 if (failed(expectedOutputShape)) {
2084 "expected batch dimensions of a and b to be broadcast compatible, "
2087 diag <<
"] and b=[";
2093 if (outputType.hasRank())
2095 op.getOperation(), outputType, *expectedOutputShape);
2099LogicalResult MatMulOp::verify() {
2111 FailureOr<int64_t> maybeAZp = getAZeroPoint();
2112 if (succeeded(maybeAZp) && verifyAZeroPoint(*maybeAZp).failed())
2115 FailureOr<int64_t> maybeBZp = getBZeroPoint();
2116 if (succeeded(maybeBZp) && verifyBZeroPoint(*maybeBZp).failed())
2122LogicalResult tosa::MatMulTOp::inferReturnTypeComponents(
2123 MLIRContext *context, ::std::optional<Location> location,
2124 MatMulTOp::Adaptor adaptor,
2129 inferredReturnShapes);
2132LogicalResult MatMulTOp::verify() {
2144 FailureOr<int64_t> maybeAZp = getAZeroPoint();
2145 if (succeeded(maybeAZp) && verifyAZeroPoint(*maybeAZp).failed())
2148 FailureOr<int64_t> maybeBZp = getBZeroPoint();
2149 if (succeeded(maybeBZp) && verifyBZeroPoint(*maybeBZp).failed())
2155LogicalResult tosa::MatmulTBlockScaledOp::inferReturnTypeComponents(
2156 MLIRContext *context, ::std::optional<Location> location,
2157 MatmulTBlockScaledOp::Adaptor adaptor,
2161 const auto aDataShape = cast<ShapedType>(adaptor.getAData().getType());
2162 if (aDataShape.hasRank()) {
2163 outShape[0] = aDataShape.getDimSize(0);
2164 outShape[1] = aDataShape.getDimSize(1);
2167 const auto aScaleShape = cast<ShapedType>(adaptor.getAScale().getType());
2168 if (aScaleShape.hasRank()) {
2169 outShape[0] = ShapedType::isDynamic(outShape[0]) ? aScaleShape.getDimSize(0)
2171 outShape[1] = ShapedType::isDynamic(outShape[1]) ? aScaleShape.getDimSize(1)
2176 const auto bDataShape = cast<ShapedType>(adaptor.getBData().getType());
2177 if (bDataShape.hasRank()) {
2178 const int64_t bDataBatchSize = bDataShape.getDimSize(0);
2179 if (bDataBatchSize != 1)
2181 ShapedType::isDynamic(outShape[0]) ? bDataBatchSize : outShape[0];
2182 outShape[2] = bDataShape.getDimSize(1);
2185 const auto bScaleShape = cast<ShapedType>(adaptor.getBScale().getType());
2186 if (bScaleShape.hasRank()) {
2187 const int64_t bScaleBatchSize = bScaleShape.getDimSize(0);
2188 if (bScaleBatchSize != 1)
2190 ShapedType::isDynamic(outShape[0]) ? bScaleBatchSize : outShape[0];
2191 outShape[2] = ShapedType::isDynamic(outShape[2]) ? bScaleShape.getDimSize(1)
2199LogicalResult MatmulTBlockScaledOp::verify() {
2201 const Type aDataType = getAData().getType();
2202 const Type bDataType = getBData().getType();
2208 int64_t N = ShapedType::kDynamic;
2209 int64_t D = ShapedType::kDynamic;
2210 int64_t H = ShapedType::kDynamic;
2213 int64_t multiplesOfC = ShapedType::kDynamic;
2225 "a_scale",
"batch")) ||
2227 "a_scale",
"height")))
2235 "b_data",
"batch")) ||
2237 "b_data",
"channels")))
2245 "b_scale",
"batch")) ||
2247 "b_scale",
"width")) ||
2255 if (ShapedType::isStatic(N) && ShapedType::isStatic(D) && N != D && D != 1)
2256 return emitOpError(
"expect B matrix batch size to be broadcast compatible "
2258 << D <<
" vs N=" << N;
2261 const uint32_t blockSize = BlockSizeAttr::getBlockSizeValue(
getBlockSize());
2262 if (blockSize != BlockSizeAttr::getBlockSizeValue(BlockSize::BLOCK_SIZE_32))
2263 return emitOpError(
"expect block size to be 32, got ") << blockSize;
2264 if (ShapedType::isStatic(C) && C % blockSize != 0)
2265 return emitOpError(
"expect C to be a multiple of block size, got C=")
2266 <<
C <<
", block_size=" << blockSize;
2269 if (ShapedType::isStatic(C) && ShapedType::isStatic(multiplesOfC) &&
2270 multiplesOfC != C / blockSize)
2272 "expect scale operands dimension 2 to equal C/block_size (")
2273 <<
C <<
"/" << blockSize <<
")" <<
", got " << multiplesOfC;
2276 N = ShapedType::isDynamic(N) ? D : N;
2278 const auto outputType = cast<ShapedType>(getResult().
getType());
2279 if (outputType.hasRank() &&
2284 opError <<
" to be compatible with expected output shape ";
2292LogicalResult tosa::PadOp::inferReturnTypeComponents(
2293 MLIRContext *context, ::std::optional<Location> location,
2294 PadOp::Adaptor adaptor,
2296 ShapeAdaptor inputShape(adaptor.getInput1().getType());
2298 cast<tosa::shapeType>(adaptor.getPadding().getType()).getRank();
2303 if (!inputShape.hasRank()) {
2304 outputShape.resize(paddingRank / 2, ShapedType::kDynamic);
2313 outputShape.resize(inputShape.getRank(), ShapedType::kDynamic);
2318 outputShape.reserve(inputShape.getRank());
2319 for (
int i = 0, s = inputShape.getRank(); i < s; i++) {
2320 if (inputShape.isDynamicDim(i)) {
2321 outputShape.push_back(ShapedType::kDynamic);
2324 auto padFront = paddingValues[i * 2];
2325 auto padBack = paddingValues[i * 2 + 1];
2326 outputShape.push_back(inputShape.getDimSize(i) + padFront + padBack);
2333LogicalResult tosa::PadOp::verify() {
2340 if (
auto padConst = getPadConst()) {
2348 RankedTensorType inputType =
2349 llvm::dyn_cast<RankedTensorType>(getInput1().
getType());
2350 RankedTensorType outputType =
2351 llvm::dyn_cast<RankedTensorType>(getOutput().
getType());
2352 if (!inputType || !outputType)
2359 auto inputRank = inputType.getRank();
2360 auto paddingRank = cast<tosa::shapeType>(getPadding().
getType()).getRank();
2361 if (paddingRank != inputRank * 2)
2362 return emitOpError() <<
"padding tensor must have " << inputRank
2363 <<
" * 2 = " << inputRank * 2 <<
" elements, but got "
2369LogicalResult tosa::SliceOp::inferReturnTypeComponents(
2370 MLIRContext *context, ::std::optional<Location> location,
2371 SliceOp::Adaptor adaptor,
2380 auto rank = cast<tosa::shapeType>(adaptor.getSize().getType()).getRank();
2388 ShapeAdaptor inputShape(adaptor.getInput1().getType());
2391 if (inputShape.hasRank()) {
2392 for (
size_t i = 0; i < size.size(); i++) {
2393 if (size[i] != 0 && size[i] >= -1 && start[i] >= 0 &&
2394 (ShapedType::isDynamic(inputShape.getDimSize(i)) ||
2395 start[i] < inputShape.getDimSize(i))) {
2397 if (ShapedType::isDynamic(inputShape.getDimSize(i))) {
2400 outputShape[i] = size[i];
2404 if (size[i] == -1) {
2405 outputShape[i] = inputShape.getDimSize(i) - start[i];
2406 }
else if (start[i] + size[i] <= inputShape.getDimSize(i)) {
2408 outputShape[i] = size[i];
2420LogicalResult tosa::SliceOp::verify() {
2421 const Value input = getInput1();
2422 const Value output = getOutput();
2428 const Value start = getStart();
2429 const Value size = getSize();
2433 if (inputShape.hasRank()) {
2434 const auto inputRank = inputShape.getRank();
2435 if (outputShape.hasRank() && inputRank != outputShape.getRank())
2437 "expect input1 and output to have the same ranks, got ")
2438 << inputRank <<
" and " << outputShape.getRank();
2440 const auto startShapeRank =
2441 llvm::cast<tosa::shapeType>(start.
getType()).getRank();
2442 if (inputRank != startShapeRank)
2443 return emitOpError(
"length of start is not equal to rank of input shape");
2445 const auto sizeShapeRank =
2446 llvm::cast<tosa::shapeType>(size.
getType()).getRank();
2447 if (inputRank != sizeShapeRank)
2448 return emitOpError(
"length of size is not equal to rank of input shape");
2454LogicalResult tosa::MulOp::inferReturnTypeComponents(
2455 MLIRContext *context, ::std::optional<Location> location,
2470LogicalResult tosa::MulOp::verify() {
2471 const Value output = getOutput();
2476 if (
auto resIntType = dyn_cast<IntegerType>(resElemType)) {
2477 IntegerType lhsIntType =
2479 IntegerType rhsIntType =
2481 if (!lhsIntType || !rhsIntType || lhsIntType != rhsIntType)
2482 return emitOpError(
"requires the same element type for all operands");
2487 if (lhsIntType.getWidth() > resIntType.getWidth())
2488 return emitOpError(
"invalid data type size for operands or result");
2493 for (
int i = 0; i < 2; ++i) {
2496 "requires the same element type for all operands and results");
2503 TypeRange operandTypes = getOperandTypes();
2504 ShapedType aType = cast<ShapedType>(operandTypes[0]);
2505 ShapedType bType = cast<ShapedType>(operandTypes[1]);
2507 const bool aHasRank = aType.hasRank();
2508 const bool bHasRank = bType.hasRank();
2510 bool hasExpectedOutputShape =
false;
2513 if (aHasRank && bHasRank) {
2514 const int64_t aRank = aType.getRank();
2515 const int64_t bRank = bType.getRank();
2517 return emitOpError(
"a and b operands don't have matching ranks, got ")
2518 << aRank <<
" and " << bRank;
2522 aType.getShape(), bType.getShape(), expectedOutputShape))
2523 return emitOpError(
"a and b operands don't have broadcast-compatible "
2525 << aType <<
" and " << bType;
2526 hasExpectedOutputShape =
true;
2529 ShapedType resultType = cast<ShapedType>(output.
getType());
2530 if (!resultType.hasRank())
2533 const int64_t resultRank = resultType.getRank();
2534 if (aHasRank && resultRank != aType.getRank())
2535 return emitOpError(
"result type has different rank than a, got ")
2536 << resultRank <<
" vs " << aType.getRank();
2537 if (bHasRank && resultRank != bType.getRank())
2538 return emitOpError(
"result type has different rank than b, got ")
2539 << resultRank <<
" vs " << bType.getRank();
2541 if (hasExpectedOutputShape &&
2543 expectedOutputShape)))
2549LogicalResult tosa::TableOp::inferReturnTypeComponents(
2550 MLIRContext *context, ::std::optional<Location> location,
2551 TableOp::Adaptor adaptor,
2553 ShapeAdaptor inputShape(adaptor.getInput1().getType());
2555 if (!inputShape.hasRank()) {
2560 inferredReturnShapes.resize(1);
2561 inputShape.getDims(inferredReturnShapes[0]);
2565LogicalResult tosa::TableOp::verify() {
2566 const TensorType inputType = getInput1().getType();
2567 const TensorType outputType = getOutput().getType();
2576 auto inputDims = inputType.
getShape();
2577 auto outputDims = outputType.
getShape();
2578 for (
auto it : llvm::enumerate(llvm::zip(inputDims, outputDims))) {
2580 auto [inputDim, outputDim] = it.value();
2581 if (ShapedType::isStatic(outputDim) && outputDim != inputDim) {
2582 return emitOpError() <<
"dim(result, " << dim <<
") = " << outputDim
2583 <<
" doesn't match dim(input, " << dim
2584 <<
") = " << inputDim;
2597 llvm::map_to_vector(multiplesAttr.getValues<APInt>(),
2598 [](
const APInt &val) { return val.getSExtValue(); });
2602LogicalResult tosa::TileOp::inferReturnTypeComponents(
2603 MLIRContext *context, ::std::optional<Location> location,
2604 TileOp::Adaptor adaptor,
2611 cast<tosa::shapeType>(adaptor.getMultiples().getType()).getRank();
2618 ShapeAdaptor inputShape(adaptor.getInput1().getType());
2620 if (!inputShape.hasRank()) {
2621 outputShape.resize(multiples.size(), ShapedType::kDynamic);
2622 inferredReturnShapes.push_back(
2626 if (
static_cast<size_t>(inputShape.getRank()) != multiples.size())
2630 outputShape.reserve(multiples.size());
2631 for (
int i = 0, s = inputShape.getRank(); i < s; i++) {
2632 if (multiples[i] == ShapedType::kDynamic) {
2633 outputShape.push_back(ShapedType::kDynamic);
2635 int64_t dim = inputShape.getDimSize(i);
2636 if (dim != ShapedType::kDynamic)
2637 dim *= multiples[i];
2638 outputShape.push_back(dim);
2646LogicalResult tosa::TileOp::verify() {
2652 ShapedType inputType = llvm::cast<ShapedType>(getInput1().
getType());
2653 ShapedType outputType = llvm::cast<ShapedType>(
getType());
2655 shapeType multiplesType =
2656 llvm::cast<tosa::shapeType>(getMultiples().
getType());
2658 auto multiplesRank = multiplesType.getRank();
2660 if (inputType.hasRank()) {
2661 if (inputType.getRank() != multiplesRank)
2662 return emitOpError(
"expect 'multiples' to have rank ")
2663 << inputType.getRank() <<
" but got " << multiplesRank <<
".";
2664 if (outputType.hasRank() &&
2668 }
else if (outputType.hasRank() && outputType.getRank() != multiplesRank)
2669 return emitOpError(
"expect 'multiples' array to have length ")
2670 << outputType.getRank() <<
" but got " << multiplesRank <<
".";
2673 if (getConstantMultiples(multiples).succeeded() &&
2674 llvm::any_of(multiples, [](
int64_t v) {
return v <= 0 && v != -1; }))
2676 "expect element of 'multiples' to be positive integer or -1.");
2682 if (l.size() != r.size() || l.size() != 1)
2687LogicalResult tosa::ReshapeOp::inferReturnTypeComponents(
2688 MLIRContext *context, ::std::optional<Location> location,
2689 ReshapeOp::Adaptor adaptor,
2691 ShapeAdaptor inputShape(adaptor.getInput1().getType());
2696 auto rank = cast<tosa::shapeType>(adaptor.getShape().getType()).getRank();
2705 if (!inputShape.hasRank() || !inputShape.hasStaticShape()) {
2706 inferredReturnShapes.push_back(
2714 int64_t numElements = inputShape.getNumElements();
2716 for (
auto val : newShapeValue) {
2717 if (ShapedType::isStatic(val)) {
2723 for (
auto &val : newShapeValue) {
2724 if (ShapedType::isDynamic(val))
2725 val = numElements / staticMul;
2728 inferredReturnShapes.push_back(
2733llvm::LogicalResult tosa::ReshapeOp::verify() {
2739 TensorType inputType = getInput1().getType();
2744 return mlir::success();
2748 if (missingDims > 1)
2749 return emitOpError() <<
"expected at most one target dimension to be "
2752 const auto outputType = dyn_cast<RankedTensorType>(
getType());
2756 if ((
int64_t)shapeValues.size() != outputType.getRank())
2757 return emitOpError() <<
"new shape does not match result rank";
2759 for (
auto [newShapeDim, outputShapeDim] :
2760 zip(shapeValues, outputType.getShape())) {
2762 newShapeDim != ShapedType::kDynamic &&
2763 outputShapeDim != ShapedType::kDynamic && newShapeDim != outputShapeDim)
2764 return emitOpError() <<
"new shape is inconsistent with result shape";
2767 return emitOpError() <<
"new shape has invalid tensor dimension size "
2771 if (inputType.hasStaticShape()) {
2772 int64_t inputElementsNum = inputType.getNumElements();
2773 if (outputType.hasStaticShape()) {
2774 int64_t outputElementsNum = outputType.getNumElements();
2775 if (inputElementsNum != outputElementsNum) {
2776 return emitOpError() <<
"cannot reshape " << inputElementsNum
2777 <<
" elements into " << outputElementsNum;
2783 return (dim > 0) ?
acc * dim :
acc;
2785 bool isStaticNewShape =
2786 llvm::all_of(shapeValues, [](
int64_t s) {
return s > 0; });
2787 if ((isStaticNewShape && inputElementsNum != newShapeElementsNum) ||
2788 (!isStaticNewShape && newShapeElementsNum > inputElementsNum)) {
2789 return emitOpError() <<
"cannot reshape " << inputElementsNum
2790 <<
" elements into " << newShapeElementsNum;
2794 return mlir::success();
2797bool tosa::ReshapeBlockScaledOp::isCompatibleReturnTypes(
TypeRange l,
2799 if (l.size() != r.size() || l.size() < 1 || l.size() > 2)
2807LogicalResult tosa::ReshapeBlockScaledOp::inferReturnTypeComponents(
2808 MLIRContext *context, ::std::optional<Location> location,
2809 ReshapeBlockScaledOp::Adaptor adaptor,
2812 const auto numInputs = adaptor.getInput().size();
2813 ShapeAdaptor inputShape(adaptor.getInput()[0].getType());
2816 const auto newShape = adaptor.getNewValueShape();
2818 auto rank = cast<tosa::shapeType>(newShape.getType()).getRank();
2827 const uint32_t blockSize =
2828 BlockSizeAttr::getBlockSizeValue(adaptor.getBlockSize());
2831 if (numInputs == 2) {
2832 newScaleShapeValue.assign(newShapeValue.begin(), newShapeValue.end());
2833 if (!newScaleShapeValue.empty() &&
2834 ShapedType::isStatic(newScaleShapeValue.back()))
2835 newScaleShapeValue.back() /= blockSize;
2838 inferredReturnShapes.push_back(
2840 if (numInputs == 2) {
2842 for (
size_t idx = 0; idx < newShapeValue.size(); idx++) {
2843 if (ShapedType::isDynamic(newScaleShapeValue[idx])) {
2844 newScaleShapeValue[idx] = newShapeValue[idx];
2845 if (idx + 1 == newShapeValue.size())
2846 newScaleShapeValue[idx] /= blockSize;
2857llvm::LogicalResult tosa::ReshapeBlockScaledOp::verify() {
2861 if (inputList.size() == 0)
2862 return emitOpError(
"requires at least one input");
2864 if (inputList.size() > 2)
2865 return emitOpError(
"requires at most two inputs");
2867 if (inputList.size() != outputList.size())
2868 return emitOpError(
"requires number of results to match inputs");
2876 if (inputList.size() == 2 &&
2877 cast<tosa::shapeType>(getNewValueShape().
getType()).getRank() == 0)
2878 return emitOpError(
"requires new shape to have a rank greater than 0");
2880 const auto inputType = llvm::cast<ShapedType>(inputList[0].
getType());
2881 if (!inputType.hasRank())
2883 const uint32_t blockSize = BlockSizeAttr::getBlockSizeValue(
getBlockSize());
2885 if (inputList.size() == 2) {
2886 if (blockSize != BlockSizeAttr::getBlockSizeValue(BlockSize::BLOCK_SIZE_32))
2887 return emitOpError(
"expect block size to be 32, got ") << blockSize;
2888 if (llvm::any_of(inputList, [](
Value v) {
2889 const auto input = cast<ShapedType>(v.
getType());
2890 return input.hasRank() && input.getRank() == 0;
2893 "requires all input shapes have a rank greater than 0");
2894 if (llvm::any_of(outputList, [](
Value v) {
2895 const auto output = cast<ShapedType>(v.
getType());
2896 return output.hasRank() && output.getRank() == 0;
2899 "requires all result shapes have a rank greater than 0");
2907 const auto inputScaleType = llvm::cast<ShapedType>(inputList[1].
getType());
2908 if (inputScaleType.hasRank()) {
2909 if (inputType.getRank() != inputScaleType.getRank())
2910 return emitOpError(
"input shapes do not have same rank");
2913 for (
auto dimIdx = 0; dimIdx < inputType.getRank() - 1; dimIdx++) {
2914 const int64_t inputValueDim = inputType.getDimSize(dimIdx);
2915 const int64_t inputScaleDim = inputScaleType.getShape()[dimIdx];
2916 if (ShapedType::isStatic(inputValueDim) &&
2917 ShapedType::isStatic(inputScaleDim) &&
2918 inputValueDim != inputScaleDim)
2919 return emitOpError(
"input shapes for data and scale do not match on "
2926 inputType.getDimSize(inputType.getRank() - 1);
2927 if (ShapedType::isStatic(lastValueDim)) {
2928 if (lastValueDim % blockSize != 0)
2929 return emitOpError(
"expect last dimension of input_data (")
2930 << lastValueDim <<
") to be divisible by block_size ("
2931 << blockSize <<
")";
2934 inputScaleType.getDimSize(inputScaleType.getRank() - 1);
2936 if (ShapedType::isStatic(lastScaleDim) &&
2937 lastScaleDim != lastValueDim / blockSize)
2938 return emitOpError(
"expect last dimension of scale_data (")
2939 << lastScaleDim <<
") to be " << lastValueDim <<
"/"
2944 if (blockSize != BlockSizeAttr::getBlockSizeValue(BlockSize::BLOCK_SIZE_1))
2945 return emitOpError(
"expect block size to be 1, got ") << blockSize;
2953 return mlir::success();
2956 if (inputList.size() == 2) {
2957 const int64_t lastShapeDim = shapeValues.back();
2958 if (ShapedType::isStatic(lastShapeDim) && lastShapeDim % blockSize != 0)
2959 return emitOpError(
"expect last dimension of new shape (")
2960 << lastShapeDim <<
") to be divisible by block_size (" << blockSize
2964 const auto outputType = llvm::cast<ShapedType>(outputList[0].
getType());
2965 if (!outputType.hasRank())
2968 if (
static_cast<int64_t>(shapeValues.size()) != outputType.getRank())
2969 return emitOpError() <<
"result does not match new shape rank";
2971 for (
auto [newShapeDim, outputShapeDim] :
2972 zip(shapeValues, outputType.getShape())) {
2973 if (ShapedType::isStatic(newShapeDim) &&
2974 ShapedType::isStatic(outputShapeDim) && newShapeDim != outputShapeDim)
2975 return emitOpError() <<
"result shape is inconsistent with new shape";
2978 if (outputList.size() == 2) {
2982 scaleShapeValues.back() /= blockSize;
2984 const auto outputScaleType =
2985 llvm::cast<ShapedType>(outputList[1].
getType());
2986 if (outputScaleType.hasRank()) {
2987 if ((
int64_t)scaleShapeValues.size() != outputScaleType.getRank())
2988 return emitOpError() <<
"result scale does not match new shape rank";
2990 for (
auto [newScaleShapeDim, outputScaleShapeDim] :
2991 zip(scaleShapeValues, outputScaleType.getShape())) {
2992 if (ShapedType::isStatic(newScaleShapeDim) &&
2993 ShapedType::isStatic(outputScaleShapeDim) &&
2994 newScaleShapeDim != outputScaleShapeDim)
2995 return emitOpError()
2996 <<
"result scale shape is inconsistent with new shape";
3001 if (inputType.hasStaticShape()) {
3002 int64_t inputElementsNum = inputType.getNumElements();
3003 if (outputType.hasStaticShape()) {
3004 int64_t outputElementsNum = outputType.getNumElements();
3005 if (inputElementsNum != outputElementsNum) {
3006 return emitOpError() <<
"cannot reshape " << inputElementsNum
3007 <<
" elements into " << outputElementsNum;
3013 return (dim > 0) ?
acc * dim :
acc;
3015 bool isStaticNewShape =
3016 llvm::all_of(shapeValues, [](
int64_t s) {
return s > 0; });
3017 if ((isStaticNewShape && inputElementsNum != newShapeElementsNum) ||
3018 (!isStaticNewShape && newShapeElementsNum > inputElementsNum)) {
3019 return emitOpError() <<
"cannot reshape " << inputElementsNum
3020 <<
" elements into " << newShapeElementsNum;
3024 return mlir::success();
3031 ElementsAttr zpAttr;
3036 Type zpElemType = zpAttr.getElementType();
3038 if (llvm::isa<FloatType>(zpElemType)) {
3039 if (zpAttr.getValues<APFloat>()[0].isZero()) {
3046 if (llvm::isa<IntegerType>(zpElemType)) {
3048 return zpAttr.getValues<APInt>()[0].getSExtValue();
3049 return zpAttr.getValues<APInt>()[0].getZExtValue();
3056template <
typename T>
3058 const std::string &operand) {
3061 if (!zpElemType.
isInteger(8) && zp != 0) {
3063 std::string lower = operand;
3064 llvm::transform(lower, lower.begin(), ::tolower);
3065 return op.emitOpError()
3066 << lower <<
" zero point must be zero for non-int8 integer types";
3074 const std::string &operand) {
3075 bool isInputZp = (operand ==
"Input");
3077 bool tensorUnsigned =
3078 isInputZp ? op.getInputUnsigned() : op.getOutputUnsigned();
3079 StringRef tensorName = isInputZp ?
"input" :
"output";
3085 !(zpElemType.
isInteger(16) && tensorUnsigned)) {
3086 return op.emitOpError()
3087 <<
"expect " << tensorName <<
"_zp of 0, got " << zp;
3089 if (zpElemType.
isInteger(16) && tensorUnsigned && zp != 32768) {
3090 return op.emitOpError() <<
"expect " << tensorName
3091 <<
"_zp of 0 or 32768 for unsigned int16 "
3092 << tensorName <<
", got " << zp;
3099#define ZERO_POINT_HELPER(OP, OPERAND_NAME, SIGN_EXTEND) \
3100 FailureOr<int64_t> tosa::OP::get##OPERAND_NAME##ZeroPoint() { \
3101 return getZeroPoint(get##OPERAND_NAME##Zp(), SIGN_EXTEND); \
3103 LogicalResult tosa::OP::verify##OPERAND_NAME##ZeroPoint(int64_t zp) { \
3104 return verifyZeroPoint(*this, get##OPERAND_NAME##Zp(), zp, #OPERAND_NAME); \
3127#undef ZERO_POINT_HELPER
3129LogicalResult tosa::TransposeOp::inferReturnTypeComponents(
3130 MLIRContext *context, ::std::optional<Location> location,
3131 TransposeOp::Adaptor adaptor,
3133 ShapeAdaptor inputShape(adaptor.getInput1().getType());
3142 const auto inputRank = inputShape.
getRank();
3146 if (adaptor.getPerms().size() !=
static_cast<size_t>(inputRank)) {
3152 if (inputRank == 0) {
3158 bool allTheSame =
true;
3159 for (
int i = 1, s = inputRank; i < s; i++) {
3169 outputShape.resize(inputRank, inputShape.
getDimSize(0));
3174 outputShape.resize(inputRank, ShapedType::kDynamic);
3177 if (llvm::any_of(adaptor.getPerms(),
3178 [inputRank](
const auto i) { return i >= inputRank; }))
3181 outputShape.reserve(inputRank);
3182 for (
int i = 0, s = inputRank; i < s; i++) {
3183 outputShape[i] = inputShape.
getDimSize(adaptor.getPerms()[i]);
3190LogicalResult tosa::TransposeOp::verify() {
3202 if (inputShape.hasRank() &&
3203 constantPerms.size() !=
static_cast<size_t>(inputShape.getRank()))
3204 return emitOpError() <<
"expected perms attribute to have size "
3205 << inputShape.getRank()
3206 <<
" (input rank) but got size "
3207 << constantPerms.size();
3209 if (inputShape.hasRank() && outputShape.hasRank() &&
3210 inputShape.getRank() != outputShape.getRank())
3211 return emitOpError()
3212 <<
"expected input tensor rank to equal result tensor rank";
3214 if (outputShape.hasRank() &&
3215 constantPerms.size() !=
static_cast<size_t>(outputShape.getRank()))
3216 return emitOpError() <<
"expected perms attribute to have size "
3217 << outputShape.getRank()
3218 <<
" (output rank) but got size "
3219 << constantPerms.size();
3221 if (!llvm::all_of(constantPerms,
3222 [&constantPerms](int32_t s) {
3224 static_cast<size_t>(s) < constantPerms.size();
3227 constantPerms, [](int32_t v) ->
int64_t {
return v; })))
3228 return emitOpError() <<
"expected valid permutation indices";
3231 constantPerms.back() !=
static_cast<int32_t
>(constantPerms.size()) - 1) {
3232 return emitOpError() <<
"expected no-op permutation on innermost dimension "
3233 "for block scaled input";
3237 if (inputShape.hasStaticShape() && outputShape.hasStaticShape() &&
3238 inputShape.getNumElements() != outputShape.getNumElements())
3239 return emitOpError() <<
"expected input1 and output to have same numbers "
3241 << inputShape.getNumElements() <<
" and "
3242 << outputShape.getNumElements();
3246 if (inputShape.hasRank() && outputShape.hasRank()) {
3247 for (
auto i = 0; i < outputShape.getRank(); i++) {
3248 if (inputShape.isDynamicDim(constantPerms[i]) ||
3249 outputShape.isDynamicDim(i))
3252 if (inputShape.getDimSize(constantPerms[i]) != outputShape.getDimSize(i))
3253 return emitOpError()
3254 <<
"expected output tensor dim " << i <<
" to match "
3255 <<
"input dim " << constantPerms[i] <<
" with value of "
3256 << inputShape.getDimSize(constantPerms[i]);
3263LogicalResult TransposeOp::reifyResultShapes(
3266 const llvm::ArrayRef<int32_t> transposePerms = getPerms();
3268 Value input = getInput1();
3269 auto inputType = cast<TensorType>(input.
getType());
3271 SmallVector<OpFoldResult> returnedDims(inputType.getRank());
3272 for (
auto dim : transposePerms) {
3273 int32_t dimInInput = transposePerms[dim];
3274 if (inputType.isDynamicDim(dimInInput))
3276 tensor::DimOp::create(builder, getLoc(), input, dimInInput)
3280 builder.
getIndexAttr(inputType.getDimSize(dimInInput));
3283 reifiedReturnShapes.emplace_back(std::move(returnedDims));
3287LogicalResult tosa::GatherOp::inferReturnTypeComponents(
3288 MLIRContext *context, ::std::optional<Location> location,
3289 GatherOp::Adaptor adaptor,
3290 SmallVectorImpl<ShapedTypeComponents> &inferredReturnShapes) {
3291 llvm::SmallVector<int64_t> outputShape;
3292 outputShape.resize(3, ShapedType::kDynamic);
3294 ShapeAdaptor valuesShape(adaptor.getValues().getType());
3295 if (valuesShape.hasRank()) {
3296 outputShape[0] = valuesShape.getDimSize(0);
3297 outputShape[2] = valuesShape.getDimSize(2);
3300 ShapeAdaptor indicesShape(adaptor.getIndices().getType());
3301 if (indicesShape.hasRank()) {
3302 if (outputShape[0] == ShapedType::kDynamic)
3303 outputShape[0] = indicesShape.getDimSize(0);
3304 if (outputShape[1] == ShapedType::kDynamic)
3305 outputShape[1] = indicesShape.getDimSize(1);
3308 inferredReturnShapes.push_back(ShapedTypeComponents(outputShape));
3312LogicalResult tosa::RowGatherOp::inferReturnTypeComponents(
3313 MLIRContext *context, ::std::optional<Location> location,
3314 RowGatherOp::Adaptor adaptor,
3315 SmallVectorImpl<ShapedTypeComponents> &inferredReturnShapes) {
3316 llvm::SmallVector<int64_t> outputShape;
3317 outputShape.resize(3, ShapedType::kDynamic);
3319 const ShapeAdaptor valuesShape(adaptor.getValues().getType());
3320 if (valuesShape.hasRank()) {
3321 outputShape[0] = valuesShape.getDimSize(0);
3322 outputShape[2] = valuesShape.getDimSize(2);
3325 const ShapeAdaptor indicesShape(adaptor.getIndices().getType());
3326 if (indicesShape.hasRank()) {
3327 if (outputShape[0] == ShapedType::kDynamic)
3328 outputShape[0] = indicesShape.getDimSize(0);
3330 const FailureOr<int32_t> maybeRowCount =
3332 if (succeeded(maybeRowCount)) {
3333 const int64_t indicesW = indicesShape.getDimSize(1);
3334 if (ShapedType::isStatic(indicesW))
3335 outputShape[1] = indicesW * maybeRowCount.value();
3339 inferredReturnShapes.push_back(ShapedTypeComponents(outputShape));
3343LogicalResult tosa::RowGatherBlockScaledOp::inferReturnTypeComponents(
3344 MLIRContext *context, ::std::optional<Location> location,
3345 RowGatherBlockScaledOp::Adaptor adaptor,
3346 SmallVectorImpl<ShapedTypeComponents> &inferredReturnShapes) {
3347 const auto values = adaptor.getValues();
3351 SmallVector<int64_t> dataShape(3, ShapedType::kDynamic);
3352 const ShapeAdaptor valuesShape(values.front().getType());
3353 if (valuesShape.hasRank()) {
3354 dataShape[0] = valuesShape.getDimSize(0);
3355 dataShape[2] = valuesShape.getDimSize(2);
3358 const ShapeAdaptor indicesShape(adaptor.getIndices().getType());
3359 if (indicesShape.hasRank()) {
3360 if (dataShape[0] == ShapedType::kDynamic)
3361 dataShape[0] = indicesShape.getDimSize(0);
3365 succeeded(rowCount) && rowCount.value() > 0) {
3366 const int64_t indicesW = indicesShape.getDimSize(1);
3367 if (ShapedType::isStatic(indicesW))
3368 dataShape[1] = indicesW * rowCount.value();
3372 inferredReturnShapes.push_back(ShapedTypeComponents(dataShape));
3373 if (values.size() == 1)
3376 SmallVector<int64_t> scaleShape = dataShape;
3377 const uint32_t blockSize =
3378 BlockSizeAttr::getBlockSizeValue(adaptor.getBlockSize());
3379 if (ShapedType::isStatic(dataShape[2]))
3380 scaleShape[2] = dataShape[2] / blockSize;
3382 inferredReturnShapes.push_back(ShapedTypeComponents(scaleShape));
3386LogicalResult tosa::GatherOp::verify() {
3393 const ShapeAdaptor valuesShape(getValues().
getType());
3395 const ShapeAdaptor outputShape(getOutput().
getType());
3397 int64_t n = ShapedType::kDynamic;
3398 int64_t w = ShapedType::kDynamic;
3399 int64_t c = ShapedType::kDynamic;
3401 if (valuesShape.hasRank()) {
3402 n = valuesShape.getDimSize(0);
3403 c = valuesShape.getDimSize(2);
3405 if (indicesShape.hasRank()) {
3406 const int64_t indicesN = indicesShape.getDimSize(0);
3407 w = indicesShape.getDimSize(1);
3408 if (n == ShapedType::kDynamic)
3410 else if (indicesN != ShapedType::kDynamic && n != indicesN)
3411 return emitOpError() <<
"requires indices dimension 0 to have size " << n
3412 <<
", got " << indicesN;
3414 if (outputShape.hasRank()) {
3415 const int64_t outputN = outputShape.getDimSize(0);
3416 const int64_t outputW = outputShape.getDimSize(1);
3417 const int64_t outputC = outputShape.getDimSize(2);
3418 if (n != ShapedType::kDynamic && outputN != ShapedType::kDynamic &&
3420 return emitOpError() <<
"requires output dimension 0 to have size " << n
3421 <<
", got " << outputN;
3423 if (w != ShapedType::kDynamic && outputW != ShapedType::kDynamic &&
3425 return emitOpError() <<
"requires output dimension 1 to have size " << w
3426 <<
", got " << outputW;
3427 if (c != ShapedType::kDynamic && outputC != ShapedType::kDynamic &&
3429 return emitOpError() <<
"requires output dimension 2 to have size " << c
3430 <<
", got " << outputC;
3435LogicalResult tosa::RowGatherOp::verify() {
3440 const FailureOr<int32_t> maybeRowCount =
3442 if (succeeded(maybeRowCount) && maybeRowCount.value() <= 0)
3443 return emitOpError() <<
"requires row_count to be > 0, got "
3444 << maybeRowCount.value();
3446 int64_t n = ShapedType::kDynamic;
3447 int64_t c = ShapedType::kDynamic;
3448 int64_t w = ShapedType::kDynamic;
3450 const ShapeAdaptor valuesShape(getValues().
getType());
3451 if (valuesShape.hasRank()) {
3452 n = valuesShape.getDimSize(0);
3453 c = valuesShape.getDimSize(2);
3457 if (indicesShape.hasRank()) {
3459 "indices",
"batch")))
3461 w = indicesShape.getDimSize(1);
3464 const ShapeAdaptor outputShape(getOutput().
getType());
3465 if (outputShape.hasRank()) {
3467 "output",
"batch")) ||
3469 "output",
"channels")))
3472 if (succeeded(maybeRowCount) && maybeRowCount.value() > 0 &&
3473 ShapedType::isStatic(w)) {
3474 const int64_t expectedOutputRows = w * maybeRowCount.value();
3475 if (ShapedType::isStatic(outputShape.getDimSize(1)) &&
3476 outputShape.getDimSize(1) != expectedOutputRows)
3477 return emitOpError()
3478 <<
"requires output dimension to be equal to "
3479 "indices[1]*row_count ("
3480 << expectedOutputRows <<
"), got " << outputShape.getDimSize(1);
3487LogicalResult tosa::RowGatherBlockScaledOp::verify() {
3488 const OperandRange values = getValues();
3489 const ResultRange output = getOutput();
3490 if (values.empty() || values.size() > 2)
3491 return emitOpError()
3492 <<
"expects values tensor list length to be 1 or 2, got "
3494 if (output.size() != values.size())
3495 return emitOpError()
3496 <<
"expects output tensor list length to match values tensor list "
3498 << output.size() <<
" results for " << values.size()
3499 <<
" input tensors";
3501 const uint32_t blockSize = BlockSizeAttr::getBlockSizeValue(
getBlockSize());
3502 if (values.size() == 1 && blockSize != 1)
3503 return emitOpError()
3504 <<
"requires block_size to be BLOCK_SIZE_1 when values tensor list "
3506 if (values.size() == 2 && blockSize == 1)
3507 return emitOpError()
3508 <<
"requires block_size to not be BLOCK_SIZE_1 when values tensor "
3512 output[0].
getType(),
"values[0]",
3517 "values[1]",
"output[1]")))
3521 succeeded(rowCount) && rowCount.value() <= 0)
3522 return emitOpError() <<
"requires row_count to be > 0, got "
3523 << rowCount.value();
3525 int64_t n = ShapedType::kDynamic;
3526 int64_t k = ShapedType::kDynamic;
3527 int64_t c = ShapedType::kDynamic;
3528 int64_t w = ShapedType::kDynamic;
3529 int64_t multiplesOfC = ShapedType::kDynamic;
3531 const ShapeAdaptor valuesDataShape(values[0].
getType());
3532 if (valuesDataShape.hasRank()) {
3533 n = valuesDataShape.getDimSize(0);
3534 k = valuesDataShape.getDimSize(1);
3535 c = valuesDataShape.getDimSize(2);
3538 if (ShapedType::isStatic(c) && c % blockSize != 0)
3539 return emitOpError() <<
"expects channels of values[0] (" << c
3540 <<
") to be divisible by block_size (" << blockSize
3544 if (indicesShape.hasRank()) {
3546 "indices",
"batch")))
3548 w = indicesShape.getDimSize(1);
3551 const ShapeAdaptor outputDataShape(output[0].
getType());
3552 if (outputDataShape.hasRank()) {
3554 "output[0]",
"batch")) ||
3556 "output[0]",
"channels")))
3560 succeeded(rowCount) && rowCount.value() > 0 &&
3561 ShapedType::isStatic(w)) {
3562 const int64_t expectedOutputRows = w * rowCount.value();
3563 if (ShapedType::isStatic(outputDataShape.getDimSize(1)) &&
3564 outputDataShape.getDimSize(1) != expectedOutputRows)
3565 return emitOpError() <<
"requires output[0] dimension 1 to have size "
3566 << expectedOutputRows <<
", got "
3567 << outputDataShape.getDimSize(1);
3571 if (values.size() == 2) {
3572 const ShapeAdaptor valuesScaleShape(values[1].
getType());
3573 if (valuesScaleShape.hasRank()) {
3575 "values[1]",
"batch")) ||
3577 "values[1]",
"rows")))
3579 multiplesOfC = valuesScaleShape.getDimSize(2);
3582 const ShapeAdaptor outputScaleShape(output[1].
getType());
3583 if (outputScaleShape.hasRank()) {
3585 "output[1]",
"batch")))
3589 succeeded(rowCount) && rowCount.value() > 0 &&
3590 ShapedType::isStatic(w)) {
3591 const int64_t expectedOutputRows = w * rowCount.value();
3592 if (ShapedType::isStatic(outputScaleShape.getDimSize(1)) &&
3593 outputScaleShape.getDimSize(1) != expectedOutputRows)
3594 return emitOpError() <<
"requires output[1] dimension 1 to have size "
3595 << expectedOutputRows <<
", got "
3596 << outputScaleShape.getDimSize(1);
3599 if (ShapedType::isDynamic(multiplesOfC))
3600 multiplesOfC = outputScaleShape.getDimSize(2);
3601 else if (ShapedType::isStatic(outputScaleShape.getDimSize(2)) &&
3602 multiplesOfC != outputScaleShape.getDimSize(2))
3603 return emitOpError()
3604 <<
"expected channels of output[1] to match size "
3605 << multiplesOfC <<
", got " << outputScaleShape.getDimSize(2);
3608 if (ShapedType::isStatic(c) && ShapedType::isStatic(multiplesOfC) &&
3609 multiplesOfC != c / blockSize)
3610 return emitOpError()
3611 <<
"expects channels of scale tensors to equal C/block_size (" << c
3612 <<
"/" << blockSize <<
"), got " << multiplesOfC;
3618LogicalResult tosa::ResizeOp::inferReturnTypeComponents(
3619 MLIRContext *context, ::std::optional<Location> location,
3620 ResizeOp::Adaptor adaptor,
3621 SmallVectorImpl<ShapedTypeComponents> &inferredReturnShapes) {
3622 llvm::SmallVector<int64_t, 4> outputShape;
3623 outputShape.resize(4, ShapedType::kDynamic);
3625 ShapeAdaptor inputShape(adaptor.getInput().getType());
3626 if (!inputShape.hasRank())
3629 outputShape[0] = inputShape.getDimSize(0);
3630 outputShape[3] = inputShape.getDimSize(3);
3631 int64_t inputHeight = inputShape.getDimSize(1);
3632 int64_t inputWidth = inputShape.getDimSize(2);
3634 if ((inputHeight == ShapedType::kDynamic) ||
3635 (inputWidth == ShapedType::kDynamic))
3638 SmallVector<int64_t> scaleInt, offsetInt, borderInt;
3650 if (llvm::any_of(scaleInt, [](int64_t s) {
return s <= 0; }))
3654 const int64_t outputHeight =
3655 (((inputHeight - 1) * scaleInt[0] - offsetInt[0] + borderInt[0]) /
3659 const int64_t outputWidth =
3660 (((inputWidth - 1) * scaleInt[2] - offsetInt[1] + borderInt[1]) /
3664 if (outputHeight < 0 || outputWidth < 0) {
3667 "calculated output height and width must be non-negative, "
3669 outputHeight,
", width = ", outputWidth);
3672 outputShape[1] = outputHeight;
3673 outputShape[2] = outputWidth;
3674 inferredReturnShapes.push_back(ShapedTypeComponents(outputShape));
3678LogicalResult tosa::ResizeOp::verify() {
3681 if (isa<BlockScaledType>(inputElementType) &&
3682 getMode() != ResizeMode::NEAREST_NEIGHBOR)
3683 return emitOpError(
"requires NEAREST_NEIGHBOR mode for block scaled input");
3688LogicalResult tosa::ScatterOp::inferReturnTypeComponents(
3689 MLIRContext *context, ::std::optional<Location> location,
3690 ScatterOp::Adaptor adaptor,
3691 SmallVectorImpl<ShapedTypeComponents> &inferredReturnShapes) {
3692 llvm::SmallVector<int64_t> outputShape;
3693 outputShape.resize(3, ShapedType::kDynamic);
3695 ShapeAdaptor valuesInShape(adaptor.getValuesIn().getType());
3696 if (valuesInShape.hasRank()) {
3697 outputShape[0] = valuesInShape.getDimSize(0);
3698 outputShape[1] = valuesInShape.getDimSize(1);
3699 outputShape[2] = valuesInShape.getDimSize(2);
3702 ShapeAdaptor indicesShape(adaptor.getIndices().getType());
3703 if (indicesShape.hasRank()) {
3704 if (outputShape[0] == ShapedType::kDynamic)
3705 outputShape[0] = indicesShape.getDimSize(0);
3708 ShapeAdaptor inputShape(adaptor.getInput().getType());
3709 if (inputShape.hasRank()) {
3710 if (outputShape[0] == ShapedType::kDynamic)
3711 outputShape[0] = inputShape.getDimSize(0);
3712 if (outputShape[2] == ShapedType::kDynamic)
3713 outputShape[2] = inputShape.getDimSize(2);
3716 inferredReturnShapes.push_back(ShapedTypeComponents(outputShape));
3720LogicalResult tosa::ScatterOp::verify() {
3730 const ShapeAdaptor valuesInShape(getValuesIn().
getType());
3732 const ShapeAdaptor inputShape(getInput().
getType());
3733 const ShapeAdaptor outputShape(getValuesOut().
getType());
3735 int64_t n = ShapedType::kDynamic;
3736 int64_t k = ShapedType::kDynamic;
3737 int64_t w = ShapedType::kDynamic;
3738 int64_t c = ShapedType::kDynamic;
3739 if (valuesInShape.hasRank()) {
3740 n = valuesInShape.getDimSize(0);
3741 k = valuesInShape.getDimSize(1);
3742 c = valuesInShape.getDimSize(2);
3744 if (indicesShape.hasRank()) {
3745 const int64_t indicesN = indicesShape.getDimSize(0);
3746 w = indicesShape.getDimSize(1);
3747 if (n == ShapedType::kDynamic)
3749 else if (indicesN != ShapedType::kDynamic && n != indicesN)
3750 return emitOpError() <<
"requires indices dimension 0 to have size " << n
3751 <<
", got " << indicesN;
3753 if (inputShape.hasRank()) {
3754 const int64_t inputN = inputShape.getDimSize(0);
3755 const int64_t inputW = inputShape.getDimSize(1);
3756 const int64_t inputC = inputShape.getDimSize(2);
3757 if (n == ShapedType::kDynamic)
3759 else if (inputN != ShapedType::kDynamic && n != inputN)
3760 return emitOpError() <<
"requires input dimension 0 to have size " << n
3761 <<
", got " << inputN;
3762 if (w == ShapedType::kDynamic)
3764 else if (inputW != ShapedType::kDynamic && w != inputW)
3765 return emitOpError() <<
"requires input dimension 1 to have size " << w
3766 <<
", got " << inputW;
3768 if (c == ShapedType::kDynamic)
3770 else if (inputC != ShapedType::kDynamic && c != inputC)
3771 return emitOpError() <<
"requires input dimension 2 to have size " << c
3772 <<
", got " << inputC;
3774 if (outputShape.hasRank()) {
3775 const int64_t outputN = outputShape.getDimSize(0);
3776 const int64_t outputK = outputShape.getDimSize(1);
3777 const int64_t outputC = outputShape.getDimSize(2);
3778 if (n != ShapedType::kDynamic && outputN != ShapedType::kDynamic &&
3780 return emitOpError() <<
"requires values_out dimension 0 to have size "
3781 << n <<
", got " << outputN;
3782 if (k == ShapedType::kDynamic)
3784 else if (outputK != ShapedType::kDynamic && k != outputK)
3785 return emitOpError() <<
"requires values_out dimension 1 to have size "
3786 << k <<
", got " << outputK;
3787 if (c != ShapedType::kDynamic && outputC != ShapedType::kDynamic &&
3789 return emitOpError() <<
"requires values_out dimension 2 to have size "
3790 << c <<
", got " << outputC;
3792 if (k != ShapedType::kDynamic && w != ShapedType::kDynamic && !(k >= w))
3793 return emitOpError() <<
"requires dimensions K >= W, got K=" << k
3802 int64_t axisVal = axis.getValue().getSExtValue();
3803 if (!operandShape.
hasRank() || operandShape.
getRank() <= axisVal) {
3809 operandShape.
getDims(outputShape);
3810 outputShape[axisVal] = 1;
3815#define COMPATIBLE_RETURN_TYPES(OP) \
3816 bool OP::isCompatibleReturnTypes(TypeRange l, TypeRange r) { \
3817 if (l.size() != r.size() || l.size() != 1) \
3819 if (getElementTypeOrSelf(l[0]) != getElementTypeOrSelf(r[0])) \
3821 return succeeded(verifyCompatibleShape(l[0], r[0])); \
3824#define REDUCE_SHAPE_INFER(OP) \
3825 LogicalResult OP::inferReturnTypeComponents( \
3826 MLIRContext *context, ::std::optional<Location> location, \
3827 OP::Adaptor adaptor, \
3828 SmallVectorImpl<ShapedTypeComponents> &inferredReturnShapes) { \
3830 llvm::cast<TensorType>(adaptor.getInput().getType()).getElementType(); \
3831 ShapeAdaptor inputShape(adaptor.getInput().getType()); \
3832 const Properties &prop = adaptor.getProperties(); \
3833 return ReduceInferReturnTypes(inputShape, inputType, prop.axis, \
3834 inferredReturnShapes); \
3836 COMPATIBLE_RETURN_TYPES(OP)
3844#undef REDUCE_SHAPE_INFER
3846#undef COMPATIBLE_RETURN_TYPES
3848template <
typename T>
3851 TensorType inputType = op.getInput().getType();
3852 TensorType outputType = op.getOutput().getType();
3853 int32_t reduceAxis = op.getAxis();
3855 if (reduceAxis < 0) {
3856 op.emitOpError(
"reduce axis must not be negative");
3860 int64_t inputRank = inputType.getRank();
3863 if (reduceAxis >= inputRank && (reduceAxis != 0 || inputRank != 0)) {
3864 op.emitOpError(
"expect input tensor rank (")
3865 << inputRank <<
") to be larger than reduce axis (" << reduceAxis
3871 int64_t outputRank = outputType.getRank();
3872 if (inputType.
hasRank() && outputRank != inputType.getRank()) {
3874 "expect output tensor rank to be equal to input tensor rank");
3877 if (reduceAxis >= outputRank && (reduceAxis != 0 || outputRank != 0)) {
3878 op.emitOpError(
"expect output tensor rank (")
3879 << outputRank <<
") to be larger than reduce axis (" << reduceAxis
3885 if (outputRank != 0) {
3886 auto outputShape = outputType.
getShape();
3887 if (!outputType.isDynamicDim(reduceAxis) &&
3888 outputShape[reduceAxis] != 1) {
3889 op.emitOpError(
"expect reduced dimension size to be 1, got ")
3890 << outputShape[reduceAxis];
3898LogicalResult tosa::ReduceAllOp::verify() {
return verifyReduceOp(*
this); }
3899LogicalResult tosa::ReduceAnyOp::verify() {
return verifyReduceOp(*
this); }
3900LogicalResult tosa::ReduceMaxOp::verify() {
return verifyReduceOp(*
this); }
3901LogicalResult tosa::ReduceMinOp::verify() {
return verifyReduceOp(*
this); }
3902LogicalResult tosa::ReduceProductOp::verify() {
return verifyReduceOp(*
this); }
3903LogicalResult tosa::ReduceSumOp::verify() {
return verifyReduceOp(*
this); }
3917#define NARY_SHAPE_INFER(OP) \
3918 LogicalResult OP::inferReturnTypeComponents( \
3919 MLIRContext *context, ::std::optional<Location> location, \
3920 ValueShapeRange operands, DictionaryAttr attributes, \
3921 PropertyRef properties, RegionRange regions, \
3922 SmallVectorImpl<ShapedTypeComponents> &inferredReturnShapes) { \
3923 return NAryInferReturnTypes(operands, inferredReturnShapes); \
3963#undef PRED_SHAPE_INFER
3965LogicalResult tosa::NegateOp::inferReturnTypeComponents(
3966 MLIRContext *context, ::std::optional<Location> location,
3967 NegateOp::Adaptor adaptor,
3969 ShapeAdaptor inputShape(adaptor.getInput1().getType());
3974LogicalResult tosa::NegateOp::verify() {
3976 const Type input1Type = getInput1().getType();
3977 const Type outputType = getOutput().getType();
3982 const SmallVector<Type, 2> types = {input1Type, outputType};
3984 return emitOpError() <<
"requires the same shape for input1 and output";
3987 const Type input1ZpEType =
3989 if (input1EType != input1ZpEType) {
3990 return emitOpError(
"expect both input1 and its zero point are the same "
3991 "element type, got ")
3992 << input1EType <<
" and " << input1ZpEType;
3995 const Type outputZpEType =
3997 if (outputEType != outputZpEType) {
3998 return emitOpError(
"expect both output and its zero point are the same "
3999 "element type, got ")
4000 << outputEType <<
" and " << outputZpEType;
4003 FailureOr<int64_t> maybeIZp = getInput1ZeroPoint();
4004 if (succeeded(maybeIZp) && verifyInput1ZeroPoint(*maybeIZp).failed())
4007 FailureOr<int64_t> maybeOZp = getOutputZeroPoint();
4008 if (succeeded(maybeOZp) && verifyOutputZeroPoint(*maybeOZp).failed())
4019 outputShape.resize(4, ShapedType::kDynamic);
4034 if (ShapedType::isStatic(height)) {
4035 int64_t padded = height + pad[0] + pad[1] - kernel[0];
4036 outputShape[1] = padded / stride[0] + 1;
4039 if (ShapedType::isStatic(width)) {
4040 int64_t padded = width + pad[2] + pad[3] - kernel[1];
4041 outputShape[2] = padded / stride[1] + 1;
4048template <
typename AdaptorT>
4054 if (ShapedType::isDynamic(current))
4055 current = candidate;
4064 : adaptor(adaptor) {}
4068 const ShapeAdaptor inputShape(adaptor.getInput().getType());
4076 outputShape[0] = outputBatch;
4077 inputSpatial[0] = inputHeight;
4078 inputSpatial[1] = inputWidth;
4083 const ShapeAdaptor weightShape(adaptor.getWeight().getType());
4091 outputShape[3] = outputChannels;
4092 weightSpatial[0] = kernelHeight;
4093 weightSpatial[1] = kernelWidth;
4102 padValues.assign(adaptor.getPad().begin(), adaptor.getPad().end());
4103 strideValues.assign(adaptor.getStride().begin(), adaptor.getStride().end());
4104 dilationValues.assign(adaptor.getDilation().begin(),
4105 adaptor.getDilation().end());
4110 Conv2DOp::Adaptor adaptor;
4118 : adaptor(adaptor) {}
4122 const ShapeAdaptor inputDataShape(adaptor.getInputData().getType());
4123 if (inputDataShape.
hasRank()) {
4128 outputShape[0] = outputBatch;
4129 inputSpatial[0] = inputHeight;
4130 inputSpatial[1] = inputWidth;
4133 const ShapeAdaptor inputScaleShape(adaptor.getInputScale().getType());
4134 if (!inputScaleShape.
hasRank())
4148 const ShapeAdaptor weightDataShape(adaptor.getWeightData().getType());
4149 if (weightDataShape.
hasRank()) {
4154 outputShape[3] = outputChannels;
4155 weightSpatial[0] = kernelHeight;
4156 weightSpatial[1] = kernelWidth;
4159 const ShapeAdaptor weightScaleShape(adaptor.getWeightScale().getType());
4160 if (!weightScaleShape.
hasRank())
4189 Conv2DBlockScaledOp::Adaptor adaptor;
4197 : adaptor(adaptor) {}
4201 const ShapeAdaptor inputShape(adaptor.getInput().getType());
4210 outputShape[0] = outputBatch;
4211 inputSpatial[0] = inputDepth;
4212 inputSpatial[1] = inputHeight;
4213 inputSpatial[2] = inputWidth;
4218 const ShapeAdaptor weightShape(adaptor.getWeight().getType());
4227 outputShape[4] = outputChannels;
4228 weightSpatial[0] = kernelDepth;
4229 weightSpatial[1] = kernelHeight;
4230 weightSpatial[2] = kernelWidth;
4239 padValues.assign(adaptor.getPad().begin(), adaptor.getPad().end());
4240 strideValues.assign(adaptor.getStride().begin(), adaptor.getStride().end());
4241 dilationValues.assign(adaptor.getDilation().begin(),
4242 adaptor.getDilation().end());
4247 Conv3DOp::Adaptor adaptor;
4250template <
typename AdaptorT>
4256 ShapedType::kDynamic);
4258 ShapedType::kDynamic);
4260 ShapedType::kDynamic);
4262 convShapeAdaptor.inferInputShape(outputShape, inputSpatial);
4263 convShapeAdaptor.inferWeightShape(outputShape, weightSpatial);
4265 const ShapeAdaptor biasShape = adaptor.getBias().getType();
4268 if (biasSize != 1) {
4269 const size_t outputChannelDim = convShapeAdaptor.getOutputRank() - 1;
4270 outputShape[outputChannelDim] =
4271 ShapedType::isDynamic(outputShape[outputChannelDim])
4273 : outputShape[outputChannelDim];
4280 if (failed(convShapeAdaptor.getSpatialParameters(padValues, strideValues,
4286 for (
int64_t dim = 0; dim < convShapeAdaptor.getNumSpatialDims(); ++dim) {
4287 if (!ShapedType::isStatic(inputSpatial[dim]) ||
4288 !ShapedType::isStatic(weightSpatial[dim]))
4291 inputSpatial[dim] + padValues[2 * dim] + padValues[2 * dim + 1];
4293 (weightSpatial[dim] - 1) * dilationValues[dim] + 1;
4294 const int64_t unstridedResult = inputSize - filterSize + 1;
4295 outputShape[dim + 1] = (unstridedResult - 1) / strideValues[dim] + 1;
4302LogicalResult Conv2DOp::inferReturnTypeComponents(
4303 MLIRContext *context, ::std::optional<Location> location,
4304 Conv2DOp::Adaptor adaptor,
4305 SmallVectorImpl<ShapedTypeComponents> &inferredReturnShapes) {
4309LogicalResult Conv2DOp::verify() {
4315LogicalResult Conv2DBlockScaledOp::inferReturnTypeComponents(
4316 MLIRContext *context, ::std::optional<Location> location,
4317 Conv2DBlockScaledOp::Adaptor adaptor,
4318 SmallVectorImpl<ShapedTypeComponents> &inferredReturnShapes) {
4322LogicalResult Conv2DBlockScaledOp::verify() {
4324 getWeightData().
getType(),
"input_data",
4327 getWeightScale().
getType(),
"input_scale",
4330 getOutput().
getType(),
"bias",
"output")))
4334 int64_t N = ShapedType::kDynamic;
4335 int64_t IH = ShapedType::kDynamic;
4336 int64_t IW = ShapedType::kDynamic;
4337 int64_t IC = ShapedType::kDynamic;
4338 int64_t multiplesOfIC = ShapedType::kDynamic;
4339 int64_t OC = ShapedType::kDynamic;
4340 int64_t KH = ShapedType::kDynamic;
4341 int64_t KW = ShapedType::kDynamic;
4343 const ShapeAdaptor inputDataShape(getInputData().
getType());
4344 if (inputDataShape.hasRank()) {
4345 N = inputDataShape.getDimSize(0);
4346 IH = inputDataShape.getDimSize(1);
4347 IW = inputDataShape.getDimSize(2);
4348 IC = inputDataShape.getDimSize(3);
4351 const ShapeAdaptor inputScaleShape(getInputScale().
getType());
4352 if (inputScaleShape.hasRank()) {
4354 "input_scale",
"batch size")) ||
4356 "input_scale",
"input height")) ||
4358 "input_scale",
"input width")))
4360 multiplesOfIC = inputScaleShape.getDimSize(3);
4363 const ShapeAdaptor weightDataShape(getWeightData().
getType());
4364 if (weightDataShape.hasRank()) {
4365 OC = weightDataShape.getDimSize(0);
4366 KH = weightDataShape.getDimSize(1);
4367 KW = weightDataShape.getDimSize(2);
4369 "weight_data",
"input channels")))
4373 const ShapeAdaptor weightScaleShape(getWeightScale().
getType());
4374 if (weightScaleShape.hasRank()) {
4376 "weight_scale",
"output channels")) ||
4378 "weight_scale",
"kernel height")) ||
4380 "weight_scale",
"kernel width")) ||
4382 weightScaleShape.getDimSize(3),
4383 "weight_scale",
"input channel blocks")))
4387 const uint32_t blockSize = BlockSizeAttr::getBlockSizeValue(
getBlockSize());
4388 if (blockSize != BlockSizeAttr::getBlockSizeValue(BlockSize::BLOCK_SIZE_32))
4389 return emitOpError(
"expect block size to be 32, got ") << blockSize;
4391 if (ShapedType::isStatic(IC) && IC % blockSize != 0)
4392 return emitOpError(
"expect IC to be a multiple of block size, got IC=")
4393 << IC <<
", block_size=" << blockSize;
4396 if (ShapedType::isStatic(IC) && ShapedType::isStatic(multiplesOfIC) &&
4397 multiplesOfIC != IC / blockSize)
4399 "expect scale operands dimension 2 to equal IC/block_size (")
4400 << IC <<
"/" << blockSize <<
")"
4401 <<
", got " << multiplesOfIC;
4404 SmallVector<int64_t> padValues;
4406 if (llvm::any_of(padValues, [](int64_t p) {
return p < 0; }))
4407 return emitOpError(
"expect all padding values to be >= 0, got ")
4411 SmallVector<int64_t> strideValues;
4413 if (llvm::any_of(strideValues, [](int64_t s) {
return s < 1; }))
4414 return emitOpError(
"expect all stride values to be >= 1, got ")
4418 SmallVector<int64_t> dilationValues;
4421 if (llvm::any_of(dilationValues, [](int64_t d) {
return d < 1; }))
4422 return emitOpError(
"expect all dilation values to be >= 1, got ")
4427 const ShapeAdaptor outputShape(getOutput().
getType());
4428 if (!padValues.empty() && !strideValues.empty() && !dilationValues.empty() &&
4429 outputShape.hasRank()) {
4431 padValues[0], padValues[1], strideValues[0],
4432 dilationValues[0],
"height",
"y",
"top",
4435 padValues[2], padValues[3], strideValues[1],
4436 dilationValues[1],
"width",
"x",
"left",
4442 const ShapeAdaptor biasShape(getBias().
getType());
4443 if (biasShape.hasRank() && outputShape.hasRank()) {
4444 const int64_t biasChannels = biasShape.getDimSize(0);
4445 const int64_t outputChannels =
4446 outputShape.getDimSize(outputShape.getRank() - 1);
4447 if (biasChannels == ShapedType::kDynamic ||
4448 outputChannels == ShapedType::kDynamic)
4452 if (biasChannels != outputChannels && biasChannels != 1)
4454 "bias channels expected to be equal to output channels (")
4455 << outputChannels <<
") or 1, got " << biasChannels;
4461LogicalResult Conv3DOp::inferReturnTypeComponents(
4462 MLIRContext *context, ::std::optional<Location> location,
4463 Conv3DOp::Adaptor adaptor,
4464 SmallVectorImpl<ShapedTypeComponents> &inferredReturnShapes) {
4468LogicalResult Conv3DOp::verify() {
4474LogicalResult AvgPool2dOp::inferReturnTypeComponents(
4475 MLIRContext *context, ::std::optional<Location> location,
4476 AvgPool2dOp::Adaptor adaptor,
4477 SmallVectorImpl<ShapedTypeComponents> &inferredReturnShapes) {
4478 ShapeAdaptor inputShape(adaptor.getInput().getType());
4479 const Properties &prop = adaptor.getProperties();
4481 inferredReturnShapes);
4484LogicalResult AvgPool2dAdaptiveOp::inferReturnTypeComponents(
4485 MLIRContext *context, ::std::optional<Location> location,
4486 AvgPool2dAdaptiveOp::Adaptor adaptor,
4487 SmallVectorImpl<ShapedTypeComponents> &inferredReturnShapes) {
4488 ShapeAdaptor inputShape(adaptor.getInput().getType());
4490 llvm::SmallVector<int64_t> kernelValues;
4491 llvm::SmallVector<int64_t> strideValues;
4492 llvm::SmallVector<int64_t> padValues;
4499 padValues, inferredReturnShapes);
4502 llvm::SmallVector<int64_t> outputShape(4, ShapedType::kDynamic);
4503 if (inputShape.hasRank()) {
4505 outputShape[0] = inputShape.getDimSize(0);
4506 outputShape[3] = inputShape.getDimSize(3);
4509 inferredReturnShapes.push_back(ShapedTypeComponents(outputShape));
4513LogicalResult MaxPool2dOp::inferReturnTypeComponents(
4514 MLIRContext *context, ::std::optional<Location> location,
4515 MaxPool2dOp::Adaptor adaptor,
4516 SmallVectorImpl<ShapedTypeComponents> &inferredReturnShapes) {
4517 ShapeAdaptor inputShape(adaptor.getInput().getType());
4518 const Properties &prop = adaptor.getProperties();
4520 inferredReturnShapes);
4523LogicalResult MaxPool2dAdaptiveOp::inferReturnTypeComponents(
4524 MLIRContext *context, ::std::optional<Location> location,
4525 MaxPool2dAdaptiveOp::Adaptor adaptor,
4526 SmallVectorImpl<ShapedTypeComponents> &inferredReturnShapes) {
4527 ShapeAdaptor inputShape(adaptor.getInput().getType());
4529 llvm::SmallVector<int64_t> kernelValues;
4530 llvm::SmallVector<int64_t> strideValues;
4531 llvm::SmallVector<int64_t> padValues;
4538 padValues, inferredReturnShapes);
4541 llvm::SmallVector<int64_t> outputShape(4, ShapedType::kDynamic);
4542 if (inputShape.hasRank()) {
4543 outputShape[0] = inputShape.getDimSize(0);
4544 outputShape[3] = inputShape.getDimSize(3);
4546 inferredReturnShapes.push_back(ShapedTypeComponents(outputShape));
4550LogicalResult MaxPool2dOp::verify() {
4561LogicalResult MaxPool2dAdaptiveOp::verify() {
4566 AdaptivePoolingConstShapeValues values;
4570 values.pad, getInput(), getOutput())))
4576LogicalResult DepthwiseConv2DOp::inferReturnTypeComponents(
4577 MLIRContext *context, ::std::optional<Location> location,
4578 DepthwiseConv2DOp::Adaptor adaptor,
4579 SmallVectorImpl<ShapedTypeComponents> &inferredReturnShapes) {
4580 llvm::SmallVector<int64_t> outputShape(4, ShapedType::kDynamic);
4582 int64_t inputWidth = ShapedType::kDynamic;
4583 int64_t inputHeight = ShapedType::kDynamic;
4584 int64_t inputChannels = ShapedType::kDynamic;
4586 int64_t weightWidth = ShapedType::kDynamic;
4587 int64_t weightHeight = ShapedType::kDynamic;
4588 int64_t depthChannels = ShapedType::kDynamic;
4591 ShapeAdaptor inputShape(adaptor.getInput().getType());
4592 if (inputShape.hasRank()) {
4593 outputShape[0] = inputShape.getDimSize(0);
4594 inputHeight = inputShape.getDimSize(1);
4595 inputWidth = inputShape.getDimSize(2);
4596 inputChannels = inputShape.getDimSize(3);
4600 ShapeAdaptor weightShape(adaptor.getWeight().getType());
4601 if (weightShape.hasRank()) {
4602 weightHeight = weightShape.getDimSize(0);
4603 weightWidth = weightShape.getDimSize(1);
4604 inputChannels = ShapedType::isDynamic(inputChannels)
4605 ? weightShape.getDimSize(2)
4607 depthChannels = weightShape.getDimSize(3);
4612 if (ShapedType::isStatic(inputChannels) &&
4613 ShapedType::isStatic(depthChannels)) {
4614 outputShape[3] = inputChannels * depthChannels;
4618 ShapeAdaptor biasShape(adaptor.getBias().getType());
4619 if (biasShape.hasRank() && ShapedType::isDynamic(outputShape[3])) {
4620 int64_t bc = biasShape.getDimSize(0);
4621 if (bc != ShapedType::kDynamic && bc != 1)
4622 outputShape[3] = bc;
4625 llvm::ArrayRef<int64_t> dilation = adaptor.getDilation();
4626 llvm::ArrayRef<int64_t> padding = adaptor.getPad();
4627 llvm::ArrayRef<int64_t> stride = adaptor.getStride();
4629 if (ShapedType::isStatic(inputHeight) && ShapedType::isStatic(weightHeight)) {
4630 int64_t inputSize = inputHeight + padding[0] + padding[1];
4631 int64_t filterSize = (weightHeight - 1) * dilation[0] + 1;
4632 int64_t unstridedResult = inputSize - filterSize + 1;
4633 outputShape[1] = (unstridedResult - 1) / stride[0] + 1;
4636 if (ShapedType::isStatic(inputWidth) && ShapedType::isStatic(weightWidth)) {
4637 int64_t inputSize = inputWidth + padding[2] + padding[3];
4638 int64_t filterSize = (weightWidth - 1) * dilation[1] + 1;
4639 int64_t unstridedResult = inputSize - filterSize + 1;
4640 outputShape[2] = (unstridedResult - 1) / stride[1] + 1;
4643 inferredReturnShapes.push_back(ShapedTypeComponents(outputShape));
4647LogicalResult DepthwiseConv2DOp::verify() {
4653LogicalResult TransposeConv2DOp::inferReturnTypeComponents(
4654 MLIRContext *context, ::std::optional<Location> location,
4655 TransposeConv2DOp::Adaptor adaptor,
4656 SmallVectorImpl<ShapedTypeComponents> &inferredReturnShapes) {
4657 llvm::SmallVector<int64_t> outputShape(4, ShapedType::kDynamic);
4659 int64_t inputWidth = ShapedType::kDynamic;
4660 int64_t inputHeight = ShapedType::kDynamic;
4661 int64_t weightWidth = ShapedType::kDynamic;
4662 int64_t weightHeight = ShapedType::kDynamic;
4665 ShapeAdaptor inputShape(adaptor.getInput().getType());
4666 if (inputShape.hasRank()) {
4667 outputShape[0] = ShapedType::isDynamic(outputShape[0])
4668 ? inputShape.getDimSize(0)
4670 inputHeight = inputShape.getDimSize(1);
4671 inputWidth = inputShape.getDimSize(2);
4675 ShapeAdaptor weightShape(adaptor.getWeight().getType());
4676 if (weightShape.hasRank()) {
4677 outputShape[3] = ShapedType::isDynamic(outputShape[3])
4678 ? weightShape.getDimSize(0)
4680 weightHeight = weightShape.getDimSize(1);
4681 weightWidth = weightShape.getDimSize(2);
4685 ShapeAdaptor biasShape(adaptor.getBias().getType());
4686 if (biasShape.hasRank() && ShapedType::isDynamic(outputShape[3])) {
4687 int64_t bc = biasShape.getDimSize(0);
4688 if (bc != ShapedType::kDynamic && bc != 1)
4689 outputShape[3] = bc;
4692 llvm::ArrayRef<int64_t> padding = adaptor.getOutPad();
4693 llvm::ArrayRef<int64_t> stride = adaptor.getStride();
4695 if (ShapedType::isStatic(inputHeight) && ShapedType::isStatic(weightHeight)) {
4696 int64_t calculateSize =
4697 (inputHeight - 1) * stride[0] + padding[0] + padding[1] + weightHeight;
4699 ShapedType::isDynamic(outputShape[1]) ? calculateSize : outputShape[1];
4702 if (ShapedType::isStatic(inputWidth) && ShapedType::isStatic(weightWidth)) {
4703 int64_t calculateSize =
4704 (inputWidth - 1) * stride[1] + padding[2] + padding[3] + weightWidth;
4706 ShapedType::isDynamic(outputShape[2]) ? calculateSize : outputShape[2];
4709 inferredReturnShapes.push_back(ShapedTypeComponents(outputShape));
4713LogicalResult TransposeConv2DOp::verify() {
4717 const llvm::ArrayRef<int64_t> strides = getStride();
4718 const int64_t strideY = strides[0];
4719 const int64_t strideX = strides[1];
4721 if (strideY < 1 || strideX < 1)
4722 return emitOpError(
"expect all stride values to be >= 1, got [")
4725 const auto checkPadAgainstKernelDim =
4726 [
this](int64_t padValue, int64_t kernelDimSize, llvm::StringRef padName,
4727 llvm::StringRef kernelDimName) -> LogicalResult {
4728 if (padValue <= -kernelDimSize)
4729 return emitOpError(
"expected ")
4730 << padName <<
" > -" << kernelDimName <<
", but got: " << padName
4731 <<
"=" << padValue <<
" and " << kernelDimName <<
"="
4736 const llvm::ArrayRef<int64_t> padding = getOutPad();
4737 const int64_t outPadTop = padding[0];
4738 const int64_t outPadBottom = padding[1];
4739 const int64_t outPadLeft = padding[2];
4740 const int64_t outPadRight = padding[3];
4742 const auto weightType =
4743 llvm::dyn_cast<RankedTensorType>(getWeight().
getType());
4746 const int64_t kernelHeight = weightType.getDimSize(1);
4747 if (ShapedType::isStatic(kernelHeight)) {
4748 if (
failed(checkPadAgainstKernelDim(outPadTop, kernelHeight,
4749 "out_pad_top",
"KH")))
4752 if (
failed(checkPadAgainstKernelDim(outPadBottom, kernelHeight,
4753 "out_pad_bottom",
"KH")))
4757 const int64_t kernelWidth = weightType.getDimSize(2);
4758 if (ShapedType::isStatic(kernelWidth)) {
4759 if (
failed(checkPadAgainstKernelDim(outPadLeft, kernelWidth,
4760 "out_pad_left",
"KW")))
4763 if (
failed(checkPadAgainstKernelDim(outPadRight, kernelWidth,
4764 "out_pad_right",
"KW")))
4770 const auto outputType =
4771 llvm::dyn_cast<RankedTensorType>(getOutput().
getType());
4775 const auto inputType = llvm::dyn_cast<RankedTensorType>(getInput().
getType());
4776 if (inputType && weightType) {
4777 const int64_t inputHeight = inputType.getDimSize(1);
4778 const int64_t kernelHeight = weightType.getDimSize(1);
4779 const int64_t outputHeight = outputType.getDimSize(1);
4781 if (ShapedType::isStatic(inputHeight) &&
4782 ShapedType::isStatic(outputHeight)) {
4784 (inputHeight - 1) * strideY + outPadTop + outPadBottom + kernelHeight)
4786 "dimension mismatch: expected OH == (IH - 1) * stride_y "
4787 "+ out_pad_top + out_pad_bottom + KH, but got ")
4788 << outputHeight <<
" != (" << inputHeight <<
" - 1) * "
4789 << strideY <<
" + " << outPadTop <<
" + " << outPadBottom
4790 <<
" + " << kernelHeight;
4793 const int64_t inputWidth = inputType.getDimSize(2);
4794 const int64_t kernelWidth = weightType.getDimSize(2);
4795 const int64_t outputWidth = outputType.getDimSize(2);
4797 if (ShapedType::isStatic(inputWidth) && ShapedType::isStatic(outputWidth)) {
4799 (inputWidth - 1) * strideX + outPadLeft + outPadRight + kernelWidth)
4801 "dimension mismatch: expected OW == (IW - 1) * stride_x "
4802 "+ out_pad_left + out_pad_right + KW, but got ")
4803 << outputWidth <<
" != (" << inputWidth <<
" - 1) * " << strideX
4804 <<
" + " << outPadLeft <<
" + " << outPadRight <<
" + "
4809 const auto biasType = llvm::dyn_cast<RankedTensorType>(getBias().
getType());
4814 const int64_t biasChannels = biasType.getDimSize(0);
4817 if (biasChannels == ShapedType::kDynamic)
4820 const int64_t outputChannels = outputType.getDimSize(3);
4821 if (!ShapedType::isDynamic(outputChannels) &&
4822 biasChannels != outputChannels && biasChannels != 1)
4824 "bias channels expected to be equal to output channels (")
4825 << outputChannels <<
") or 1, got " << biasChannels;
4830LogicalResult RescaleOp::verify() {
4831 const auto inputType = llvm::cast<ShapedType>(getInput().
getType());
4832 auto inputElementType =
4834 if (!mlir::isa<IntegerType>(inputElementType)) {
4835 emitOpError(
"expect input to have integer element type, got ")
4836 << inputElementType;
4840 const auto outputType = llvm::cast<ShapedType>(getOutput().
getType());
4841 auto outputElementType =
4843 if (!mlir::isa<IntegerType>(outputElementType)) {
4844 emitOpError(
"expect output to have integer element type, got ")
4845 << outputElementType;
4857 const auto multiplierType = llvm::cast<ShapedType>(getMultiplier().
getType());
4859 if (getScale32() && !multiplierType.getElementType().isInteger(32)) {
4860 emitOpError(
"expect i32 element type for multiplier for scale32=true, got ")
4861 << multiplierType.getElementType();
4866 if (!getScale32() && !multiplierType.getElementType().isInteger(16)) {
4868 "expect i16 element type for multiplier for scale32=false, got ")
4869 << multiplierType.getElementType();
4873 if (!inputType.hasRank())
4879 int64_t numChannels = 1;
4880 if (getPerChannel()) {
4881 if (inputType.getRank() < 1) {
4882 emitOpError(
"requires input to be at least rank 1 when per_channel is "
4883 "true, but got rank ")
4884 << inputType.getRank();
4887 numChannels = inputType.getDimSize(inputType.getRank() - 1);
4890 if (outputType.hasRank()) {
4892 getOperation(), outputType, inputType.getShape())))
4896 if (multiplierType.hasRank()) {
4897 ArrayRef<int64_t> multiplierShape = multiplierType.getShape();
4899 if (multiplierShape[0] != ShapedType::kDynamic &&
4900 multiplierShape[0] != numChannels) {
4901 emitOpError(
"expect shape of { ")
4902 << numChannels <<
" } for multiplier input, got { "
4903 << multiplierShape[0] <<
" }";
4908 const auto shiftType = llvm::cast<ShapedType>(getShift().
getType());
4909 if (shiftType.hasRank()) {
4910 ArrayRef<int64_t> shiftShape = shiftType.getShape();
4912 if (shiftShape[0] != ShapedType::kDynamic && shiftShape[0] != numChannels) {
4913 emitOpError(
"expect shape of { ")
4914 << numChannels <<
" } for shift input, got { " << shiftShape[0]
4923LogicalResult RescaleOp::inferReturnTypeComponents(
4924 MLIRContext *context, ::std::optional<Location> location,
4925 RescaleOp::Adaptor adaptor,
4926 SmallVectorImpl<ShapedTypeComponents> &inferredReturnShapes) {
4927 ShapeAdaptor inputShape(adaptor.getInput().getType());
4928 inferredReturnShapes.push_back(ShapedTypeComponents(inputShape));
4932LogicalResult CastOp::verify() {
4933 const ShapedType inputType = llvm::cast<ShapedType>(getInput().
getType());
4934 const ShapedType outputType = llvm::cast<ShapedType>(
getType());
4935 const Type inputElementType = inputType.getElementType();
4936 const Type outputElementType = outputType.getElementType();
4938 const bool inputIsBlockScaled = llvm::isa<BlockScaledType>(inputElementType);
4939 const bool outputIsBlockScaled =
4940 llvm::isa<BlockScaledType>(outputElementType);
4942 const bool isUnsigned = this->getInputUnsigned();
4947 return emitOpError()
4948 <<
"attribute input_unsigned requires integer type inputs. Got: "
4951 if (!inputIsBlockScaled && !outputIsBlockScaled)
4954 if (inputIsBlockScaled && outputIsBlockScaled)
4955 return emitOpError()
4956 <<
"requires exactly one of input or output to have block scaled "
4959 const Type scalarElementType =
4960 inputIsBlockScaled ? outputElementType : inputElementType;
4961 if (!llvm::isa<FloatType>(scalarElementType))
4962 return emitOpError()
4963 <<
"requires non-block-scaled element type to be floating-point "
4964 "when casting to or from block scaled element type, got "
4965 << scalarElementType;
4970LogicalResult CastFromBlockScaledOp::inferReturnTypeComponents(
4971 MLIRContext *context, ::std::optional<Location> location,
4972 CastFromBlockScaledOp::Adaptor adaptor,
4973 SmallVectorImpl<ShapedTypeComponents> &inferredReturnShapes) {
4974 const ShapeAdaptor inputShape(adaptor.getInputData().getType());
4975 inferredReturnShapes.push_back(ShapedTypeComponents(inputShape));
4979LogicalResult CastFromBlockScaledOp::verify() {
4980 const Type inputDataType = getInputData().getType();
4981 const Type outputDataType = getResult().getType();
4983 return emitOpError() <<
"require compatible shapes for input_data ("
4984 << inputDataType <<
") and " <<
"output_data ("
4985 << outputDataType <<
")";
4987 const ShapeAdaptor inputDataShape = ShapeAdaptor(inputDataType);
4989 if (inputDataShape.
hasRank()) {
4990 const unsigned int blockSize =
4992 if (blockSize != BlockSizeAttr::getBlockSizeValue(BlockSize::BLOCK_SIZE_32))
4993 return emitOpError(
"expect block size to be 32, got ") << blockSize;
4994 const int64_t inputDataLastDim =
4996 if (inputDataLastDim % blockSize != 0)
4997 return emitOpError() <<
"expect last dimension of input_data ("
4999 <<
") to be divisible by block_size (" << blockSize
5002 const Type inputScaleType = getInputScale().getType();
5003 const ShapeAdaptor inputScaleShape = ShapeAdaptor(inputScaleType);
5005 if (inputScaleShape.
hasRank()) {
5006 SmallVector<int64_t> inputDataDims, inputScaleDims;
5007 inputDataShape.
getDims(inputDataDims);
5008 inputScaleShape.
getDims(inputScaleDims);
5010 if (inputDataDims.size() != inputScaleDims.size() ||
5012 ArrayRef<int64_t>(inputDataDims).drop_back(1),
5013 ArrayRef<int64_t>(inputScaleDims).drop_back(1))))
5014 return emitOpError()
5015 <<
"require compatible shapes for input_data (" << inputDataType
5016 <<
") and " <<
"input_scale (" << inputScaleType
5017 <<
") except for the last dimension";
5019 const SmallVector<int64_t, 2> dimsToCheck{inputDataLastDim / blockSize,
5020 inputScaleDims.back()};
5021 if (ShapedType::isStatic(inputDataLastDim) &&
5023 return emitOpError()
5024 <<
"expect last dimension of input_scale ("
5025 << inputScaleDims.back()
5026 <<
") to be equal to last dimension of input_data / block_size ("
5027 << inputDataDims.back() / blockSize <<
")";
5034LogicalResult CastToBlockScaledOp::inferReturnTypeComponents(
5035 MLIRContext *context, ::std::optional<Location> location,
5036 CastToBlockScaledOp::Adaptor adaptor,
5037 SmallVectorImpl<ShapedTypeComponents> &inferredReturnShapes) {
5038 const ShapeAdaptor inputShape(adaptor.getInputData().getType());
5039 inferredReturnShapes.push_back(ShapedTypeComponents(inputShape));
5040 if (!inputShape.hasRank())
5044 SmallVector<int64_t> outputScaleShape;
5045 inputShape.getDims(outputScaleShape);
5046 const int64_t lastDimLoc = inputShape.getRank() - 1;
5047 const int64_t lastDimSize = inputShape.getDimSize(lastDimLoc);
5048 if (ShapedType::isStatic(lastDimSize)) {
5049 const unsigned int blockSize =
5050 BlockSizeAttr::getBlockSizeValue(adaptor.getBlockSize());
5051 outputScaleShape[lastDimLoc] = lastDimSize / blockSize;
5053 inferredReturnShapes.push_back(ShapedTypeComponents(outputScaleShape));
5057LogicalResult CastToBlockScaledOp::verify() {
5058 const Type inputDataType = getInputData().getType();
5059 const Type outputDataType = getResult(0).getType();
5061 return emitOpError() <<
"require compatible shapes for input_data ("
5062 << inputDataType <<
") and " <<
"output_data ("
5063 << outputDataType <<
")";
5065 const unsigned int blockSize =
5067 if (blockSize != BlockSizeAttr::getBlockSizeValue(BlockSize::BLOCK_SIZE_32))
5068 return emitOpError(
"expect block size to be 32, got ") << blockSize;
5069 const ShapeAdaptor inputDataShape = ShapeAdaptor(inputDataType);
5070 if (inputDataShape.
hasRank()) {
5071 const int64_t inputDataLastDim =
5073 if (ShapedType::isStatic(inputDataLastDim) &&
5074 inputDataLastDim % blockSize != 0)
5075 return emitOpError() <<
"expect last dimension of input_data ("
5077 <<
") to be divisible by block_size (" << blockSize
5081 const ShapeAdaptor outputDataShape = ShapeAdaptor(outputDataType);
5082 const Type outputScaleType = getResult(1).getType();
5083 const ShapeAdaptor outputScaleShape = ShapeAdaptor(outputScaleType);
5085 SmallVector<int64_t> outputDataDims, outputScaleDims;
5086 outputDataShape.
getDims(outputDataDims);
5087 outputScaleShape.
getDims(outputScaleDims);
5089 if (outputDataDims.size() != outputScaleDims.size() ||
5091 ArrayRef<int64_t>(outputDataDims).drop_back(1),
5092 ArrayRef<int64_t>(outputScaleDims).drop_back(1))))
5093 return emitOpError() <<
"require compatible shapes for output_data ("
5094 << outputDataType <<
") and " <<
"output_scale ("
5096 <<
") except for the last dimension";
5098 const int64_t outputDataLastDim = outputDataDims.back();
5099 const SmallVector<int64_t, 2> dimsToCheck{outputDataLastDim / blockSize,
5100 outputScaleDims.back()};
5101 if (ShapedType::isStatic(outputDataLastDim) &&
5103 return emitOpError()
5104 <<
"expect last dimension of output_scale ("
5105 << outputScaleDims.back()
5106 <<
") to be equal to last dimension of output_data / block_size ("
5107 << outputDataDims.back() / blockSize <<
")";
5113LogicalResult IfOp::inferReturnTypeComponents(
5114 MLIRContext *context, ::std::optional<Location> location,
5115 IfOp::Adaptor adaptor,
5116 SmallVectorImpl<ShapedTypeComponents> &inferredReturnShapes) {
5117 llvm::SmallVector<tosa::YieldOp> yieldOps;
5118 for (Region *region : adaptor.getRegions()) {
5119 for (
auto &block : *region)
5120 if (
auto returnOp = dyn_cast<tosa::YieldOp>(block.getTerminator()))
5121 yieldOps.push_back(returnOp);
5124 if (yieldOps.empty())
5128 llvm::SmallVector<ValueKnowledge> resultKnowledge;
5129 resultKnowledge.reserve(yieldOps.front().getNumOperands());
5130 for (
auto operand : yieldOps.front().getOperands()) {
5131 resultKnowledge.push_back(
5135 for (
auto yieldOp : yieldOps) {
5136 if (resultKnowledge.size() != yieldOp.getNumOperands())
5139 for (
const auto &it : llvm::enumerate(yieldOp.getOperands())) {
5140 int32_t index = it.index();
5142 resultKnowledge[index],
5146 resultKnowledge[index] = meet;
5150 for (
const ValueKnowledge &
result : resultKnowledge) {
5151 inferredReturnShapes.push_back(
result.getShapedTypeComponents());
5157LogicalResult WhileOp::inferReturnTypeComponents(
5158 MLIRContext *context, ::std::optional<Location> location,
5159 WhileOp::Adaptor adaptor,
5160 SmallVectorImpl<ShapedTypeComponents> &inferredReturnShapes) {
5161 llvm::SmallVector<tosa::YieldOp> yieldOps;
5162 for (
auto &block : adaptor.getBodyGraph())
5163 if (
auto returnOp = dyn_cast<tosa::YieldOp>(block.getTerminator()))
5164 yieldOps.push_back(returnOp);
5168 if (yieldOps.empty())
5172 llvm::SmallVector<ValueKnowledge> resultKnowledge;
5173 resultKnowledge.reserve(yieldOps.front().getNumOperands());
5174 for (
auto operand : yieldOps.front().getOperands()) {
5175 resultKnowledge.push_back(
5179 for (
auto yieldOp : yieldOps) {
5180 if (resultKnowledge.size() != yieldOp.getNumOperands())
5183 for (
const auto &it : llvm::enumerate(yieldOp.getOperands())) {
5184 int32_t index = it.index();
5186 resultKnowledge[index],
5188 resultKnowledge[index] = meet;
5193 for (
const ValueKnowledge &
result : resultKnowledge) {
5194 inferredReturnShapes.push_back(
result.getShapedTypeComponents());
5203 StringRef prefix =
"") {
5204 assert(blocksArgs.size() == initializers.size() &&
5205 "expected same length of arguments and initializers");
5206 if (initializers.empty())
5209 parser << prefix <<
'(';
5210 llvm::interleaveComma(
5211 llvm::zip(blocksArgs, initializers), parser,
5212 [&](
auto it) { parser << std::get<0>(it) <<
" = " << std::get<1>(it); });
5217ParseResult IfOp::parse(OpAsmParser &parser, OperationState &
result) {
5219 result.regions.reserve(2);
5220 Region *thenRegion =
result.addRegion();
5221 Region *elseRegion =
result.addRegion();
5223 OpAsmParser::UnresolvedOperand cond;
5228 SmallVector<OpAsmParser::Argument, 4> regionArgs;
5229 SmallVector<OpAsmParser::UnresolvedOperand, 4> operands;
5232 OptionalParseResult listResult =
5240 "expected type for condition operand");
5246 "expected type for condition operand");
5254 FunctionType functionType;
5258 <<
"expected list of types for block arguments "
5259 <<
"followed by arrow type and list of return types";
5261 result.addTypes(functionType.getResults());
5263 if (functionType.getNumInputs() != operands.size()) {
5265 <<
"expected as many input types as operands " <<
"(expected "
5266 << operands.size() <<
" got " << functionType.getNumInputs()
5297void IfOp::print(OpAsmPrinter &p) {
5298 p <<
" " << getCondition();
5301 getInputList(),
" ");
5303 p << getCondition().getType();
5305 if (!getInputList().empty()) {
5307 llvm::interleaveComma(getInputList().getTypes(), p);
5316 auto &elseRegion = getElseGraph();
5317 if (!elseRegion.
empty()) {
5325LogicalResult IfOp::verify() {
5327 "'then_graph' arguments", getInputList(),
5333 "'else_graph' arguments", getInputList(),
5339 if (getThenGraph().front().mightHaveTerminator()) {
5341 dyn_cast<tosa::YieldOp>(getThenGraph().front().getTerminator());
5343 *
this, thenYield.getInputs(),
"'then_graph' results",
5344 getOutputList(),
"'output_list'")
5350 if (getElseGraph().front().mightHaveTerminator()) {
5352 dyn_cast<tosa::YieldOp>(getElseGraph().front().getTerminator());
5354 *
this, elseYield.getInputs(),
"'else_graph' results",
5355 getOutputList(),
"'output_list'")
5360 auto condType = getCondition().getType();
5362 return emitOpError() <<
"'condition' must be a size 1 tensor, got "
5368LogicalResult WhileOp::verify() {
5370 getOutputList(),
"'output_list'")
5375 "'cond_graph' arguments", getInputList(),
5381 "'body_graph' arguments", getInputList(),
5386 if (getBodyGraph().front().mightHaveTerminator()) {
5388 dyn_cast<tosa::YieldOp>(getBodyGraph().front().getTerminator());
5390 "'body_graph' results",
5391 getInputList(),
"'input_list'")
5398 if (!getCondGraph().front().mightHaveTerminator())
5402 dyn_cast<tosa::YieldOp>(getCondGraph().front().getTerminator());
5406 if (condYield.getInputs().size() != 1)
5407 return emitOpError() <<
"require 'cond_graph' only have one result";
5409 auto condOutType = condYield.getInputs()[0].getType();
5411 return emitOpError() <<
"'cond_graph' result must be a size 1 tensor, got "
5415 return emitOpError() <<
"'cond_graph' result must be a boolean tensor, got "
5421LogicalResult ReverseOp::verify() {
5422 TensorType inputType = getInput1().getType();
5423 int32_t reverseAxis = getAxis();
5425 if (reverseAxis < 0)
5426 return emitOpError(
"expected non-negative reverse axis");
5428 int64_t inputRank = inputType.getRank();
5431 if (reverseAxis >= inputRank && (reverseAxis != 0 || inputRank != 0))
5432 return emitOpError(
"expect input tensor rank (")
5433 << inputRank <<
") to be larger than reverse axis (" << reverseAxis
5440LogicalResult tosa::SelectOp::verify() {
5451 auto predicateType = llvm::dyn_cast<ShapedType>(getPred().
getType());
5452 if (!predicateType) {
5453 return emitOpError(
"expect shaped tensor for input1, got ")
5454 << getInput1().getType();
5456 auto predicateElementType = predicateType.getElementType();
5457 if (!predicateElementType.isInteger(1)) {
5458 return emitOpError(
"expect element type of bool for input1, got ")
5459 << predicateElementType;
5465LogicalResult tosa::VariableReadOp::verify() {
5473LogicalResult tosa::VariableWriteOp::verify() {
5482ParseResult WhileOp::parse(OpAsmParser &parser, OperationState &
result) {
5483 SmallVector<OpAsmParser::Argument, 4> regionArgs;
5484 SmallVector<OpAsmParser::UnresolvedOperand, 4> operands;
5485 Region *cond =
result.addRegion();
5486 Region *body =
result.addRegion();
5488 OptionalParseResult listResult =
5493 FunctionType functionType;
5498 result.addTypes(functionType.getResults());
5500 if (functionType.getNumInputs() != operands.size()) {
5502 <<
"expected as many input types as operands " <<
"(expected "
5503 << operands.size() <<
" got " << functionType.getNumInputs() <<
")";
5513 for (
size_t i = 0, e = regionArgs.size(); i != e; ++i)
5514 regionArgs[i].type = functionType.getInput(i);
5516 return failure(parser.
parseRegion(*cond, regionArgs) ||
5521void WhileOp::print(OpAsmPrinter &parser) {
5523 getInputList(),
" ");
5526 getResults().getTypes());
5532 (*this)->getDiscardableAttrDictionary().getValue());
5541 auto zpType = mlir::RankedTensorType::get({1}, srcElemType);
5542 if (llvm::isa<FloatType>(srcElemType)) {
5544 zpType, builder.
getFloatAttr(srcElemType,
static_cast<double>(zp)));
5545 return tosa::ConstOp::create(builder, loc, zpType, zpAttr);
5547 if (llvm::isa<IntegerType>(srcElemType)) {
5550 return tosa::ConstOp::create(builder, loc, zpType, zpAttr);
5552 llvm::errs() <<
"zero point is not allowed for unsupported data types\n";
5553 return std::nullopt;
5561 return mlir::isa<tosa::shapeType>(t);
5568 return emitError() <<
"invalid rank (must be >= 0): " << rank;
5574 if (mlir::isa<::mlir::tosa::shapeType>(v.getType())) {
5575 Operation *definingOp = v.getDefiningOp();
5577 return op->
emitOpError(
"shape operand is not compile time resolvable");
5590 auto getRank = [](
const Type type) {
5591 return mlir::cast<mlir::tosa::shapeType>(type).getRank();
5597 for (
auto type : operandTypes) {
5598 if (getRank(type) != rank) {
5599 return op->
emitOpError(
"operands don't have matching ranks");
5602 for (
auto type : resultTypes) {
5603 if (getRank(type) != rank) {
5604 return op->
emitOpError(
"result shape has different rank than operands");
5614LogicalResult tosa::ConstShapeOp::verify() {
5616 auto valuesRank = getValues().getType().getRank();
5617 if (valuesRank != 1)
5618 return emitOpError(
"expect elements in attribute values with rank 1");
5620 auto count = getValues().getNumElements();
5621 auto rank = (cast<tosa::shapeType>(getResult().
getType())).getRank();
5622 if (count != rank && (count != 1 || rank != 0)) {
5623 return emitOpError(
"expect number of elements in attribute values (")
5624 << count <<
") to be equal to the rank (" << rank
5625 <<
") for the result shape type";
5630LogicalResult tosa::DimOp::verify() {
5631 const tosa::shapeType outShapeType =
5632 cast<tosa::shapeType>(getResult().
getType());
5633 if (outShapeType.getRank() != 1)
5634 return emitOpError(
"expect output shape type to contain one element, got ")
5639 const int64_t inputRank = inputType.getRank();
5640 const int64_t axis = getAxisAttr().getInt();
5641 if (axis < 0 || axis >= inputRank)
5642 return emitOpError(
"expect axis to be in the range [0, ")
5643 << inputRank <<
"), got " << axis;
5648LogicalResult tosa::ConcatShapeOp::verify() {
5649 const tosa::shapeType outShapeType =
5650 cast<tosa::shapeType>(getResult().
getType());
5651 const int64_t outputRank = outShapeType.getRank();
5654 if (inputList.size() == 0)
5655 return emitOpError(
"requires at least one input shape");
5657 if (llvm::any_of(inputList, [](Value v) {
5658 return cast<tosa::shapeType>(v.
getType()).getRank() == 0;
5660 return emitOpError(
"requires all inputs shapes have a rank greater than 0");
5662 const int64_t inputsRank =
5663 llvm::accumulate(inputList, 0, [](int64_t acc,
const Value &input) {
5664 const tosa::shapeType inShapeType =
5665 cast<tosa::shapeType>(input.
getType());
5666 return acc + inShapeType.getRank();
5668 if (outputRank != inputsRank)
5669 return emitOpError(
"requires output shape rank to be equal to the sum of "
5670 "the input shape ranks (")
5671 << inputsRank <<
"), got " << outputRank;
5676LogicalResult tosa::SliceShapeOp::verify() {
5677 std::optional<int32_t> start;
5678 DenseIntElementsAttr startAttr;
5680 start = startAttr.getValues<int32_t>()[0];
5681 if (start && start.value() < 0)
5682 return emitOpError(
"expected non-negative start index, got ")
5685 std::optional<int32_t> size;
5686 DenseIntElementsAttr sizeAttr;
5688 size = sizeAttr.getValues<int32_t>()[0];
5689 if (size && size.value() <= 0)
5690 return emitOpError(
"expected positive size, got ") << size.value();
5695 const tosa::shapeType outShapeType =
5696 cast<tosa::shapeType>(getResult().
getType());
5697 const int64_t outputRank = outShapeType.getRank();
5698 if (outputRank != size)
5700 "expected output type size to be equal to size attribute, got ")
5701 << outputRank <<
" vs " << size.value();
5706 const tosa::shapeType inShapeType =
5707 cast<tosa::shapeType>(getInput().
getType());
5708 const int64_t inputRank = inShapeType.getRank();
5709 const int64_t sliceSize = start.value() + size.value();
5710 if (sliceSize > inputRank)
5711 return emitOpError(
"expected start + size to be less than or equal to "
5712 "input shape rank (")
5713 << inputRank <<
"), got " << sliceSize;
5722#define GET_ATTRDEF_CLASSES
5723#include "mlir/Dialect/Tosa/IR/TosaAttributes.cpp.inc"
5728#define GET_TYPEDEF_CLASSES
5729#include "mlir/Dialect/Tosa/IR/TosaOpsTypesBase.cpp.inc"
5750 printer << keyword <<
'(';
5772#define GET_OP_CLASSES
5773#include "mlir/Dialect/Tosa/IR/TosaOps.cpp.inc"
static void printInitializationList(OpAsmPrinter &p, Block::BlockArgListType blocksArgs, ValueRange initializers, StringRef prefix="")
Prints the initialization list in the form of <prefix>(inner = outer, inner2 = outer2,...
true
Given two iterators into the same block, return "true" if a is before `b.
static bool isLegalToInline(InlinerInterface &interface, Region *src, Region *insertRegion, bool shouldCloneInlinedRegion, IRMapping &valueMapping)
Utility to check that all of the operations within 'src' can be inlined.
static Type getValueType(Attribute attr)
static Type getElementType(Type type, ArrayRef< int32_t > indices, function_ref< InFlightDiagnostic(StringRef)> emitErrorFn)
Walks the given type hierarchy with the given indices, potentially down to component granularity,...
static LogicalResult verifyMatMulShapes(T op, bool transposeB)
static ParseResult parseOptionalBoolClause(OpAsmParser &parser, StringRef keyword, BoolAttr &result)
static void printShapeToDiagnostic(InFlightDiagnostic &diag, ArrayRef< int64_t > shape)
static void buildMatMulOpWithQuantInfo(OpBuilder &builder, OperationState &result, Type outputType, Value a, Value b)
static LogicalResult verifySameElementTypes(Operation *op, Type aType, Type bType, StringRef aName="input", StringRef bName="output")
static ParseResult parseLocalBound(OpAsmParser &parser, BoolAttr &result)
LogicalResult inferConvReturnTypeComponents(AdaptorT adaptor, SmallVectorImpl< ShapedTypeComponents > &inferredReturnShapes)
static int64_t getMatMulBatchDim(const ShapeAdaptor &shape, int64_t outputRank, int64_t axis)
static SmallVector< int64_t > convertToMlirShape(ArrayRef< int64_t > shape)
static LogicalResult ReduceInferReturnTypes(ShapeAdaptor operandShape, Type inputType, IntegerAttr axis, SmallVectorImpl< ShapedTypeComponents > &inferredReturnShapes)
static void printScaleValues(AsmPrinter &printer, ArrayRef< Attribute > scaleValues, Type)
static void buildAvgPool2dAdaptiveOpWithQuantInfo(OpBuilder &builder, OperationState &result, Type outputType, Value input, DenseI64ArrayAttr kernel, DenseI64ArrayAttr stride, DenseI64ArrayAttr pad, TypeAttr accType)
This builder mirrors avg_pool2d quant-info handling and materializes kernel/stride/pad as const_shape...
static LogicalResult verifyRescaleValueAndZpTypes(Operation *op, Value val, Value valZp, StringRef name)
static void printOptionalBoolClause(OpAsmPrinter &printer, StringRef keyword, BoolAttr attr)
static LogicalResult errorIfShapeNotSizeOne(Operation *op, Type type)
static void printLocalBound(OpAsmPrinter &printer, Operation *, BoolAttr attr)
LogicalResult argMaxMinVerify(T op)
static LogicalResult verifyMatMulZeroPointType(T op, Value input, Value zp, StringRef inputName, StringRef zpName)
static ParseResult parseScaleValues(AsmParser &parser, SmallVector< Attribute > &scaleValues, Type scaleType)
static ParseResult parseInputUnsigned(OpAsmParser &parser, BoolAttr &result)
#define REDUCE_SHAPE_INFER(OP)
static LogicalResult verifyConvOp(T op)
static LogicalResult verifyAvgPoolCommonTypeAndZpChecks(T op)
static LogicalResult verifyVariableOpErrorIf(T op, Type type, StringRef name)
static LogicalResult poolingInferReturnTypes(ShapeAdaptor inputShape, ArrayRef< int64_t > kernel, ArrayRef< int64_t > stride, ArrayRef< int64_t > pad, SmallVectorImpl< ShapedTypeComponents > &inferredReturnShapes)
static void buildPadOpWithQuantInfo(OpBuilder &builder, OperationState &result, Type outputType, Value input, Value paddings)
This builder is called on TOSA pad operator that needs to create its own OptionalAttr quantization_at...
static LogicalResult verifyPoolingOpImpl(Operation *op, ArrayRef< int64_t > kernel, ArrayRef< int64_t > strides, ArrayRef< int64_t > padding, Value input, Value output)
static std::optional< int64_t > idivCheck(const int64_t lhs, const int64_t rhs)
static void buildVariableOp(OpBuilder &builder, OperationState &result, StringRef name, Type variableType, Attribute initialValue)
static void buildMatMulLikeOpWithQuantInfo(OpBuilder &builder, OperationState &result, Type outputType, Value a, Value b)
LogicalResult verifyConvOutputSize(Operation *op, const int64_t inputSize, const int64_t kernelSize, const int64_t outputSize, const int64_t padBefore, const int64_t padAfter, const int64_t stride, const int64_t dilation, const llvm::StringRef dimName, const llvm::StringRef dimAxis, const llvm::StringRef padBeforeName, const llvm::StringRef padAfterName)
static LogicalResult verifyReduceOp(T op)
#define NARY_SHAPE_INFER(OP)
#define ZERO_POINT_HELPER(OP, OPERAND_NAME, SIGN_EXTEND)
static void buildTransConvOpWithQuantInfo(OpBuilder &builder, OperationState &result, Type outputType, Value input, Value weight, Value bias, DenseI64ArrayAttr outpad, DenseI64ArrayAttr stride, TypeAttr accType)
Handles tosa.transpose_conv2d which has outpad and output shape attributes.
LogicalResult inferArgMaxMinReturnTypeComponents(MLIRContext *context, ::std::optional< Location > location, A adaptor, SmallVectorImpl< ShapedTypeComponents > &inferredReturnShapes)
static void extractAdaptivePoolingConstShapeOperands(T op, AdaptivePoolingConstShapeValues &values)
static LogicalResult verifyConvOpErrorIf(T op)
static FailureOr< int64_t > getZeroPoint(Value val, bool signExtend)
static constexpr bool IsSupportedAdaptivePoolConstShapeVerifyOp
LogicalResult tryUpdateDimOrFailure(Operation *op, int64_t &currDim, const int64_t newDim, const StringRef operandName, const StringRef dimName)
static LogicalResult NAryInferReturnTypes(const ValueShapeRange &operands, SmallVectorImpl< ShapedTypeComponents > &inferredReturnShapes)
#define COMPATIBLE_RETURN_TYPES(OP)
static LogicalResult resolveBroadcastShape(const ValueShapeRange &operands, SmallVector< int64_t > &outShape)
static LogicalResult verifyMatMulQuantizedOperandsType(T op, Type aElementType, Type bElementType)
static LogicalResult verifyOutputShapeCompatibleWithExpected(Operation *op, ShapedType outputType, ArrayRef< int64_t > expectedShape, StringRef outputName="output")
static void printInputUnsigned(OpAsmPrinter &printer, Operation *, BoolAttr attr)
static void buildNegateOpWithQuantInfo(OpBuilder &builder, OperationState &result, Type outputType, Value input)
This builder is called on single-parameter negate operator to construct input and output zero points ...
static void buildConvOpWithQuantInfo(OpBuilder &builder, OperationState &result, Type outputType, Value input, Value weight, Value bias, DenseI64ArrayAttr pad, DenseI64ArrayAttr stride, DenseI64ArrayAttr dilation, TypeAttr accType)
This builder is called on all convolution operators except TransposeConv, which has specialized outpu...
static SmallVector< int64_t > getMatMulBatchShape(const ShapeAdaptor &shape)
static void buildAvgPool2dOpWithQuantInfo(OpBuilder &builder, OperationState &result, Type outputType, Value input, DenseArrayAttr kernel, DenseArrayAttr stride, DenseArrayAttr pad, TypeAttr accType)
Both the tosa.avg_pool2d and unary ops use the same UnaryOpQuantizationAttr but avg_pool operator has...
static LogicalResult errorIfTypeOrShapeMismatch(Operation *op, Type type1, StringRef name1, Type type2, StringRef name2)
static LogicalResult inferMatMulReturnTypeComponents(const ShapeAdaptor &aShape, const ShapeAdaptor &bShape, bool transposeB, SmallVectorImpl< ShapedTypeComponents > &inferredReturnShapes)
static void buildMatMulTOpWithQuantInfo(OpBuilder &builder, OperationState &result, Type outputType, Value a, Value b)
static FailureOr< SmallVector< int64_t > > resolveMatMulOutputShape(const ShapeAdaptor &aShape, const ShapeAdaptor &bShape, int64_t outputRank, bool transposeB)
static FailureOr< int64_t > resolveBroadcastDim(const int64_t dim1, const int64_t dim2)
static LogicalResult verifyZeroPoint(T op, Value val, const int64_t &zp, const std::string &operand)
static LogicalResult verifyPoolingOp(T op)
static LogicalResult verifyDimIsPowerOfTwo(Operation *op, const int64_t dimSize, const llvm::StringRef dimName)
static ArrayRef< int64_t > getShape(Type type)
Returns the shape of the given type.
static void updateIfDynamic(int64_t ¤t, int64_t candidate)
void inferWeightShape(SmallVectorImpl< int64_t > &outputShape, SmallVectorImpl< int64_t > &weightSpatial)
LogicalResult getSpatialParameters(SmallVector< int64_t > &padValues, SmallVector< int64_t > &strideValues, SmallVector< int64_t > &dilationValues)
void inferInputShape(SmallVectorImpl< int64_t > &outputShape, SmallVectorImpl< int64_t > &inputSpatial)
ConvInferShapeAdaptor(Conv2DBlockScaledOp::Adaptor adaptor)
int64_t getOutputRank() const
int64_t getNumSpatialDims() const
void inferInputShape(SmallVectorImpl< int64_t > &outputShape, SmallVectorImpl< int64_t > &inputSpatial)
void inferWeightShape(SmallVectorImpl< int64_t > &outputShape, SmallVectorImpl< int64_t > &weightSpatial)
ConvInferShapeAdaptor(Conv2DOp::Adaptor adaptor)
int64_t getNumSpatialDims() const
int64_t getOutputRank() const
LogicalResult getSpatialParameters(SmallVector< int64_t > &padValues, SmallVector< int64_t > &strideValues, SmallVector< int64_t > &dilationValues)
int64_t getNumSpatialDims() const
void inferWeightShape(SmallVectorImpl< int64_t > &outputShape, SmallVectorImpl< int64_t > &weightSpatial)
int64_t getOutputRank() const
ConvInferShapeAdaptor(Conv3DOp::Adaptor adaptor)
void inferInputShape(SmallVectorImpl< int64_t > &outputShape, SmallVectorImpl< int64_t > &inputSpatial)
LogicalResult getSpatialParameters(SmallVector< int64_t > &padValues, SmallVector< int64_t > &strideValues, SmallVector< int64_t > &dilationValues)
This base class exposes generic asm parser hooks, usable across the various derived parsers.
virtual ParseResult parseCommaSeparatedList(Delimiter delimiter, function_ref< ParseResult()> parseElementFn, StringRef contextMessage=StringRef())=0
Parse a list of comma-separated items with an optional delimiter.
virtual ParseResult parseOptionalAttrDict(NamedAttrList &result)=0
Parse a named dictionary into 'result' if it is present.
virtual ParseResult parseOptionalEqual()=0
Parse a = token if present.
virtual ParseResult parseOptionalKeyword(StringRef keyword)=0
Parse the given keyword if present.
MLIRContext * getContext() const
virtual ParseResult parseRParen()=0
Parse a ) token.
virtual InFlightDiagnostic emitError(SMLoc loc, const Twine &message={})=0
Emit a diagnostic at the specified location and return failure.
virtual ParseResult parseOptionalColon()=0
Parse a : token if present.
virtual ParseResult parseOptionalAttrDictWithKeyword(NamedAttrList &result)=0
Parse a named dictionary into 'result' if the attributes keyword is present.
virtual ParseResult parseColonType(Type &result)=0
Parse a colon followed by a type.
virtual SMLoc getCurrentLocation()=0
Get the location of the next token and store it into the argument.
virtual ParseResult parseColon()=0
Parse a : token.
virtual ParseResult parseLParen()=0
Parse a ( token.
virtual ParseResult parseType(Type &result)=0
Parse a type.
virtual ParseResult parseOptionalArrowTypeList(SmallVectorImpl< Type > &result)=0
Parse an optional arrow followed by a type list.
virtual ParseResult parseFloat(double &result)=0
Parse a floating point value from the stream.
ParseResult parseKeyword(StringRef keyword)
Parse a given keyword.
virtual ParseResult parseAttribute(Attribute &result, Type type={})=0
Parse an arbitrary attribute of a given type and return it in result.
This base class exposes generic asm printer hooks, usable across the various derived printers.
virtual void printAttributeWithoutType(Attribute attr)
Print the given attribute without its type.
virtual void printAttribute(Attribute attr)
void printArrowTypeList(TypeRange &&types)
Attributes are known-constant values of operations.
MutableArrayRef< BlockArgument > BlockArgListType
Special case of IntegerAttr to represent boolean integers, i.e., signless i1 integers.
This class is a general helper class for creating context-global objects like types,...
IntegerAttr getIndexAttr(int64_t value)
IntegerAttr getIntegerAttr(Type type, int64_t value)
FloatAttr getFloatAttr(Type type, double value)
IntegerType getIntegerType(unsigned width)
StringAttr getStringAttr(const Twine &bytes)
DenseIntElementsAttr getIndexTensorAttr(ArrayRef< int64_t > values)
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.
static DenseElementsAttr get(ShapedType type, ArrayRef< Attribute > values)
Constructs a dense elements attribute from an array of element values.
An attribute that represents a reference to a dense integer vector or tensor object.
This class contains all of the information necessary to report a diagnostic to the DiagnosticEngine.
virtual InFlightDiagnostic emitError(const Twine &msg={}) const =0
Emit an error to the reader.
ImplicitLocOpBuilder maintains a 'current location', allowing use of the create<> method without spec...
This class represents a diagnostic that is inflight and set to be reported.
This class defines the main interface for locations in MLIR and acts as a non-nullable wrapper around...
MLIRContext is the top-level object for a collection of MLIR operations.
The OpAsmParser has methods for interacting with the asm parser: parsing things from it,...
virtual OptionalParseResult parseOptionalAssignmentList(SmallVectorImpl< Argument > &lhs, SmallVectorImpl< UnresolvedOperand > &rhs)=0
virtual ParseResult parseRegion(Region ®ion, ArrayRef< Argument > arguments={}, bool enableNameShadowing=false)=0
Parses a region.
virtual ParseResult resolveOperand(const UnresolvedOperand &operand, Type type, SmallVectorImpl< Value > &result)=0
Resolve an operand to an SSA value, emitting an error on failure.
ParseResult resolveOperands(Operands &&operands, Type type, SmallVectorImpl< Value > &result)
Resolve a list of operands to SSA values, emitting an error on failure, or appending the results to t...
virtual ParseResult parseOperand(UnresolvedOperand &result, bool allowResultNumber=true)=0
Parse a single SSA value operand name along with a result number if allowResultNumber is true.
This is a pure-virtual base class that exposes the asmprinter hooks necessary to implement a custom p...
virtual void printOptionalAttrDictWithKeyword(ArrayRef< NamedAttribute > attrs, ArrayRef< StringRef > elidedAttrs={})=0
If the specified operation has attributes, print out an attribute dictionary prefixed with 'attribute...
virtual void printOptionalAttrDict(ArrayRef< NamedAttribute > attrs, ArrayRef< StringRef > elidedAttrs={})=0
If the specified operation has attributes, print out an attribute dictionary with their values.
void printFunctionalType(Operation *op)
Print the complete type of an operation in functional form.
virtual void printRegion(Region &blocks, bool printEntryBlockArgs=true, bool printBlockTerminators=true, bool printEmptyBlock=false)=0
Prints a region.
This class helps build Operations.
This class indicates that op operates on tosa shape types.
Operation is the basic unit of execution within MLIR.
ResultRange result_range
Support result iteration.
bool hasTrait()
Returns true if the operation was registered with a particular trait, e.g.
OperandRange operand_range
operand_type_range getOperandTypes()
result_type_range getResultTypes()
operand_range getOperands()
Returns an iterator on the underlying Value's.
InFlightDiagnostic emitOpError(const Twine &message={})
Emit an error with the op name prefixed, like "'dim' op " which is convenient for verifiers.
ParseResult value() const
Access the internal ParseResult value.
bool has_value() const
Returns true if we contain a valid ParseResult value.
Type-safe wrapper around a void* for passing properties, including the properties structs of operatio...
This class provides an abstraction over the different types of ranges over Regions.
This diagnostic handler is a simple RAII class that registers and erases a diagnostic handler on a gi...
Adaptor class to abstract the differences between whether value is from a ShapedType or ShapedTypeCom...
bool isDynamicDim(int index) const
Returns whether the index'th dimension is dynamic.
int64_t getDimSize(int index) const
Returns the size of the index'th dimension.
int64_t getRank() const
Returns the rank of the shape.
bool hasStaticShape() const
Returns whether the shape is fully static.
int64_t getNumElements() const
Returns the number of elements in the shape.
void getDims(SmallVectorImpl< int64_t > &res) const
Populates the dimensions from shape referenced.
bool hasRank() const
Returns whether the shape has a rank.
ShapedTypeComponents that represents the components of a ShapedType.
This class allows for representing and managing the symbol table used by operations with the 'SymbolT...
Operation * lookup(StringRef name) const
Look up a symbol with the specified name, returning null if no such name exists.
Tensor types represent multi-dimensional arrays, and have two variants: RankedTensorType and Unranked...
ArrayRef< int64_t > getShape() const
Returns the shape of this tensor type.
bool hasRank() const
Returns if this type is ranked, i.e. it has a known number of dimensions.
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...
MLIRContext * getContext() const
Return the MLIRContext in which this type was uniqued.
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.
type_range getTypes() const
Range of values and shapes (corresponding effectively to Shapes dialect's ValueShape type concept).
ShapeAdaptor getShape(int index) const
Returns the shape of index'th operand.
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.
ArrayRef< T > asArrayRef() const
LogicalResult verifyAtLeastNOperands(Operation *op, unsigned numOperands)
LogicalResult verifyTosaShapeOperatorWithSameRanks(Operation *op)
LogicalResult verifyTosaResolvableShapeOperands(Operation *op)
bool getBroadcastedShape(ArrayRef< int64_t > shape1, ArrayRef< int64_t > shape2, SmallVectorImpl< int64_t > &resultShape)
Returns true and sets resultShape to the broadcasted shape from the two given shapes if they are broa...
LogicalResult convertFloatTypeFromAttribute(Type type, Attribute attr, llvm::SmallVectorImpl< char > &result)
Float type implementation of DenseElementTypeInterface::convertFromAttribute.
Attribute convertFloatTypeToAttribute(Type type, llvm::ArrayRef< char > rawData)
Float type implementation of DenseElementTypeInterface::convertToAttribute.
Operation::operand_range getIndices(Operation *op)
Get the indices that the given load/store operation is operating on.
SmallVector< unsigned > getBlockSize(AffineMap dimToLvl)
Given the dimToLvl map, returns the block sizes in a vector.
ConvOpQuantizationAttr buildConvOpQuantizationAttr(OpBuilder &builder, Value input, Value weight)
Method to build ConvOpQuantizationAttr, called from ConvOpQuantInfoBuilder/TransConvOpQuantInfoBuilde...
Type getStorageElementTypeOrSelf(Type type)
RankedTensorType getVariableType(VariableOp variableOp)
Type buildConvOpResultTypeInfo(OpBuilder &builder, Type outputType, Value input, Value weight)
construct ConvOp output type with correct bitwidth based on input/weight width.
ParseResult parseVariableOpTypeOrInitialValue(OpAsmParser &parser, DenseElementsAttr &varShapeAttr, TypeAttr &typeAttr, Attribute &initialValueAttr)
PadOpQuantizationAttr buildPadOpQuantizationAttr(OpBuilder &builder, Value input)
Builds PadOpQuantizationAttr, called from PadOpQuantInfoBuilder: inputZp: input zeropoint.
constexpr int64_t kInferableDimSize
Represents a dimension in the shape of a tensor that can be inferred based on the other provided dime...
std::pair< Value, Value > createZPsAsConst(OpBuilder &builder, Value input, Value weight)
void printVariableOpTypeOrInitialValue(OpAsmPrinter &p, Operation *op, DenseElementsAttr varShapeAttr, TypeAttr typeAttr, Attribute initialValueAttr)
FailureOr< T > getConstantScalarIntValue(Value val)
Value getTosaConstShape(ImplicitLocOpBuilder &builder, llvm::ArrayRef< int64_t > shape)
MatMulOpQuantizationAttr buildMatMulOpQuantizationAttr(OpBuilder &builder, Value a, Value b)
Builds MatMulOpQuantizationAttr, called from MatMulOpQuantInfoBuilder: aZp: input a zeropoint bZp: in...
unsigned getBitWidth(Type type)
std::optional< Value > createZeroPointTensor(OpBuilder &builder, Location loc, Type srcElemType, int64_t zp=0)
bool isa_tosa_shape_type(mlir::Type t)
SmallVector< int64_t > convertFromMlirShape(ArrayRef< int64_t > shape)
UnaryOpQuantizationAttr buildUnaryOpQuantizationAttr(OpBuilder &builder, Value input, Type outputRawType)
Builds UnaryOpQuantizationAttr UnaryOpQuantInfoBuilder: inputZp: input zeropoint outputZp: output zer...
Type getStorageElementTypeFromQuantized(quant::QuantizedType quantizedType)
Value createPadConstTensor(OpBuilder &builder, Location loc, Value src, int32_t val=0)
LogicalResult verifyBlockScaledTensorType(mlir::Type type, llvm::function_ref< mlir::InFlightDiagnostic()> emitError=nullptr, bool allowScaleValues=false)
std::string getTosaTensorTypeErrorMessage(mlir::Type type)
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.
detail::DenseArrayAttrImpl< int64_t > DenseI64ArrayAttr
LogicalResult verifyCompatibleShapes(TypeRange types1, TypeRange types2)
Returns success if the given two arrays have the same number of elements and each pair wise entries h...
Type getType(OpFoldResult ofr)
Returns the int type of the integer in ofr.
LogicalResult emitOptionalError(std::optional< Location > loc, Args &&...args)
Overloads of the above emission functions that take an optionally null location.
InFlightDiagnostic emitError(Location loc)
Utility method to emit an error message using this location.
SmallVector< SmallVector< OpFoldResult > > ReifiedRankedShapedTypeDims
Type getElementTypeOrSelf(Type type)
Return the element type or return the type itself.
LogicalResult verifyCompatibleDims(ArrayRef< int64_t > dims)
Dimensions are compatible if all non-dynamic dims are equal.
LogicalResult verifyRanksMatch(Operation *op, ShapedType lhs, ShapedType rhs, StringRef lhsName, StringRef rhsName)
Verify that two shaped types have matching ranks.
LogicalResult verifyCompatibleShape(ArrayRef< int64_t > shape1, ArrayRef< int64_t > shape2)
Returns success if the given two shapes are compatible.
detail::constant_op_matcher m_Constant()
Matches a constant foldable operation.
llvm::function_ref< Fn > function_ref
bool isPermutationVector(ArrayRef< int64_t > interchange)
Method to check if an interchange vector is a permutation.
This represents an operation in an abstracted form, suitable for use with the builder APIs.
static ValueKnowledge meet(const ValueKnowledge &lhs, const ValueKnowledge &rhs)
static ValueKnowledge getKnowledgeFromType(Type type)