30#include "llvm/ADT/APFloat.h"
31#include "llvm/ADT/SmallVectorExtras.h"
32#include "llvm/ADT/TypeSwitch.h"
40#include "mlir/Dialect/Tosa/IR/TosaOpsDialect.cpp.inc"
47#include "mlir/Dialect/Tosa/IR/TosaEnums.cpp.inc"
48#include "mlir/Dialect/Tosa/IR/TosaInterfaces.cpp.inc"
51#include "mlir/Dialect/Tosa/IR/TosaDialectBytecode.cpp.inc"
56struct TosaInlinerInterface :
public DialectInlinerInterface {
57 using DialectInlinerInterface::DialectInlinerInterface;
65 IRMapping &map)
const final {
71 IRMapping &map)
const final {
72 return (isa<tosa::IfOp>(dest->getParentOp()) ||
73 isa<tosa::WhileOp>(dest->getParentOp()));
78struct TosaDialectBytecodeInterface :
public BytecodeDialectInterface {
79 TosaDialectBytecodeInterface(Dialect *dialect)
80 : BytecodeDialectInterface(dialect) {}
85 Attribute readAttribute(DialectBytecodeReader &reader)
const override {
89 LogicalResult writeAttribute(Attribute attr,
90 DialectBytecodeWriter &writer)
const override {
91 return ::writeAttribute(attr, writer);
97 Type readType(DialectBytecodeReader &reader)
const override {
101 LogicalResult writeType(Type type,
102 DialectBytecodeWriter &writer)
const override {
103 return ::writeType(type, writer);
106 void writeVersion(DialectBytecodeWriter &writer)
const final {
110 std::unique_ptr<DialectVersion>
111 readVersion(DialectBytecodeReader &reader)
const final {
113 reader.
emitError(
"Dialect does not support versioning");
117 LogicalResult upgradeFromVersion(Operation *topLevelOp,
118 const DialectVersion &version)
const final {
131 return {&getBodyGraph()};
140 return dim == -1 ? ShapedType::kDynamic : dim;
146 Type elementType = variableOp.getType();
149 return RankedTensorType::get(
shape, elementType);
156void TosaDialect::initialize() {
158#define GET_TYPEDEF_LIST
159#include "mlir/Dialect/Tosa/IR/TosaOpsTypesBase.cpp.inc"
163#include "mlir/Dialect/Tosa/IR/TosaOps.cpp.inc"
166#define GET_ATTRDEF_LIST
167#include "mlir/Dialect/Tosa/IR/TosaAttributes.cpp.inc"
169 addInterfaces<TosaDialectBytecodeInterface, TosaInlinerInterface>();
170 declarePromisedInterfaces<
171 shard::ShardingInterface, ClampOp, SigmoidOp, TanhOp, AddOp,
172 ArithmeticRightShiftOp, BitwiseAndOp, BitwiseOrOp, BitwiseXorOp, IntDivOp,
173 LogicalAndOp, LogicalLeftShiftOp, LogicalRightShiftOp, LogicalOrOp,
174 LogicalXorOp, MaximumOp, MinimumOp, MulOp, PowOp, SubOp, AbsOp,
175 BitwiseNotOp, CeilOp, ClzOp, ExpOp, FloorOp, LogOp, LogicalNotOp,
176 NegateOp, ReciprocalOp, RsqrtOp, SelectOp, EqualOp, GreaterOp,
177 GreaterEqualOp, MatMulOp>();
184 if (llvm::isa<shapeType>(type) && llvm::isa<DenseIntElementsAttr>(value)) {
185 return tosa::ConstShapeOp::create(builder, loc, type,
186 llvm::cast<DenseIntElementsAttr>(value));
188 if (llvm::isa<ElementsAttr>(value))
189 return tosa::ConstOp::create(builder, loc, type,
190 llvm::cast<ElementsAttr>(value));
200ParseResult getShapeAndElementType(
OpAsmParser &parser,
Type parsedType,
202 TypeAttr &typeAttr) {
203 if (
auto shapedType = dyn_cast<ShapedType>(parsedType)) {
204 if (!shapedType.hasRank())
206 <<
"expected ranked type";
208 auto elementType = shapedType.getElementType();
209 typeAttr = TypeAttr::get(elementType);
216 <<
"expected shaped type";
233 <<
"expected attribute";
235 if (
auto typedAttr = dyn_cast<TypedAttr>(initialValueAttr)) {
236 return getShapeAndElementType(parser, typedAttr.getType(), varShapeAttr,
240 <<
"expected Typed attr";
243 initialValueAttr =
nullptr;
247 <<
"expected type after colon";
249 return getShapeAndElementType(parser, parsedType, varShapeAttr, typeAttr);
254 TypeAttr typeAttr,
Attribute initialValueAttr) {
255 bool needsSpace =
false;
256 if (!dyn_cast_or_null<TypedAttr>(initialValueAttr)) {
259 Type elementType = typeAttr.getValue();
260 RankedTensorType tensorType =
262 auto tensorTypeAttr = TypeAttr::get(tensorType);
267 if (initialValueAttr) {
278template <
typename EnumType>
279ParseResult parseAttrEntryWithEnumHandling(
OpAsmParser &parser,
281 llvm::StringRef name;
288 if constexpr (std::is_same_v<EnumType, tosa::RoundingMode>) {
289 if (name ==
"rounding_mode" &&
291 auto sym = symbolizeRoundingMode(kw);
294 <<
"invalid rounding_mode value: " << kw;
295 auto attr = RoundingModeAttr::get(parser.
getContext(), sym.value());
301 if constexpr (std::is_same_v<EnumType, tosa::ResizeMode>) {
303 auto sym = symbolizeResizeMode(kw);
306 <<
"invalid resize mode value: " << kw;
307 auto attr = ResizeModeAttr::get(parser.
getContext(), sym.value());
314 if constexpr (std::is_same_v<EnumType, tosa::NanPropagationMode>) {
316 auto sym = symbolizeNanPropagationMode(kw);
319 <<
"invalid nan_mode value: " << kw;
320 auto attr = NanPropagationModeAttr::get(parser.
getContext(), sym.value());
327 if constexpr (std::is_same_v<EnumType, tosa::BlockSize>) {
329 auto sym = symbolizeBlockSize(kw);
332 <<
"invalid block_size value: " << kw;
333 auto attr = BlockSizeAttr::get(parser.
getContext(), sym.value());
345template <
typename EnumType>
350 [&]() { return parser.parseOperand(operands.emplace_back()); }))
358 if (parseAttrEntryWithEnumHandling<EnumType>(parser, attrs))
375 result.addTypes(fnTy.getResults());
376 result.addAttributes(attrs);
382 parser << namedAttr.
getName().strref() <<
" = ";
384 if (
auto roundingModeAttr = dyn_cast<tosa::RoundingModeAttr>(attr)) {
385 parser << roundingModeAttr.getValue();
386 }
else if (
auto resizeModeAttr = dyn_cast<tosa::ResizeModeAttr>(attr)) {
387 parser << resizeModeAttr.getValue();
388 }
else if (
auto nanPropagationModeAttr =
389 dyn_cast<tosa::NanPropagationModeAttr>(attr)) {
390 parser << nanPropagationModeAttr.getValue();
391 }
else if (
auto blockSizeAttr = dyn_cast<tosa::BlockSizeAttr>(attr)) {
392 parser << blockSizeAttr.getValue();
405 const auto kDefaultNanValue = NanPropagationMode::PROPAGATE;
407 if (
auto nanAttr = dyn_cast<NanPropagationModeAttr>(attr.getValue())) {
408 if (nanAttr.getValue() == kDefaultNanValue) {
410 toPrint.erase(attr.getName());
416 if (!toPrint.empty()) {
418 llvm::interleaveComma(toPrint, parser, [&](
const NamedAttribute namedAttr) {
419 printNamedAttr(parser, namedAttr);
435 llvm::interleaveComma(op->
getAttrs(), parser,
437 printNamedAttr(parser, namedAttr);
449 return parseWithEnumHandling<tosa::RoundingMode>(parser,
result);
453 printWithEnumHandling(parser, *
this);
457 return parseWithEnumHandling<tosa::RoundingMode>(parser,
result);
461 printWithEnumHandling(parser, *
this);
465 return parseWithEnumHandling<tosa::ResizeMode>(parser,
result);
469 printWithEnumHandling(parser, *
this);
473 return parseWithEnumHandling<tosa::NanPropagationMode>(parser,
result);
477 printWithNanPropagationHandling(parser, *
this);
481 return parseWithEnumHandling<tosa::NanPropagationMode>(parser,
result);
485 printWithNanPropagationHandling(parser, *
this);
488ParseResult MaxPool2dAdaptiveOp::parse(
OpAsmParser &parser,
490 return parseWithEnumHandling<tosa::NanPropagationMode>(parser,
result);
494 printWithNanPropagationHandling(parser, *
this);
498 return parseWithEnumHandling<tosa::NanPropagationMode>(parser,
result);
502 printWithNanPropagationHandling(parser, *
this);
506 return parseWithEnumHandling<tosa::NanPropagationMode>(parser,
result);
510 printWithNanPropagationHandling(parser, *
this);
514 return parseWithEnumHandling<tosa::NanPropagationMode>(parser,
result);
518 printWithNanPropagationHandling(parser, *
this);
522 return parseWithEnumHandling<tosa::NanPropagationMode>(parser,
result);
526 printWithNanPropagationHandling(parser, *
this);
530 return parseWithEnumHandling<tosa::NanPropagationMode>(parser,
result);
534 printWithNanPropagationHandling(parser, *
this);
537ParseResult MatmulTBlockScaledOp::parse(
OpAsmParser &parser,
539 return parseWithEnumHandling<tosa::BlockSize>(parser,
result);
543 printWithEnumHandling(parser, *
this);
546ParseResult CastFromBlockScaledOp::parse(
OpAsmParser &parser,
548 return parseWithEnumHandling<tosa::BlockSize>(parser,
result);
551void CastFromBlockScaledOp::print(
OpAsmPrinter &parser) {
552 printWithEnumHandling(parser, *
this);
555ParseResult CastToBlockScaledOp::parse(
OpAsmParser &parser,
557 return parseWithEnumHandling<tosa::BlockSize>(parser,
result);
561 printWithEnumHandling(parser, *
this);
564ParseResult Conv2DBlockScaledOp::parse(
OpAsmParser &parser,
566 return parseWithEnumHandling<tosa::BlockSize>(parser,
result);
570 printWithEnumHandling(parser, *
this);
585 if (
auto quantType = llvm::dyn_cast<mlir::quant::QuantizedType>(srcType))
595 Value valZp, StringRef name) {
600 mlir::isa<IntegerType>(eType) && mlir::isa<IntegerType>(eZpType);
604 if (!bothInts || !sameBitWidth) {
606 <<
"expected " << name <<
" and " << name
607 <<
"_zp to both be integer of the same bitwidth, but got " << eType
608 <<
" vs. " << eZpType;
615 Value src, int32_t val) {
618 const auto padConstType = mlir::RankedTensorType::get({1}, srcType);
619 const auto padConstEType = mlir::RankedTensorType::get({1}, srcElemType);
620 const auto padConstAttr{
621 llvm::isa<FloatType>(srcElemType)
626 return tosa::ConstOp::create(builder, loc, padConstType, padConstAttr);
630 if (
auto blockScaledTy = dyn_cast<tosa::BlockScaledType>(type))
632 if (dyn_cast<tosa::mxint8Type>(type))
641 const StringRef operandName,
642 const StringRef dimName) {
643 if (ShapedType::isDynamic(currDim)) {
646 }
else if (ShapedType::isStatic(newDim) && currDim != newDim) {
648 << dimName <<
" of " << operandName <<
" to match size " << currDim
649 <<
", got " << newDim;
656 auto printDim = [&](
int64_t dim) {
657 if (ShapedType::isDynamic(dim))
663 llvm::interleaveComma(
shape,
diag, printDim);
669 StringRef outputName =
"output") {
670 assert(outputType.hasRank() &&
"expected output type to be ranked");
676 diag << outputName <<
" shape ";
678 diag <<
" to be compatible with inferred shape ";
686 const int64_t stride,
const int64_t dilation,
const llvm::StringRef dimName,
687 const llvm::StringRef dimAxis,
const llvm::StringRef padBeforeName,
688 const llvm::StringRef padAfterName) {
689 if (inputSize == ShapedType::kDynamic || kernelSize == ShapedType::kDynamic)
694 const std::optional<int64_t> calculatedOutSizeMinusOne =
idivCheck(
695 inputSize - 1 + padBefore + padAfter - (kernelSize - 1) * dilation,
697 if (!calculatedOutSizeMinusOne.has_value())
699 << dimName <<
" - 1 + pad_" << padBeforeName <<
" + pad_"
700 << padAfterName <<
" - (kernel_" << dimName <<
" - 1) * dilation_"
701 << dimAxis <<
" to be wholly divisible by stride_" << dimAxis
702 <<
", got (" << inputSize <<
" - 1 + " << padBefore <<
" + "
703 << padAfter <<
" - (" << kernelSize <<
" - 1) * " << dilation
706 const int64_t calculatedOutSize = calculatedOutSizeMinusOne.value() + 1;
707 if (outputSize != ShapedType::kDynamic && calculatedOutSize != outputSize)
709 << dimName <<
" did not match expected: "
710 <<
"calculated=" << calculatedOutSize <<
", expected=" << outputSize;
718size_t mlir::tosa::mxint8Type::getDenseElementBitSize()
const {
return 8; }
721mlir::tosa::mxint8Type::convertToAttribute(
ArrayRef<char> rawData)
const {
722 assert(rawData.size() == 1 &&
"expected 1 byte for tosa.mxint8 element");
723 const auto intType = IntegerType::get(
getContext(), 8);
724 return intType.convertToAttribute(rawData);
727LogicalResult mlir::tosa::mxint8Type::convertFromAttribute(
729 const auto intAttr = dyn_cast<IntegerAttr>(attr);
732 const Type attrType = intAttr.getType();
735 return cast<IntegerType>(attrType).convertFromAttribute(attr,
result);
744 bool allowScaleValues) {
745 const auto tensorType = llvm::cast<ShapedType>(type);
746 const BlockScaledType elemType =
747 llvm::dyn_cast<BlockScaledType>(tensorType.getElementType());
751 if (!allowScaleValues && elemType.hasScaleValues()) {
754 <<
"block scaled tensor type with scale values is not allowed";
758 if (!tensorType.hasRank())
761 if (tensorType.getRank() == 0) {
763 emitError() <<
"block scaled tensor type must have rank greater than "
769 const uint32_t blockSize =
770 BlockShapeAttr::getBlockShapeValue(elemType.getBlockShape());
772 if (allowScaleValues && elemType.hasScaleValues() &&
773 tensorType.hasStaticShape()) {
774 const size_t numBlocks = tensorType.getNumElements() / blockSize;
775 if (elemType.getScaleValues().size() != numBlocks) {
777 emitError() <<
"block scaled tensor type with scale values must have "
778 "scale values for each block, expected "
779 << numBlocks <<
", got "
780 << elemType.getScaleValues().size();
785 const int64_t blockedDimension = tensorShape.back();
786 if (ShapedType::isDynamic(blockedDimension))
789 if (blockedDimension % blockSize != 0) {
791 emitError() <<
"last dimension of block scaled tensor type ("
792 << blockedDimension <<
") must be divisible by block size ("
808 type, [ctx] {
return emitError(UnknownLoc::get(ctx)); })) &&
810 return ": " + message;
819 const auto parseScaleValue = [&]() -> ParseResult {
826 if (floatValue < 0.0)
827 return parser.
emitError(loc,
"scale value must be non-negative, got ")
830 Type attrType = scaleType;
834 if (attrType != scaleType)
835 return parser.
emitError(loc,
"parsed attribute type ")
836 << attrType <<
" does not match expected scale type " << scaleType;
838 scaleValues.push_back(FloatAttr::get(attrType, floatValue));
847 llvm::interleaveComma(scaleValues, printer, [&](
Attribute scaleValue) {
852size_t mlir::tosa::BlockScaledType::getDenseElementBitSize()
const {
854 if (isa<tosa::mxint8Type>(valueType))
860mlir::tosa::BlockScaledType::convertToAttribute(
ArrayRef<char> rawData)
const {
864 assert(rawData.size() == 1 &&
"expected 1 byte for block_scaled element");
866 if (
const auto mxint8Value = dyn_cast<tosa::mxint8Type>(valueType))
867 return mxint8Value.convertToAttribute(rawData);
868 if (!isa<FloatType>(valueType))
873LogicalResult mlir::tosa::BlockScaledType::convertFromAttribute(
876 if (
const auto mxint8Value = dyn_cast<tosa::mxint8Type>(valueType))
877 return mxint8Value.convertFromAttribute(attr,
result);
879 const auto floatAttr = dyn_cast<FloatAttr>(attr);
880 if (!floatAttr || floatAttr.getType() != valueType)
893 const auto inputType = llvm::dyn_cast<TensorType>(op.getInput().getType());
894 const auto weightType = llvm::dyn_cast<TensorType>(op.getWeight().getType());
896 auto inputEType = inputType.getElementType();
897 auto weightEType = weightType.getElementType();
899 llvm::cast<ShapedType>(op.getBias().getType()).getElementType();
901 llvm::cast<ShapedType>(op.getResult().getType()).getElementType();
902 bool biasIsFloat = llvm::isa<FloatType>(biasEType);
903 bool resultIsFloat = llvm::isa<FloatType>(resultEType);
905 if (
auto quantType = llvm::dyn_cast<mlir::quant::QuantizedType>(inputEType))
908 if (
auto quantType = llvm::dyn_cast<mlir::quant::QuantizedType>(weightEType))
911 if (
auto quantType = llvm::dyn_cast<mlir::quant::QuantizedType>(biasEType))
914 if (
auto quantType = llvm::dyn_cast<mlir::quant::QuantizedType>(resultEType))
917 if (biasIsFloat && resultIsFloat && (biasEType != resultEType)) {
921 "expect both bias and result to have same element type, got ")
922 << biasEType <<
" and " << resultEType;
926 const bool isInputBlockScaled = llvm::isa<BlockScaledType>(inputEType);
927 const bool isWeightBlockScaled = llvm::isa<BlockScaledType>(weightEType);
928 const bool isInputFloat = llvm::isa<FloatType>(inputEType);
929 const bool isWeightFloat = llvm::isa<FloatType>(weightEType);
931 const bool isInputBSorFloat = isInputBlockScaled || isInputFloat;
932 const bool isWeightBSorFloat = isWeightBlockScaled || isWeightFloat;
935 if (isInputBSorFloat != isWeightBSorFloat) {
937 "expect both input and weight to be float or not together, got ")
938 << inputEType <<
" and " << weightEType;
943 if (!isInputBlockScaled && inputEType != inputZpEType) {
944 return op.emitOpError(
"expect both input and its zero point are the same "
945 "element type, got ")
946 << inputEType <<
" and " << inputZpEType;
948 if (isInputBlockScaled && !llvm::isa<Float32Type>(inputZpEType)) {
949 return op.emitOpError(
950 "expect block scaled input to have fp32 zero point, got ")
951 << inputEType <<
" and " << inputZpEType;
955 if (!isWeightBlockScaled && weightEType != weightZpEType) {
956 return op.emitOpError(
"expect both weight and its zero point are the same "
957 "element type, got ")
958 << weightEType <<
" and " << weightZpEType;
960 if (isWeightBlockScaled && !llvm::isa<Float32Type>(weightZpEType)) {
961 return op.emitOpError(
962 "expect block scaled weight to have fp32 zero point, got ")
963 << weightEType <<
" and " << weightZpEType;
966 FailureOr<int64_t> maybeIZp = op.getInputZeroPoint();
967 if (succeeded(maybeIZp) && op.verifyInputZeroPoint(*maybeIZp).failed())
970 FailureOr<int64_t> maybeWZp = op.getWeightZeroPoint();
971 if (succeeded(maybeWZp) && op.verifyWeightZeroPoint(*maybeWZp).failed())
977LogicalResult tosa::ConstOp::verify() {
979 auto attrType = llvm::dyn_cast<TensorType>(getValuesAttr().
getType());
980 auto outputType = llvm::dyn_cast<TensorType>(getOutput().
getType());
982 if (!attrType || !outputType) {
983 emitOpError(
"expected tensors for attr/result type");
987 const Type attrElemType = attrType.getElementType();
988 const Type resultElemType = outputType.getElementType();
991 llvm::dyn_cast<mlir::quant::QuantizedType>(resultElemType)) {
996 if (
auto attrBlockScaledType =
997 llvm::dyn_cast<mlir::tosa::BlockScaledType>(attrElemType)) {
998 if (!attrBlockScaledType.hasScaleValues())
1000 "attribute block scaled type must have scale values");
1002 const auto emitAttributeError = [&op]() {
1003 return op.
emitOpError(
"attribute block scaled type is invalid: ");
1009 const BlockScaledType resultBlockScaledType =
1010 llvm::dyn_cast<mlir::tosa::BlockScaledType>(resultElemType);
1011 if (!resultBlockScaledType)
1013 "result type must be block scaled type if attribute is block "
1016 if (attrBlockScaledType.getValueType() !=
1017 resultBlockScaledType.getValueType() ||
1018 attrBlockScaledType.getScaleType() !=
1019 resultBlockScaledType.getScaleType() ||
1020 attrBlockScaledType.getBlockShape() !=
1021 resultBlockScaledType.getBlockShape())
1023 "expected block scaled element type to be compatible "
1024 "between attr and result, got ")
1025 << attrBlockScaledType <<
" vs. " << resultBlockScaledType;
1030 if (attrElemType != resultElemType)
1031 return emitOpError(
"expected same attr/result element types");
1036template <
typename T>
1039 llvm::cast<ShapedType>(op.getInput().getType()).getElementType();
1041 if (
auto quantType = llvm::dyn_cast<mlir::quant::QuantizedType>(inputEType))
1045 llvm::cast<ShapedType>(op.getResult().getType()).getElementType();
1047 if (
auto quantType = llvm::dyn_cast<mlir::quant::QuantizedType>(resultEType))
1058template <
typename T>
1061 if (llvm::any_of(padding, [](
int64_t p) {
return p < 0; }))
1062 return op.emitOpError(
"expect all padding values to be >= 0, got ")
1066 if (llvm::any_of(strides, [](
int64_t s) {
return s < 1; }))
1067 return op.emitOpError(
"expect all stride values to be >= 1, got ")
1071 if (llvm::any_of(dilations, [](
int64_t d) {
return d < 1; }))
1072 return op.emitOpError(
"expect all dilation values to be >= 1, got ")
1075 const RankedTensorType outputType =
1076 llvm::dyn_cast<RankedTensorType>(op.getOutput().getType());
1081 const RankedTensorType inputType =
1082 llvm::dyn_cast<RankedTensorType>(op.getInput().getType());
1083 const RankedTensorType weightType =
1084 llvm::dyn_cast<RankedTensorType>(op.getWeight().getType());
1086 if (inputType && weightType) {
1088 if constexpr (std::is_same<T, tosa::Conv2DOp>::value) {
1090 op, inputType.getDimSize(1), weightType.getDimSize(1),
1091 outputType.getDimSize(1), padding[0], padding[1], strides[0],
1092 dilations[0],
"height",
"y",
"top",
"bottom")))
1096 op, inputType.getDimSize(2), weightType.getDimSize(2),
1097 outputType.getDimSize(2), padding[2], padding[3], strides[1],
1098 dilations[1],
"width",
"x",
"left",
"right")))
1103 if constexpr (std::is_same<T, tosa::DepthwiseConv2DOp>::value) {
1105 op, inputType.getDimSize(1), weightType.getDimSize(0),
1106 outputType.getDimSize(1), padding[0], padding[1], strides[0],
1107 dilations[0],
"height",
"y",
"top",
"bottom")))
1111 op, inputType.getDimSize(2), weightType.getDimSize(1),
1112 outputType.getDimSize(2), padding[2], padding[3], strides[1],
1113 dilations[1],
"width",
"x",
"left",
"right")))
1118 if constexpr (std::is_same<T, tosa::Conv3DOp>::value) {
1120 op, inputType.getDimSize(1), weightType.getDimSize(1),
1121 outputType.getDimSize(1), padding[0], padding[1], strides[0],
1122 dilations[0],
"depth",
"d",
"front",
"back")))
1126 op, inputType.getDimSize(2), weightType.getDimSize(2),
1127 outputType.getDimSize(2), padding[2], padding[3], strides[1],
1128 dilations[1],
"height",
"y",
"top",
"bottom")))
1132 op, inputType.getDimSize(3), weightType.getDimSize(3),
1133 outputType.getDimSize(3), padding[4], padding[5], strides[2],
1134 dilations[2],
"width",
"x",
"left",
"right")))
1139 const RankedTensorType biasType =
1140 llvm::dyn_cast<RankedTensorType>(op.getBias().getType());
1145 const int64_t biasChannels = biasType.getDimSize(0);
1146 const int64_t outputChannels =
1147 outputType.getDimSize(outputType.getRank() - 1);
1148 if (biasChannels == ShapedType::kDynamic ||
1149 outputChannels == ShapedType::kDynamic)
1153 if (biasChannels != outputChannels && biasChannels != 1)
1154 return op.emitOpError(
1155 "bias channels expected to be equal to output channels (")
1156 << outputChannels <<
") or 1, got " << biasChannels;
1163 StringRef name1,
Type type2,
1165 auto shapeType1 = dyn_cast<ShapedType>(type1);
1166 auto shapeType2 = dyn_cast<ShapedType>(type2);
1167 if (!shapeType1 || !shapeType2)
1170 auto elemType1 = shapeType1.getElementType();
1171 auto elemType2 = shapeType2.getElementType();
1172 if (elemType1 != elemType2)
1174 <<
"require same element type for " << name1 <<
" (" << elemType1
1175 <<
") and " << name2 <<
" (" << elemType2 <<
")";
1179 <<
"require same shapes for " << name1 <<
" (" << type1 <<
") and "
1180 << name2 <<
" (" << type2 <<
")";
1190 if (list1.size() != list2.size())
1192 <<
"require same number of values in " << name1 <<
" ("
1193 << list1.size() <<
") and " << name2 <<
" (" << list2.size() <<
")";
1195 for (
auto [type1, type2] :
1212template <
typename T>
1215 op->template getParentWithTrait<OpTrait::SymbolTable>();
1222 const auto varOp = symTable.
lookup<tosa::VariableOp>(op.getName());
1226 return op->emitOpError(
"'")
1227 << op.getName() <<
"' has not been declared by 'tosa.variable'";
1241 StringRef aName =
"input",
1242 StringRef bName =
"output") {
1243 auto aTType = llvm::dyn_cast<TensorType>(aType);
1244 auto bTType = llvm::dyn_cast<TensorType>(bType);
1246 op->
emitOpError(
"expect shaped tensor for") << aName <<
", got " << aType;
1250 op->
emitOpError(
"expect shaped tensor for") << bName <<
", got" << bType;
1253 auto aElementType = aTType.getElementType();
1254 auto bElementType = bTType.getElementType();
1256 llvm::dyn_cast<mlir::quant::UniformQuantizedType>(aElementType);
1258 llvm::dyn_cast<mlir::quant::UniformQuantizedType>(bElementType);
1259 if ((aElementType.isIntOrIndexOrFloat() || aQuantType) &&
1260 (bElementType.isIntOrIndexOrFloat() || bQuantType) &&
1261 aElementType != bElementType) {
1267 << aName <<
" and " << bName <<
" to have same element type, got "
1268 << aElementType <<
" and " << bElementType;
1274LogicalResult tosa::ArgMaxOp::verify() {
1275 const ShapedType resultType = llvm::cast<ShapedType>(
getType());
1278 if (
const auto resultETy = resultType.getElementType();
1279 !resultETy.isIntOrIndex())
1280 return emitOpError(
"result tensor is not of integer type");
1282 const auto inputType = llvm::cast<ShapedType>(getInput().
getType());
1283 if (!inputType.hasRank())
1287 const int64_t axis = getAxisAttr().getInt();
1288 if (((axis < 0) || axis >= inputType.getRank()))
1289 return emitOpError(
"specified axis is outside the rank of the tensor");
1291 if (!resultType.hasRank())
1297 expectedOutputShape.erase(expectedOutputShape.begin() + axis);
1300 << expectedOutputShape <<
"', got '" << outputShape <<
"'";
1313 const bool hasKernel = kernel.size() > 0;
1314 const bool hasStrides = strides.size() > 0;
1315 const bool hasPad = padding.size() > 0;
1317 if (hasKernel && llvm::any_of(kernel, [](
int64_t s) {
return s < 1; }))
1318 return op->
emitOpError(
"expect all kernel values to be >= 1, got ")
1321 if (hasStrides && llvm::any_of(strides, [](
int64_t s) {
return s < 1; }))
1322 return op->
emitOpError(
"expect all stride values to be >= 1, got ")
1325 if (hasPad && llvm::any_of(padding, [](
int64_t p) {
return p < 0; }))
1326 return op->
emitOpError(
"expect all padding values to be >= 0, got ")
1329 if (hasKernel && hasPad) {
1331 const int64_t kernelX = kernel[1];
1332 const int64_t padLeft = padding[2];
1333 const int64_t padRight = padding[3];
1334 if (padRight >= kernelX || padLeft >= kernelX)
1335 return op->
emitOpError(
"expected left/right padding to be less than the "
1336 "width of the kernel, got pad_left=")
1337 << padLeft <<
", pad_right=" << padRight
1338 <<
", kernel_x=" << kernelX;
1340 const int64_t kernelY = kernel[0];
1341 const int64_t padTop = padding[0];
1342 const int64_t padBottom = padding[1];
1343 if (padTop >= kernelY || padBottom >= kernelY)
1344 return op->
emitOpError(
"expected top/bottom padding to be less than the "
1345 "height of the kernel, got pad_top=")
1346 << padTop <<
", pad_bottom=" << padBottom
1347 <<
", kernel_y=" << kernelY;
1350 const auto inputType = llvm::dyn_cast<RankedTensorType>(input.
getType());
1351 const auto outputType = llvm::dyn_cast<RankedTensorType>(output.
getType());
1352 if (!inputType || !outputType)
1355 if (hasKernel && hasStrides && hasPad) {
1356 const auto verifyOutputSize =
1360 const llvm::StringRef dimName,
const llvm::StringRef dimAxis,
1361 const llvm::StringRef padBeforeName,
1362 const llvm::StringRef padAfterName) -> LogicalResult {
1363 if (ShapedType::isDynamic(inputSize))
1366 const std::optional<int64_t> calculatedOutSizeMinusOne =
1367 idivCheck(inputSize + padBefore + padAfter - kernelSize, strideSize);
1368 if (!calculatedOutSizeMinusOne.has_value())
1370 << dimName <<
" + pad_" << padBeforeName <<
" + pad_"
1371 << padAfterName <<
" - kernel_" << dimAxis
1372 <<
" to be wholly divisible by stride_" << dimAxis <<
", got ("
1373 << inputSize <<
" + " << padBefore <<
" + " << padAfter <<
" - "
1374 << kernelSize <<
") / " << strideSize;
1376 const int64_t calculatedOutSize = calculatedOutSizeMinusOne.value() + 1;
1377 if (ShapedType::isStatic(outputSize) && calculatedOutSize != outputSize)
1379 << dimName <<
" did not match expected: " <<
"calculated="
1380 << calculatedOutSize <<
", expected=" << outputSize;
1385 if (failed(verifyOutputSize(inputType.getDimSize(1),
1386 outputType.getDimSize(1), kernel[0], strides[0],
1387 padding[0], padding[1],
"height",
"y",
"top",
1391 if (failed(verifyOutputSize(
1392 inputType.getDimSize(2), outputType.getDimSize(2), kernel[1],
1393 strides[1], padding[2], padding[3],
"width",
"x",
"left",
"right")))
1399template <
typename T>
1402 op.getPad(), op.getInput(), op.getOutput());
1405template <
typename T>
1409 const Type inputZpETy =
1411 const Type outputZpETy =
1414 auto accType = op.getAccType();
1415 if (llvm::isa<IntegerType>(inputETy) && !accType.isInteger(32))
1416 return op.emitOpError(
"accumulator type for integer tensor is not i32");
1418 if (inputETy.
isF16() && !(accType.isF16() || accType.isF32()))
1419 return op.emitOpError(
"accumulator type for f16 tensor is not f16/f32");
1421 if (inputETy.
isBF16() && !accType.isF32())
1422 return op.emitOpError(
"accumulator type for bf16 tensor is not f32");
1424 if (inputETy.
isF32() && !accType.isF32())
1425 return op.emitOpError(
"accumulator type for f32 tensor is not f32");
1427 if (inputETy != inputZpETy)
1428 return op.emitOpError(
"expect both input and its zero point are the same "
1429 "element type, got ")
1430 << inputETy <<
" and " << inputZpETy;
1432 if (resultETy != outputZpETy)
1433 return op.emitOpError(
"expect both output and its zero point are the same "
1434 "element type, got ")
1435 << resultETy <<
" and " << outputZpETy;
1437 FailureOr<int64_t> maybeIZp = op.getInputZeroPoint();
1438 if (succeeded(maybeIZp) && op.verifyInputZeroPoint(*maybeIZp).failed())
1441 FailureOr<int64_t> maybeOZp = op.getOutputZeroPoint();
1442 if (succeeded(maybeOZp) && op.verifyOutputZeroPoint(*maybeOZp).failed())
1449struct AdaptivePoolingConstShapeValues {
1450 llvm::SmallVector<int64_t> kernel;
1451 llvm::SmallVector<int64_t> stride;
1452 llvm::SmallVector<int64_t> pad;
1456template <
typename T>
1458 std::is_same_v<T, tosa::AvgPool2dAdaptiveOp> ||
1459 std::is_same_v<T, tosa::MaxPool2dAdaptiveOp>;
1461template <
typename T,
1462 typename std::enable_if<IsSupportedAdaptivePoolConstShapeVerifyOp<T>,
1465 T op, AdaptivePoolingConstShapeValues &values) {
1471LogicalResult tosa::AvgPool2dOp::verify() {
1479LogicalResult tosa::AvgPool2dAdaptiveOp::verify() {
1480 AdaptivePoolingConstShapeValues values;
1489 values.pad, getInput(), getOutput())))
1498LogicalResult tosa::ClampOp::verify() {
1500 llvm::cast<ShapedType>(getInput().
getType()).getElementType();
1501 if (
auto quantType =
1502 llvm::dyn_cast<mlir::quant::UniformQuantizedType>(inputETy)) {
1506 llvm::cast<ShapedType>(getOutput().
getType()).getElementType();
1507 if (
auto quantType =
1508 llvm::dyn_cast<mlir::quant::UniformQuantizedType>(outputETy)) {
1511 if (inputETy != outputETy)
1512 return emitOpError(
"input/output element types are incompatible.");
1514 auto maxValAttr = getMaxValAttr();
1515 auto minValAttr = getMinValAttr();
1519 if (inputETy.
isInteger(dataTypeBitWidth)) {
1523 auto intMaxValAttr = mlir::dyn_cast<mlir::IntegerAttr>(maxValAttr);
1524 auto intMinValAttr = mlir::dyn_cast<mlir::IntegerAttr>(minValAttr);
1525 if (!intMaxValAttr || !intMinValAttr ||
1526 (intMaxValAttr.getType() != intMinValAttr.getType()) ||
1527 (intMaxValAttr.getType() != inputETy))
1528 return emitOpError(
"min/max attributes types are incompatible with "
1529 "input/output element types.");
1532 const bool isBoolean = inputETy.
isInteger(1);
1533 const APInt minVal = intMinValAttr.getValue();
1534 const APInt maxVal = intMaxValAttr.getValue();
1535 if ((isUnsigned || isBoolean) ? maxVal.ult(minVal) : maxVal.slt(minVal))
1536 return emitOpError(
"expected min_val <= max_val, got min_val=")
1537 << minValAttr <<
", max_val=" << maxValAttr;
1542 auto floatMaxValAttr = mlir::dyn_cast<mlir::FloatAttr>(maxValAttr);
1543 auto floatMinValAttr = mlir::dyn_cast<mlir::FloatAttr>(minValAttr);
1544 if (!floatMaxValAttr || !floatMinValAttr ||
1545 (floatMaxValAttr.getType() != floatMinValAttr.getType()) ||
1546 (floatMaxValAttr.getType() != inputETy))
1547 return emitOpError(
"min/max attributes types are incompatible with "
1548 "input/output element types.");
1550 const APFloat minVal = floatMinValAttr.getValue();
1551 const APFloat maxVal = floatMaxValAttr.getValue();
1552 if (minVal.isNaN() || maxVal.isNaN())
1553 return emitOpError(
"min/max attributes should not be 'NaN', got min_val=")
1554 << minValAttr <<
", max_val=" << maxValAttr;
1556 if (maxVal < minVal)
1557 return emitOpError(
"expected min_val <= max_val, got min_val=")
1558 << minValAttr <<
", max_val=" << maxValAttr;
1578 result.addOperands({input, weight, bias, zps.first, zps.second});
1579 result.addAttribute(
"pad", pad);
1580 result.addAttribute(
"stride", stride);
1581 result.addAttribute(
"dilation", dilation);
1582 result.addAttribute(
"acc_type", accType);
1583 Type finalOutputType = outputType;
1589 result.addTypes(finalOutputType);
1600 result.addOperands({input, weight, bias, zps.first, zps.second});
1601 result.addAttribute(
"out_pad", outpad);
1602 result.addAttribute(
"stride", stride);
1603 result.addAttribute(
"acc_type", accType);
1604 Type finalOutputType = outputType;
1610 result.addTypes(finalOutputType);
1617 result.addOperands({a,
b, zps.first, zps.second});
1619 Type finalOutputType{outputType};
1622 auto inputBits = eType.getIntOrFloatBitWidth();
1624 auto outputShapedType = llvm::dyn_cast<ShapedType>(outputType);
1625 assert(outputShapedType &&
"Output must be a shaped type");
1627 IntegerType accElementType;
1628 if (inputBits == 16)
1633 finalOutputType = outputShapedType.clone(accElementType);
1635 result.addTypes(finalOutputType);
1656 DenseArrayAttr kernel, DenseArrayAttr stride,
1657 DenseArrayAttr pad, TypeAttr accType) {
1662 if (
auto quantAttr =
1664 inputZp = quantAttr.getInputZp();
1665 outputZp = quantAttr.getOutputZp();
1667 const std::optional<Value> inputZpOp =
1672 "Failed to create input zero point tensor for quantized AVG_POOL2D op");
1674 const std::optional<Value> outputZpOp =
1677 (
void)
emitError(loc,
"Failed to create output zero point tensor for "
1678 "quantized AVG_POOL2D op");
1681 if (inputZpOp && outputZpOp) {
1682 result.addOperands({input, inputZpOp.value(), outputZpOp.value()});
1687 result.addOperands({input});
1689 result.addAttribute(
"kernel", kernel);
1690 result.addAttribute(
"stride", stride);
1691 result.addAttribute(
"pad", pad);
1692 result.addAttribute(
"acc_type", accType);
1693 result.types.push_back(outputType);
1706 if (
auto quantAttr =
1708 inputZp = quantAttr.getInputZp();
1709 outputZp = quantAttr.getOutputZp();
1711 const std::optional<Value> inputZpOp =
1715 "Failed to create input zero point tensor for quantized "
1716 "AVG_POOL2D_ADAPTIVE op");
1718 const std::optional<Value> outputZpOp =
1721 (
void)
emitError(loc,
"Failed to create output zero point tensor for "
1722 "quantized AVG_POOL2D_ADAPTIVE op");
1725 if (inputZpOp && outputZpOp) {
1730 result.addOperands({input, inputZpOp.value(), outputZpOp.value(),
1731 kernelShape, strideShape, padShape});
1736 result.addOperands({input});
1738 result.addAttribute(
"acc_type", accType);
1739 result.types.push_back(outputType);
1753 input1Zp = quantAttr.getInputZp();
1754 outputZp = quantAttr.getOutputZp();
1756 const std::optional<Value> input1ZpOp =
1760 loc,
"Failed to create input1 zero point for quantized NEGATE op");
1763 const std::optional<Value> outputZpOp =
1767 loc,
"Failed to create output zero point for quantized NEGATE op");
1770 if (input1ZpOp && outputZpOp) {
1771 result.addOperands({input, input1ZpOp.value(), outputZpOp.value()});
1776 result.addOperands({input});
1779 result.types.push_back(outputType);
1792 zp =
static_cast<int32_t
>(quantAttr.getInputZp());
1795 result.addOperands({input, paddings, padConstOp});
1796 result.types.push_back(outputType);
1800 StringRef name,
Type variableType,
1805 auto shapedType = dyn_cast<ShapedType>(variableType);
1807 (
void)
emitError(loc,
"variable type must be a shaped type");
1810 if (!shapedType.hasRank()) {
1811 (
void)
emitError(loc,
"variable type must be a ranked type");
1815 auto elementType = shapedType.getElementType();
1816 auto elementTypeAttr = TypeAttr::get(elementType);
1820 result.addAttribute(
"sym_name", nameAttr);
1821 result.addAttribute(
"var_shape", varShapeAttr);
1822 result.addAttribute(
"type", elementTypeAttr);
1823 result.addAttribute(
"initial_value", initialValue);
1836 if (ShapedType::isStatic(dim1) && ShapedType::isStatic(dim2) && dim1 != dim2)
1840 return ShapedType::isDynamic(dim1) ? dim2 : dim1;
1846 for (
int i = 0, e = operands.size(); i != e; ++i) {
1848 if (!
shape.hasRank()) {
1853 outRank = std::max<int64_t>(outRank,
shape.getRank());
1856 outShape.resize(outRank, 1);
1858 for (
int i = 0, e = operands.size(); i != e; ++i) {
1860 auto rankDiff = outShape.size() -
shape.getRank();
1862 for (
size_t i = 0, e =
shape.getRank(); i < e; ++i) {
1863 auto dim1 = outShape[i + rankDiff];
1864 auto dim2 =
shape.getDimSize(i);
1866 const FailureOr<int64_t> maybeResolvedDim =
1868 if (failed(maybeResolvedDim))
1870 const int64_t resolvedDim = *maybeResolvedDim;
1871 outShape[i + rankDiff] = resolvedDim;
1878LogicalResult tosa::ArgMaxOp::inferReturnTypeComponents(
1879 MLIRContext *context, ::std::optional<Location> location,
1880 ArgMaxOp::Adaptor adaptor,
1883 IntegerAttr axis = adaptor.getProperties().axis;
1884 int32_t axisVal = axis.getValue().getSExtValue();
1886 if (!inputShape.hasRank()) {
1892 outShape.reserve(inputShape.getRank() - 1);
1893 for (
int i = 0, s = inputShape.getRank(); i < s; i++) {
1896 outShape.push_back(inputShape.getDimSize(i));
1903LogicalResult tosa::RFFT2dOp::inferReturnTypeComponents(
1904 MLIRContext *context, ::std::optional<Location> location,
1905 RFFT2dOp::Adaptor adaptor,
1907 ShapeAdaptor inputShape(adaptor.getInputReal().getType());
1909 if (!inputShape.hasRank())
1913 outputShape.resize(3, ShapedType::kDynamic);
1914 outputShape[0] = inputShape.getDimSize(0);
1915 outputShape[1] = inputShape.getDimSize(1);
1916 int64_t inWidth = inputShape.getDimSize(2);
1920 if (inWidth != ShapedType::kDynamic)
1921 outputShape[2] = inWidth / 2 + 1;
1930 const llvm::StringRef dimName) {
1931 const bool isPowerOfTwo = (dimSize & (dimSize - 1)) == 0 && dimSize > 0;
1934 << dimName <<
" to be a power of two, got " << dimSize;
1939LogicalResult tosa::RFFT2dOp::verify() {
1940 const auto outputTypes = getResultTypes();
1942 return emitOpError(
"expected output shapes to match, got ") << outputTypes;
1944 const auto inputType =
1945 llvm::dyn_cast<RankedTensorType>(getInputReal().
getType());
1949 const int64_t height = inputType.getDimSize(1);
1950 if (ShapedType::isStatic(height) &&
1954 const int64_t width = inputType.getDimSize(2);
1955 if (ShapedType::isStatic(width) &&
1959 const auto outputType = llvm::dyn_cast<RankedTensorType>(outputTypes[0]);
1965 outputType.getShape().drop_back())))
1966 return emitOpError(
"expected batch and height dimensions of input/output "
1967 "to match, got input=")
1968 << inputType <<
" output=" << outputType;
1971 const int64_t outputWidth = outputType.getDimSize(2);
1972 if (ShapedType::isStatic(width) && ShapedType::isStatic(outputWidth) &&
1973 (outputWidth != (width / 2) + 1))
1975 "expected output width to be equal to input_width / 2 + 1, got ")
1981LogicalResult tosa::FFT2dOp::inferReturnTypeComponents(
1982 MLIRContext *context, ::std::optional<Location> location,
1983 FFT2dOp::Adaptor adaptor,
1985 inferredReturnShapes.push_back(
1987 inferredReturnShapes.push_back(
1992LogicalResult tosa::FFT2dOp::verify() {
1993 const auto inputRealType =
1994 llvm::dyn_cast<RankedTensorType>(getInputReal().
getType());
1995 const auto inputImagType =
1996 llvm::dyn_cast<RankedTensorType>(getInputImag().
getType());
1997 if (!inputRealType || !inputImagType)
2000 const auto trySelectStaticDim = [](
const int64_t a,
const int64_t b) {
2001 return ShapedType::isDynamic(a) ? a :
b;
2004 const int64_t height = trySelectStaticDim(inputRealType.getDimSize(1),
2005 inputImagType.getDimSize(1));
2006 if (ShapedType::isStatic(height) &&
2010 const int64_t width = trySelectStaticDim(inputRealType.getDimSize(2),
2011 inputImagType.getDimSize(2));
2012 if (ShapedType::isStatic(width) &&
2019LogicalResult tosa::ConcatOp::inferReturnTypeComponents(
2020 MLIRContext *context, ::std::optional<Location> location,
2021 ConcatOp::Adaptor adaptor,
2024 const Properties &prop = adaptor.getProperties();
2025 int32_t axis = prop.axis.getValue().getSExtValue();
2027 bool hasRankedInput =
false;
2028 for (
auto operand : adaptor.getOperands()) {
2030 if (!operandShape.hasRank())
2034 if (!hasRankedInput)
2035 outputShape.resize(operandShape.getRank(), ShapedType::kDynamic);
2038 for (
int i = 0, s = operandShape.getRank(); i < s; i++) {
2039 if (i == axis || operandShape.isDynamicDim(i))
2041 if (outputShape[i] == ShapedType::kDynamic)
2042 outputShape[i] = operandShape.getDimSize(i);
2043 if (outputShape[i] != operandShape.getDimSize(i))
2045 "Cannot concat tensors with different sizes"
2046 " on the non-axis dimension ",
2050 hasRankedInput =
true;
2053 if (adaptor.getInput1().empty())
2057 llvm::cast<TensorType>(adaptor.getInput1().getType()[0]).getElementType();
2058 if (!hasRankedInput) {
2065 for (
auto operand : adaptor.getOperands()) {
2070 if (!operandShape.hasRank() || operandShape.isDynamicDim(axis)) {
2071 concatDimSize = ShapedType::kDynamic;
2075 concatDimSize += operandShape.getDimSize(axis);
2078 outputShape[axis] = concatDimSize;
2084LogicalResult tosa::ConcatOp::verify() {
2086 auto outType = getOutput().getType();
2090 if (inputList.empty())
2093 if (!llvm::all_of(inputList, [&](
auto input) {
2095 *
this, input.getType(), outType));
2100 const int32_t axis = getAxis();
2102 for (
const auto &input : inputList) {
2103 const Type inputType = input.getType();
2105 if (currShape.hasRank()) {
2106 firstRankedInputShape = currShape;
2108 if (axis < 0 || axis >= firstRankedInputShape.
getRank())
2109 return emitOpError(
"expect axis to be within range 0 < axis < "
2110 "rank(input1[firstRankedTensorIdx]), got ")
2116 const auto allOperandsHasRank = [](
const Value input) {
2119 if (llvm::all_of(inputList, allOperandsHasRank)) {
2122 for (
const auto &[
index, input] : llvm::enumerate(inputList.drop_front())) {
2124 const int64_t inputRank = inputShape.getRank();
2125 const size_t operandNum =
index + 1;
2128 if (inputRank != firstInputRank)
2130 "expect all operands to have the same rank, but got ")
2131 << firstInputRank <<
" vs " << inputRank <<
" on operands 0 and "
2135 for (
int i = 0; i < inputRank; i++) {
2136 const int64_t inputDim = inputShape.getDimSize(i);
2138 if (i == axis || firstRankedInputShape.
isDynamicDim(i) ||
2139 inputShape.isDynamicDim(i))
2141 if (inputDim != firstInputDim)
2142 return emitOpError(
"expect all operand shapes to have the same sizes "
2143 "on non-axis dimensions, but got ")
2144 << inputDim <<
" vs " << firstInputDim <<
" at index " << i
2145 <<
" on operands 0 and " << operandNum;
2150 if (outputShape.hasRank() && outputShape.getRank() != firstInputRank)
2151 return emitOpError(
"expect output rank to match inputs rank, got ")
2152 << outputShape.getRank() <<
" vs " << firstInputRank;
2156 for (
const auto &input : inputList) {
2158 if (inputShape.isDynamicDim(axis)) {
2163 axisSum += inputShape.getDimSize(axis);
2166 if (axisSum >= 0 && outputShape.hasRank() &&
2167 !outputShape.isDynamicDim(axis) &&
2168 axisSum != outputShape.getDimSize(axis))
2169 return emitOpError(
"requires sum of axis dimensions of input1 "
2170 "equal to output axis dimension, got ")
2171 << axisSum <<
" and " << outputShape.getDimSize(axis);
2177LogicalResult tosa::EqualOp::inferReturnTypeComponents(
2178 MLIRContext *context, ::std::optional<Location> location,
2182 auto elementType = IntegerType::get(context, 1);
2195 if (l.size() != r.size() || l.size() != 1)
2200LogicalResult tosa::MatMulOp::inferReturnTypeComponents(
2201 MLIRContext *context, ::std::optional<Location> location,
2202 MatMulOp::Adaptor adaptor,
2209 outShape.resize(3, ShapedType::kDynamic);
2211 if (lhsShape.hasRank()) {
2212 outShape[0] = lhsShape.getDimSize(0);
2213 outShape[1] = lhsShape.getDimSize(1);
2216 if (rhsShape.hasRank()) {
2217 outShape[0] = outShape[0] == ShapedType::kDynamic ? rhsShape.getDimSize(0)
2219 outShape[2] = rhsShape.getDimSize(2);
2226template <
typename T>
2228 Type bElementType) {
2229 const auto aQuantizedEType =
2230 llvm::dyn_cast<quant::UniformQuantizedType>(aElementType);
2231 const auto bQuantizedEType =
2232 llvm::dyn_cast<quant::UniformQuantizedType>(bElementType);
2234 if (aQuantizedEType || bQuantizedEType) {
2235 if (!aQuantizedEType || !bQuantizedEType) {
2236 return op.emitOpError(
"expect operands to be both quantized or both not "
2238 << aElementType <<
" and " << bElementType;
2241 auto aQuantWidth = aQuantizedEType.getStorageTypeIntegralWidth();
2242 auto bQuantWidth = bQuantizedEType.getStorageTypeIntegralWidth();
2243 if (aQuantWidth != bQuantWidth) {
2244 return op.emitOpError(
"expect quantized operands to have same widths, "
2246 << aQuantWidth <<
" and " << bQuantWidth;
2253template <
typename T>
2255 StringRef inputName,
2260 Type expectedElementType = inputStorageElementType;
2262 if (isa<BlockScaledType>(inputElementType))
2263 expectedElementType = Float32Type::get(op.getContext());
2265 if (expectedElementType == zpElementType)
2269 diag << inputName <<
" and " << zpName;
2270 if (isa<BlockScaledType>(inputElementType))
2271 diag <<
" have compatible element types, got " << inputElementType
2272 <<
" and " << zpElementType;
2274 diag <<
" have the same element type, got " << inputStorageElementType
2275 <<
" and " << zpElementType;
2279LogicalResult MatMulOp::verify() {
2282 const Type aElementType = aShape.getElementType();
2283 const Type bElementType = bShape.getElementType();
2293 FailureOr<int64_t> maybeAZp = getAZeroPoint();
2294 if (succeeded(maybeAZp) && verifyAZeroPoint(*maybeAZp).failed())
2297 FailureOr<int64_t> maybeBZp = getBZeroPoint();
2298 if (succeeded(maybeBZp) && verifyBZeroPoint(*maybeBZp).failed())
2302 int64_t N = ShapedType::kDynamic;
2303 int64_t H = ShapedType::kDynamic;
2307 if (aShape.hasRank()) {
2308 N = aShape.getDimSize(0);
2309 H = aShape.getDimSize(1);
2310 C = aShape.getDimSize(2);
2313 if (bShape.hasRank()) {
2319 W = bShape.getDimSize(2);
2323 const auto outputType = cast<ShapedType>(getResult().
getType());
2324 if (outputType.hasRank() &&
2326 expectedOutputShape)))
2332LogicalResult tosa::MatMulTOp::inferReturnTypeComponents(
2333 MLIRContext *context, ::std::optional<Location> location,
2334 MatMulTOp::Adaptor adaptor,
2341 if (lhsShape.hasRank()) {
2342 outShape[0] = lhsShape.getDimSize(0);
2343 outShape[1] = lhsShape.getDimSize(1);
2346 if (rhsShape.hasRank()) {
2347 const int64_t bBatchSize = rhsShape.getDimSize(0);
2348 if (bBatchSize != 1 && ShapedType::isDynamic(outShape[0]))
2349 outShape[0] = bBatchSize;
2350 outShape[2] = rhsShape.getDimSize(1);
2357LogicalResult MatMulTOp::verify() {
2360 const Type aElementType = aShape.getElementType();
2361 const Type bElementType = bShape.getElementType();
2371 FailureOr<int64_t> maybeAZp = getAZeroPoint();
2372 if (succeeded(maybeAZp) && verifyAZeroPoint(*maybeAZp).failed())
2375 FailureOr<int64_t> maybeBZp = getBZeroPoint();
2376 if (succeeded(maybeBZp) && verifyBZeroPoint(*maybeBZp).failed())
2380 int64_t N = ShapedType::kDynamic;
2381 int64_t D = ShapedType::kDynamic;
2382 int64_t H = ShapedType::kDynamic;
2386 if (aShape.hasRank()) {
2387 N = aShape.getDimSize(0);
2388 H = aShape.getDimSize(1);
2389 C = aShape.getDimSize(2);
2392 if (bShape.hasRank()) {
2393 D = bShape.getDimSize(0);
2394 W = bShape.getDimSize(1);
2401 if (ShapedType::isStatic(N) && ShapedType::isStatic(D) && N != D && D != 1)
2402 return emitOpError(
"expect B matrix batch size to be broadcast compatible "
2404 << D <<
" vs N=" << N;
2406 if (ShapedType::isDynamic(N) && ShapedType::isStatic(D) && D != 1)
2410 const auto outputType = cast<ShapedType>(getResult().
getType());
2411 if (outputType.hasRank() &&
2413 expectedOutputShape)))
2419LogicalResult tosa::MatmulTBlockScaledOp::inferReturnTypeComponents(
2420 MLIRContext *context, ::std::optional<Location> location,
2421 MatmulTBlockScaledOp::Adaptor adaptor,
2425 const auto aDataShape = cast<ShapedType>(adaptor.getAData().getType());
2426 if (aDataShape.hasRank()) {
2427 outShape[0] = aDataShape.getDimSize(0);
2428 outShape[1] = aDataShape.getDimSize(1);
2431 const auto aScaleShape = cast<ShapedType>(adaptor.getAScale().getType());
2432 if (aScaleShape.hasRank()) {
2433 outShape[0] = ShapedType::isDynamic(outShape[0]) ? aScaleShape.getDimSize(0)
2435 outShape[1] = ShapedType::isDynamic(outShape[1]) ? aScaleShape.getDimSize(1)
2440 const auto bDataShape = cast<ShapedType>(adaptor.getBData().getType());
2441 if (bDataShape.hasRank()) {
2442 const int64_t bDataBatchSize = bDataShape.getDimSize(0);
2443 if (bDataBatchSize != 1)
2445 ShapedType::isDynamic(outShape[0]) ? bDataBatchSize : outShape[0];
2446 outShape[2] = bDataShape.getDimSize(1);
2449 const auto bScaleShape = cast<ShapedType>(adaptor.getBScale().getType());
2450 if (bScaleShape.hasRank()) {
2451 const int64_t bScaleBatchSize = bScaleShape.getDimSize(0);
2452 if (bScaleBatchSize != 1)
2454 ShapedType::isDynamic(outShape[0]) ? bScaleBatchSize : outShape[0];
2455 outShape[2] = ShapedType::isDynamic(outShape[2]) ? bScaleShape.getDimSize(1)
2463LogicalResult MatmulTBlockScaledOp::verify() {
2465 const Type aDataType = getAData().getType();
2466 const Type bDataType = getBData().getType();
2472 int64_t N = ShapedType::kDynamic;
2473 int64_t D = ShapedType::kDynamic;
2474 int64_t H = ShapedType::kDynamic;
2477 int64_t multiplesOfC = ShapedType::kDynamic;
2489 "a_scale",
"batch")) ||
2491 "a_scale",
"height")))
2499 "b_data",
"batch")) ||
2501 "b_data",
"channels")))
2509 "b_scale",
"batch")) ||
2511 "b_scale",
"width")) ||
2519 if (ShapedType::isStatic(N) && ShapedType::isStatic(D) && N != D && D != 1)
2520 return emitOpError(
"expect B matrix batch size to be broadcast compatible "
2522 << D <<
" vs N=" << N;
2525 const uint32_t blockSize = BlockSizeAttr::getBlockSizeValue(
getBlockSize());
2526 if (blockSize != BlockSizeAttr::getBlockSizeValue(BlockSize::BLOCK_SIZE_32))
2527 return emitOpError(
"expect block size to be 32, got ") << blockSize;
2528 if (ShapedType::isStatic(C) && C % blockSize != 0)
2529 return emitOpError(
"expect C to be a multiple of block size, got C=")
2530 <<
C <<
", block_size=" << blockSize;
2533 if (ShapedType::isStatic(C) && ShapedType::isStatic(multiplesOfC) &&
2534 multiplesOfC != C / blockSize)
2536 "expect scale operands dimension 2 to equal C/block_size (")
2537 <<
C <<
"/" << blockSize <<
")" <<
", got " << multiplesOfC;
2540 N = ShapedType::isDynamic(N) ? D : N;
2542 const auto outputType = cast<ShapedType>(getResult().
getType());
2543 if (outputType.hasRank() &&
2548 opError <<
" to be compatible with expected output shape ";
2556LogicalResult tosa::PadOp::inferReturnTypeComponents(
2557 MLIRContext *context, ::std::optional<Location> location,
2558 PadOp::Adaptor adaptor,
2560 ShapeAdaptor inputShape(adaptor.getInput1().getType());
2562 cast<tosa::shapeType>(adaptor.getPadding().getType()).getRank();
2567 if (!inputShape.hasRank()) {
2568 outputShape.resize(paddingRank / 2, ShapedType::kDynamic);
2577 outputShape.resize(inputShape.getRank(), ShapedType::kDynamic);
2582 outputShape.reserve(inputShape.getRank());
2583 for (
int i = 0, s = inputShape.getRank(); i < s; i++) {
2584 if (inputShape.isDynamicDim(i)) {
2585 outputShape.push_back(ShapedType::kDynamic);
2588 auto padFront = paddingValues[i * 2];
2589 auto padBack = paddingValues[i * 2 + 1];
2590 if (padFront < 0 || padBack < 0) {
2592 outputShape.push_back(ShapedType::kDynamic);
2596 outputShape.push_back(inputShape.getDimSize(i) + padFront + padBack);
2603LogicalResult tosa::PadOp::verify() {
2610 if (
auto padConst = getPadConst()) {
2618 RankedTensorType inputType =
2619 llvm::dyn_cast<RankedTensorType>(getInput1().
getType());
2620 RankedTensorType outputType =
2621 llvm::dyn_cast<RankedTensorType>(getOutput().
getType());
2622 if (!inputType || !outputType)
2629 auto inputRank = inputType.getRank();
2634 auto paddingValues = paddingAttr.getValues<APInt>();
2635 if (paddingValues.size() !=
static_cast<size_t>(inputRank * 2))
2636 return emitOpError() <<
"padding tensor must have " << inputRank
2637 <<
" * 2 = " << inputRank * 2 <<
" elements, but got "
2638 << paddingValues.size();
2640 auto inputShape = inputType.getShape();
2641 auto outputShape = outputType.getShape();
2643 for (
int64_t i = 0; i < inputRank; ++i) {
2644 int64_t padStart = paddingValues[i * 2].getSExtValue();
2645 int64_t padEnd = paddingValues[i * 2 + 1].getSExtValue();
2647 if ((padStart < 0 && padStart != -1) || (padEnd < 0 && padEnd != -1)) {
2649 <<
"invalid padding values at dimension " << i
2650 <<
": values must be non-negative or -1 for dynamic padding, got ["
2651 << padStart <<
", " << padEnd <<
"]";
2655 if (inputShape[i] == ShapedType::kDynamic ||
2656 outputShape[i] == ShapedType::kDynamic)
2659 if (outputShape[i] != inputShape[i] + padStart + padEnd) {
2660 return emitOpError() <<
"mismatch in output shape at dimension " << i
2661 <<
": expected " << inputShape[i] <<
" + "
2662 << padStart <<
" + " << padEnd <<
" = "
2663 << (inputShape[i] + padStart + padEnd)
2664 <<
", but got " << outputShape[i];
2671LogicalResult tosa::SliceOp::inferReturnTypeComponents(
2672 MLIRContext *context, ::std::optional<Location> location,
2673 SliceOp::Adaptor adaptor,
2682 auto rank = cast<tosa::shapeType>(adaptor.getSize().getType()).getRank();
2690 ShapeAdaptor inputShape(adaptor.getInput1().getType());
2693 if (inputShape.hasRank()) {
2694 for (
size_t i = 0; i < size.size(); i++) {
2695 if (size[i] != 0 && size[i] >= -1 && start[i] >= 0 &&
2696 (ShapedType::isDynamic(inputShape.getDimSize(i)) ||
2697 start[i] < inputShape.getDimSize(i))) {
2699 if (ShapedType::isDynamic(inputShape.getDimSize(i))) {
2702 outputShape[i] = size[i];
2706 if (size[i] == -1) {
2707 outputShape[i] = inputShape.getDimSize(i) - start[i];
2708 }
else if (start[i] + size[i] <= inputShape.getDimSize(i)) {
2710 outputShape[i] = size[i];
2722LogicalResult tosa::SliceOp::verify() {
2723 const Value input = getInput1();
2724 const Value output = getOutput();
2730 const Value start = getStart();
2731 const Value size = getSize();
2735 if (inputShape.hasRank()) {
2736 const auto inputRank = inputShape.getRank();
2737 if (outputShape.hasRank() && inputRank != outputShape.getRank())
2739 "expect input1 and output to have the same ranks, got ")
2740 << inputRank <<
" and " << outputShape.getRank();
2742 const auto startShapeRank =
2743 llvm::cast<tosa::shapeType>(start.
getType()).getRank();
2744 if (inputRank != startShapeRank)
2745 return emitOpError(
"length of start is not equal to rank of input shape");
2747 const auto sizeShapeRank =
2748 llvm::cast<tosa::shapeType>(size.
getType()).getRank();
2749 if (inputRank != sizeShapeRank)
2750 return emitOpError(
"length of size is not equal to rank of input shape");
2755 if (startValues.size()) {
2756 if (llvm::any_of(startValues, [](
const int64_t v) {
2759 return emitOpError(
"start values must be non-negative, got [")
2760 << startValues <<
"]";
2767 if (llvm::any_of(sizeValues, [](
const int64_t v) {
2770 return emitOpError(
"size values must be > 0, got [") << sizeValues <<
"]";
2771 if (outputShape.hasRank()) {
2773 outputShape.getDims(outputDims);
2774 const bool hasNoInferableDims = llvm::all_of(
2776 if (hasNoInferableDims &&
2778 return emitOpError(
"expected output shape to match size values, got ")
2779 << output.
getType() <<
" vs [" << sizeValues <<
"]";
2782 if (inputShape.hasRank() && startValues.size()) {
2784 inputShape.getDims(inputDims);
2785 for (
const auto &[
index, vals] :
2786 llvm::enumerate(llvm::zip_equal(startValues, sizeValues, inputDims))) {
2787 const auto &[start, size, inputDim] = vals;
2789 ShapedType::isDynamic(inputDim))
2791 if (start + size > inputDim)
2792 return emitOpError(
"start + size must be less than or equal to input "
2793 "dimension size, got start=")
2794 << start <<
", size=" << size
2795 <<
" vs input dim size=" << inputDim <<
" at dimension "
2803LogicalResult tosa::MulOp::inferReturnTypeComponents(
2804 MLIRContext *context, ::std::optional<Location> location,
2819LogicalResult tosa::MulOp::verify() {
2820 const Value output = getOutput();
2825 if (
auto resIntType = dyn_cast<IntegerType>(resElemType)) {
2826 IntegerType lhsIntType =
2828 IntegerType rhsIntType =
2830 if (!lhsIntType || !rhsIntType || lhsIntType != rhsIntType)
2831 return emitOpError(
"requires the same element type for all operands");
2836 if (lhsIntType.getWidth() > resIntType.getWidth())
2837 return emitOpError(
"invalid data type size for operands or result");
2842 for (
int i = 0; i < 2; ++i) {
2845 "requires the same element type for all operands and results");
2849 ElementsAttr shiftElem;
2851 int32_t shift = shiftElem.getValues<IntegerAttr>()[0].getInt();
2853 return emitOpError() <<
"require shift to be 0 for float type";
2861 TypeRange operandTypes = getOperandTypes();
2862 ShapedType aType = cast<ShapedType>(operandTypes[0]);
2863 ShapedType bType = cast<ShapedType>(operandTypes[1]);
2865 const bool aHasRank = aType.hasRank();
2866 const bool bHasRank = bType.hasRank();
2868 bool hasExpectedOutputShape =
false;
2871 if (aHasRank && bHasRank) {
2872 const int64_t aRank = aType.getRank();
2873 const int64_t bRank = bType.getRank();
2875 return emitOpError(
"a and b operands don't have matching ranks, got ")
2876 << aRank <<
" and " << bRank;
2880 aType.getShape(), bType.getShape(), expectedOutputShape))
2881 return emitOpError(
"a and b operands don't have broadcast-compatible "
2883 << aType <<
" and " << bType;
2884 hasExpectedOutputShape =
true;
2887 ShapedType resultType = cast<ShapedType>(output.
getType());
2888 if (!resultType.hasRank())
2891 const int64_t resultRank = resultType.getRank();
2892 if (aHasRank && resultRank != aType.getRank())
2893 return emitOpError(
"result type has different rank than a, got ")
2894 << resultRank <<
" vs " << aType.getRank();
2895 if (bHasRank && resultRank != bType.getRank())
2896 return emitOpError(
"result type has different rank than b, got ")
2897 << resultRank <<
" vs " << bType.getRank();
2899 if (hasExpectedOutputShape &&
2901 expectedOutputShape)))
2907LogicalResult tosa::TableOp::inferReturnTypeComponents(
2908 MLIRContext *context, ::std::optional<Location> location,
2909 TableOp::Adaptor adaptor,
2911 ShapeAdaptor inputShape(adaptor.getInput1().getType());
2913 if (!inputShape.hasRank()) {
2918 inferredReturnShapes.resize(1);
2919 inputShape.getDims(inferredReturnShapes[0]);
2923LogicalResult tosa::TableOp::verify() {
2924 const TensorType inputType = getInput1().getType();
2925 const TensorType outputType = getOutput().getType();
2934 auto inputDims = inputType.
getShape();
2935 auto outputDims = outputType.
getShape();
2936 for (
auto it : llvm::enumerate(llvm::zip(inputDims, outputDims))) {
2938 auto [inputDim, outputDim] = it.value();
2939 if (ShapedType::isStatic(outputDim) && outputDim != inputDim) {
2940 return emitOpError() <<
"dim(result, " << dim <<
") = " << outputDim
2941 <<
" doesn't match dim(input, " << dim
2942 <<
") = " << inputDim;
2955 llvm::map_to_vector(multiplesAttr.getValues<APInt>(),
2956 [](
const APInt &val) { return val.getSExtValue(); });
2960LogicalResult tosa::TileOp::inferReturnTypeComponents(
2961 MLIRContext *context, ::std::optional<Location> location,
2962 TileOp::Adaptor adaptor,
2969 cast<tosa::shapeType>(adaptor.getMultiples().getType()).getRank();
2976 ShapeAdaptor inputShape(adaptor.getInput1().getType());
2978 if (!inputShape.hasRank()) {
2979 outputShape.resize(multiples.size(), ShapedType::kDynamic);
2980 inferredReturnShapes.push_back(
2984 if (
static_cast<size_t>(inputShape.getRank()) != multiples.size())
2988 outputShape.reserve(multiples.size());
2989 for (
int i = 0, s = inputShape.getRank(); i < s; i++) {
2990 if (multiples[i] == ShapedType::kDynamic) {
2991 outputShape.push_back(ShapedType::kDynamic);
2993 int64_t dim = inputShape.getDimSize(i);
2994 if (dim != ShapedType::kDynamic)
2995 dim *= multiples[i];
2996 outputShape.push_back(dim);
3004LogicalResult tosa::TileOp::verify() {
3010 ShapedType inputType = llvm::cast<ShapedType>(getInput1().
getType());
3011 ShapedType outputType = llvm::cast<ShapedType>(
getType());
3013 shapeType multiplesType =
3014 llvm::cast<tosa::shapeType>(getMultiples().
getType());
3016 auto multiplesRank = multiplesType.getRank();
3018 if (inputType.hasRank()) {
3019 if (inputType.getRank() != multiplesRank)
3020 return emitOpError(
"expect 'multiples' to have rank ")
3021 << inputType.getRank() <<
" but got " << multiplesRank <<
".";
3022 if (outputType.hasRank() &&
3026 }
else if (outputType.hasRank() && outputType.getRank() != multiplesRank)
3027 return emitOpError(
"expect 'multiples' array to have length ")
3028 << outputType.getRank() <<
" but got " << multiplesRank <<
".";
3031 if (getConstantMultiples(multiples).succeeded() &&
3032 llvm::any_of(multiples, [](
int64_t v) {
return v <= 0 && v != -1; }))
3034 "expect element of 'multiples' to be positive integer or -1.");
3040 if (l.size() != r.size() || l.size() != 1)
3045LogicalResult tosa::ReshapeOp::inferReturnTypeComponents(
3046 MLIRContext *context, ::std::optional<Location> location,
3047 ReshapeOp::Adaptor adaptor,
3049 ShapeAdaptor inputShape(adaptor.getInput1().getType());
3054 auto rank = cast<tosa::shapeType>(adaptor.getShape().getType()).getRank();
3063 if (!inputShape.hasRank() || !inputShape.hasStaticShape()) {
3064 inferredReturnShapes.push_back(
3072 int64_t numElements = inputShape.getNumElements();
3074 for (
auto val : newShapeValue) {
3075 if (ShapedType::isStatic(val)) {
3081 for (
auto &val : newShapeValue) {
3082 if (ShapedType::isDynamic(val))
3083 val = numElements / staticMul;
3086 inferredReturnShapes.push_back(
3091llvm::LogicalResult tosa::ReshapeOp::verify() {
3097 TensorType inputType = getInput1().getType();
3102 return mlir::success();
3106 if (missingDims > 1)
3107 return emitOpError() <<
"expected at most one target dimension to be "
3110 const auto outputType = dyn_cast<RankedTensorType>(
getType());
3114 if ((
int64_t)shapeValues.size() != outputType.getRank())
3115 return emitOpError() <<
"new shape does not match result rank";
3117 for (
auto [newShapeDim, outputShapeDim] :
3118 zip(shapeValues, outputType.getShape())) {
3120 newShapeDim != ShapedType::kDynamic &&
3121 outputShapeDim != ShapedType::kDynamic && newShapeDim != outputShapeDim)
3122 return emitOpError() <<
"new shape is inconsistent with result shape";
3125 return emitOpError() <<
"new shape has invalid tensor dimension size "
3129 if (inputType.hasStaticShape()) {
3130 int64_t inputElementsNum = inputType.getNumElements();
3131 if (outputType.hasStaticShape()) {
3132 int64_t outputElementsNum = outputType.getNumElements();
3133 if (inputElementsNum != outputElementsNum) {
3134 return emitOpError() <<
"cannot reshape " << inputElementsNum
3135 <<
" elements into " << outputElementsNum;
3141 return (dim > 0) ?
acc * dim :
acc;
3143 bool isStaticNewShape =
3144 llvm::all_of(shapeValues, [](
int64_t s) {
return s > 0; });
3145 if ((isStaticNewShape && inputElementsNum != newShapeElementsNum) ||
3146 (!isStaticNewShape && newShapeElementsNum > inputElementsNum)) {
3147 return emitOpError() <<
"cannot reshape " << inputElementsNum
3148 <<
" elements into " << newShapeElementsNum;
3152 return mlir::success();
3155bool tosa::ReshapeBlockScaledOp::isCompatibleReturnTypes(
TypeRange l,
3157 if (l.size() != r.size() || l.size() < 1 || l.size() > 2)
3165LogicalResult tosa::ReshapeBlockScaledOp::inferReturnTypeComponents(
3166 MLIRContext *context, ::std::optional<Location> location,
3167 ReshapeBlockScaledOp::Adaptor adaptor,
3170 const auto numInputs = adaptor.getInput().size();
3171 ShapeAdaptor inputShape(adaptor.getInput()[0].getType());
3174 const auto newShape = adaptor.getNewValueShape();
3176 auto rank = cast<tosa::shapeType>(newShape.getType()).getRank();
3185 const uint32_t blockSize =
3186 BlockSizeAttr::getBlockSizeValue(adaptor.getBlockSize());
3189 if (numInputs == 2) {
3190 newScaleShapeValue.assign(newShapeValue.begin(), newShapeValue.end());
3191 if (ShapedType::isStatic(newScaleShapeValue.back()))
3192 newScaleShapeValue.back() /= blockSize;
3195 inferredReturnShapes.push_back(
3197 if (numInputs == 2) {
3199 for (
size_t idx = 0; idx < newShapeValue.size(); idx++) {
3200 if (ShapedType::isDynamic(newScaleShapeValue[idx])) {
3201 newScaleShapeValue[idx] = newShapeValue[idx];
3202 if (idx == (newShapeValue.size() - 1))
3203 newScaleShapeValue[idx] /= blockSize;
3214llvm::LogicalResult tosa::ReshapeBlockScaledOp::verify() {
3218 if (inputList.size() == 0)
3219 return emitOpError(
"requires at least one input");
3221 if (inputList.size() > 2)
3222 return emitOpError(
"requires at most two inputs");
3224 if (inputList.size() != outputList.size())
3225 return emitOpError(
"requires number of results to match inputs");
3233 const auto inputType = llvm::cast<ShapedType>(inputList[0].
getType());
3234 if (!inputType.hasRank())
3236 const uint32_t blockSize = BlockSizeAttr::getBlockSizeValue(
getBlockSize());
3238 if (inputList.size() == 2) {
3239 if (blockSize != BlockSizeAttr::getBlockSizeValue(BlockSize::BLOCK_SIZE_32))
3240 return emitOpError(
"expect block size to be 32, got ") << blockSize;
3241 if (llvm::any_of(inputList, [](
Value v) {
3242 const auto input = cast<ShapedType>(v.
getType());
3243 return input.hasRank() && input.getRank() == 0;
3246 "requires all input shapes have a rank greater than 0");
3247 if (llvm::any_of(outputList, [](
Value v) {
3248 const auto output = cast<ShapedType>(v.
getType());
3249 return output.hasRank() && output.getRank() == 0;
3252 "requires all result shapes have a rank greater than 0");
3260 const auto inputScaleType = llvm::cast<ShapedType>(inputList[1].
getType());
3261 if (inputScaleType.hasRank()) {
3262 if (inputType.getRank() != inputScaleType.getRank())
3263 return emitOpError(
"input shapes do not have same rank");
3266 for (
auto dimIdx = 0; dimIdx < inputType.getRank() - 1; dimIdx++) {
3267 const int64_t inputValueDim = inputType.getDimSize(dimIdx);
3268 const int64_t inputScaleDim = inputScaleType.getShape()[dimIdx];
3269 if (ShapedType::isStatic(inputValueDim) &&
3270 ShapedType::isStatic(inputScaleDim) &&
3271 inputValueDim != inputScaleDim)
3272 return emitOpError(
"input shapes for data and scale do not match on "
3279 inputType.getDimSize(inputType.getRank() - 1);
3280 if (ShapedType::isStatic(lastValueDim)) {
3281 if (lastValueDim % blockSize != 0)
3282 return emitOpError(
"expect last dimension of input_data (")
3283 << lastValueDim <<
") to be divisible by block_size ("
3284 << blockSize <<
")";
3287 inputScaleType.getDimSize(inputScaleType.getRank() - 1);
3289 if (ShapedType::isStatic(lastScaleDim) &&
3290 lastScaleDim != lastValueDim / blockSize)
3291 return emitOpError(
"expect last dimension of scale_data (")
3292 << lastScaleDim <<
") to be " << lastValueDim <<
"/"
3297 if (blockSize != BlockSizeAttr::getBlockSizeValue(BlockSize::BLOCK_SIZE_1))
3298 return emitOpError(
"expect block size to be 1, got ") << blockSize;
3306 return mlir::success();
3309 if (inputList.size() == 2) {
3310 if (
static_cast<int64_t>(shapeValues.size()) == 0)
3311 return emitOpError(
"requires new shape to have a rank greater than 0");
3313 const int64_t lastShapeDim = shapeValues.back();
3314 if (ShapedType::isStatic(lastShapeDim) && lastShapeDim % blockSize != 0)
3315 return emitOpError(
"expect last dimension of new shape (")
3316 << lastShapeDim <<
") to be divisible by block_size (" << blockSize
3320 const auto outputType = llvm::cast<ShapedType>(outputList[0].
getType());
3321 if (!outputType.hasRank())
3324 if (
static_cast<int64_t>(shapeValues.size()) != outputType.getRank())
3325 return emitOpError() <<
"result does not match new shape rank";
3327 for (
auto [newShapeDim, outputShapeDim] :
3328 zip(shapeValues, outputType.getShape())) {
3329 if (ShapedType::isStatic(newShapeDim) &&
3330 ShapedType::isStatic(outputShapeDim) && newShapeDim != outputShapeDim)
3331 return emitOpError() <<
"result shape is inconsistent with new shape";
3334 if (outputList.size() == 2) {
3338 scaleShapeValues.back() /= blockSize;
3340 const auto outputScaleType =
3341 llvm::cast<ShapedType>(outputList[1].
getType());
3342 if (outputScaleType.hasRank()) {
3343 if ((
int64_t)scaleShapeValues.size() != outputScaleType.getRank())
3344 return emitOpError() <<
"result scale does not match new shape rank";
3346 for (
auto [newScaleShapeDim, outputScaleShapeDim] :
3347 zip(scaleShapeValues, outputScaleType.getShape())) {
3348 if (ShapedType::isStatic(newScaleShapeDim) &&
3349 ShapedType::isStatic(outputScaleShapeDim) &&
3350 newScaleShapeDim != outputScaleShapeDim)
3352 <<
"result scale shape is inconsistent with new shape";
3357 if (inputType.hasStaticShape()) {
3358 int64_t inputElementsNum = inputType.getNumElements();
3359 if (outputType.hasStaticShape()) {
3360 int64_t outputElementsNum = outputType.getNumElements();
3361 if (inputElementsNum != outputElementsNum) {
3362 return emitOpError() <<
"cannot reshape " << inputElementsNum
3363 <<
" elements into " << outputElementsNum;
3369 return (dim > 0) ?
acc * dim :
acc;
3371 bool isStaticNewShape =
3372 llvm::all_of(shapeValues, [](
int64_t s) {
return s > 0; });
3373 if ((isStaticNewShape && inputElementsNum != newShapeElementsNum) ||
3374 (!isStaticNewShape && newShapeElementsNum > inputElementsNum)) {
3375 return emitOpError() <<
"cannot reshape " << inputElementsNum
3376 <<
" elements into " << newShapeElementsNum;
3380 return mlir::success();
3387 ElementsAttr zpAttr;
3392 Type zpElemType = zpAttr.getElementType();
3394 if (llvm::isa<FloatType>(zpElemType)) {
3395 if (zpAttr.getValues<APFloat>()[0].isZero()) {
3402 if (llvm::isa<IntegerType>(zpElemType)) {
3404 return zpAttr.getValues<APInt>()[0].getSExtValue();
3405 return zpAttr.getValues<APInt>()[0].getZExtValue();
3412template <
typename T>
3414 const std::string &operand) {
3417 if (!zpElemType.
isInteger(8) && zp != 0) {
3419 std::string lower = operand;
3420 llvm::transform(lower, lower.begin(), ::tolower);
3421 return op.emitOpError()
3422 << lower <<
" zero point must be zero for non-int8 integer types";
3430 const std::string &operand) {
3431 bool isInputZp = (operand ==
"Input");
3433 bool tensorUnsigned =
3434 isInputZp ? op.getInputUnsigned() : op.getOutputUnsigned();
3435 StringRef tensorName = isInputZp ?
"input" :
"output";
3441 !(zpElemType.
isInteger(16) && tensorUnsigned)) {
3442 return op.emitOpError()
3443 <<
"expect " << tensorName <<
"_zp of 0, got " << zp;
3445 if (zpElemType.
isInteger(16) && tensorUnsigned && zp != 32768) {
3446 return op.emitOpError() <<
"expect " << tensorName
3447 <<
"_zp of 0 or 32768 for unsigned int16 "
3448 << tensorName <<
", got " << zp;
3455#define ZERO_POINT_HELPER(OP, OPERAND_NAME, SIGN_EXTEND) \
3456 FailureOr<int64_t> tosa::OP::get##OPERAND_NAME##ZeroPoint() { \
3457 return getZeroPoint(get##OPERAND_NAME##Zp(), SIGN_EXTEND); \
3459 LogicalResult tosa::OP::verify##OPERAND_NAME##ZeroPoint(int64_t zp) { \
3460 return verifyZeroPoint(*this, get##OPERAND_NAME##Zp(), zp, #OPERAND_NAME); \
3483#undef ZERO_POINT_HELPER
3485LogicalResult tosa::TransposeOp::inferReturnTypeComponents(
3486 MLIRContext *context, ::std::optional<Location> location,
3487 TransposeOp::Adaptor adaptor,
3489 ShapeAdaptor inputShape(adaptor.getInput1().getType());
3498 const auto inputRank = inputShape.
getRank();
3502 if (adaptor.getPerms().size() !=
static_cast<size_t>(inputRank)) {
3508 if (inputRank == 0) {
3514 bool allTheSame =
true;
3515 for (
int i = 1, s = inputRank; i < s; i++) {
3525 outputShape.resize(inputRank, inputShape.
getDimSize(0));
3530 outputShape.resize(inputRank, ShapedType::kDynamic);
3533 if (llvm::any_of(adaptor.getPerms(),
3534 [inputRank](
const auto i) { return i >= inputRank; }))
3537 outputShape.reserve(inputRank);
3538 for (
int i = 0, s = inputRank; i < s; i++) {
3539 outputShape[i] = inputShape.
getDimSize(adaptor.getPerms()[i]);
3546LogicalResult tosa::TransposeOp::verify() {
3558 if (inputShape.hasRank() &&
3559 constantPerms.size() !=
static_cast<size_t>(inputShape.getRank()))
3560 return emitOpError() <<
"expected perms attribute to have size "
3561 << inputShape.getRank()
3562 <<
" (input rank) but got size "
3563 << constantPerms.size();
3565 if (inputShape.hasRank() && outputShape.hasRank() &&
3566 inputShape.getRank() != outputShape.getRank())
3568 <<
"expected input tensor rank to equal result tensor rank";
3570 if (outputShape.hasRank() &&
3571 constantPerms.size() !=
static_cast<size_t>(outputShape.getRank()))
3572 return emitOpError() <<
"expected perms attribute to have size "
3573 << outputShape.getRank()
3574 <<
" (output rank) but got size "
3575 << constantPerms.size();
3577 if (!llvm::all_of(constantPerms,
3578 [&constantPerms](int32_t s) {
3580 static_cast<size_t>(s) < constantPerms.size();
3583 constantPerms, [](int32_t v) ->
int64_t {
return v; })))
3584 return emitOpError() <<
"expected valid permutation indices";
3587 if (inputShape.hasStaticShape() && outputShape.hasStaticShape() &&
3588 inputShape.getNumElements() != outputShape.getNumElements())
3589 return emitOpError() <<
"expected input1 and output to have same numbers "
3591 << inputShape.getNumElements() <<
" and "
3592 << outputShape.getNumElements();
3596 if (inputShape.hasRank() && outputShape.hasRank()) {
3597 for (
auto i = 0; i < outputShape.getRank(); i++) {
3598 if (inputShape.isDynamicDim(constantPerms[i]) ||
3599 outputShape.isDynamicDim(i))
3602 if (inputShape.getDimSize(constantPerms[i]) != outputShape.getDimSize(i))
3604 <<
"expected output tensor dim " << i <<
" to match "
3605 <<
"input dim " << constantPerms[i] <<
" with value of "
3606 << inputShape.getDimSize(constantPerms[i]);
3613LogicalResult TransposeOp::reifyResultShapes(
3616 const llvm::ArrayRef<int32_t> transposePerms = getPerms();
3618 Value input = getInput1();
3619 auto inputType = cast<TensorType>(input.
getType());
3621 SmallVector<OpFoldResult> returnedDims(inputType.getRank());
3622 for (
auto dim : transposePerms) {
3623 int32_t dimInInput = transposePerms[dim];
3624 if (inputType.isDynamicDim(dimInInput))
3626 tensor::DimOp::create(builder, getLoc(), input, dimInInput)
3630 builder.
getIndexAttr(inputType.getDimSize(dimInInput));
3633 reifiedReturnShapes.emplace_back(std::move(returnedDims));
3637LogicalResult tosa::GatherOp::inferReturnTypeComponents(
3638 MLIRContext *context, ::std::optional<Location> location,
3639 GatherOp::Adaptor adaptor,
3640 SmallVectorImpl<ShapedTypeComponents> &inferredReturnShapes) {
3641 llvm::SmallVector<int64_t> outputShape;
3642 outputShape.resize(3, ShapedType::kDynamic);
3644 ShapeAdaptor valuesShape(adaptor.getValues().getType());
3645 if (valuesShape.hasRank()) {
3646 outputShape[0] = valuesShape.getDimSize(0);
3647 outputShape[2] = valuesShape.getDimSize(2);
3650 ShapeAdaptor indicesShape(adaptor.getIndices().getType());
3651 if (indicesShape.hasRank()) {
3652 if (outputShape[0] == ShapedType::kDynamic)
3653 outputShape[0] = indicesShape.getDimSize(0);
3654 if (outputShape[1] == ShapedType::kDynamic)
3655 outputShape[1] = indicesShape.getDimSize(1);
3658 inferredReturnShapes.push_back(ShapedTypeComponents(outputShape));
3662LogicalResult tosa::RowGatherOp::inferReturnTypeComponents(
3663 MLIRContext *context, ::std::optional<Location> location,
3664 RowGatherOp::Adaptor adaptor,
3665 SmallVectorImpl<ShapedTypeComponents> &inferredReturnShapes) {
3666 llvm::SmallVector<int64_t> outputShape;
3667 outputShape.resize(3, ShapedType::kDynamic);
3669 const ShapeAdaptor valuesShape(adaptor.getValues().getType());
3670 if (valuesShape.hasRank()) {
3671 outputShape[0] = valuesShape.getDimSize(0);
3672 outputShape[2] = valuesShape.getDimSize(2);
3675 const ShapeAdaptor indicesShape(adaptor.getIndices().getType());
3676 if (indicesShape.hasRank()) {
3677 if (outputShape[0] == ShapedType::kDynamic)
3678 outputShape[0] = indicesShape.getDimSize(0);
3680 const FailureOr<int32_t> maybeRowCount =
3682 if (succeeded(maybeRowCount)) {
3683 const int64_t indicesW = indicesShape.getDimSize(1);
3684 if (ShapedType::isStatic(indicesW))
3685 outputShape[1] = indicesW * maybeRowCount.value();
3689 inferredReturnShapes.push_back(ShapedTypeComponents(outputShape));
3693LogicalResult tosa::RowGatherBlockScaledOp::inferReturnTypeComponents(
3694 MLIRContext *context, ::std::optional<Location> location,
3695 RowGatherBlockScaledOp::Adaptor adaptor,
3696 SmallVectorImpl<ShapedTypeComponents> &inferredReturnShapes) {
3697 const auto values = adaptor.getValues();
3701 SmallVector<int64_t> dataShape(3, ShapedType::kDynamic);
3702 const ShapeAdaptor valuesShape(values.front().getType());
3703 if (valuesShape.hasRank()) {
3704 dataShape[0] = valuesShape.getDimSize(0);
3705 dataShape[2] = valuesShape.getDimSize(2);
3708 const ShapeAdaptor indicesShape(adaptor.getIndices().getType());
3709 if (indicesShape.hasRank()) {
3710 if (dataShape[0] == ShapedType::kDynamic)
3711 dataShape[0] = indicesShape.getDimSize(0);
3715 succeeded(rowCount) && rowCount.value() > 0) {
3716 const int64_t indicesW = indicesShape.getDimSize(1);
3717 if (ShapedType::isStatic(indicesW))
3718 dataShape[1] = indicesW * rowCount.value();
3722 inferredReturnShapes.push_back(ShapedTypeComponents(dataShape));
3723 if (values.size() == 1)
3726 SmallVector<int64_t> scaleShape = dataShape;
3727 const uint32_t blockSize =
3728 BlockSizeAttr::getBlockSizeValue(adaptor.getBlockSize());
3729 if (ShapedType::isStatic(dataShape[2]))
3730 scaleShape[2] = dataShape[2] / blockSize;
3732 inferredReturnShapes.push_back(ShapedTypeComponents(scaleShape));
3736LogicalResult tosa::GatherOp::verify() {
3743 const ShapeAdaptor valuesShape(getValues().
getType());
3745 const ShapeAdaptor outputShape(getOutput().
getType());
3747 int64_t n = ShapedType::kDynamic;
3748 int64_t w = ShapedType::kDynamic;
3749 int64_t c = ShapedType::kDynamic;
3751 if (valuesShape.hasRank()) {
3752 n = valuesShape.getDimSize(0);
3753 c = valuesShape.getDimSize(2);
3755 if (indicesShape.hasRank()) {
3756 const int64_t indicesN = indicesShape.getDimSize(0);
3757 w = indicesShape.getDimSize(1);
3758 if (n == ShapedType::kDynamic)
3760 else if (indicesN != ShapedType::kDynamic && n != indicesN)
3761 return emitOpError() <<
"requires indices dimension 0 to have size " << n
3762 <<
", got " << indicesN;
3764 if (outputShape.hasRank()) {
3765 const int64_t outputN = outputShape.getDimSize(0);
3766 const int64_t outputW = outputShape.getDimSize(1);
3767 const int64_t outputC = outputShape.getDimSize(2);
3768 if (n != ShapedType::kDynamic && outputN != ShapedType::kDynamic &&
3770 return emitOpError() <<
"requires output dimension 0 to have size " << n
3771 <<
", got " << outputN;
3773 if (w != ShapedType::kDynamic && outputW != ShapedType::kDynamic &&
3775 return emitOpError() <<
"requires output dimension 1 to have size " << w
3776 <<
", got " << outputW;
3777 if (c != ShapedType::kDynamic && outputC != ShapedType::kDynamic &&
3779 return emitOpError() <<
"requires output dimension 2 to have size " << c
3780 <<
", got " << outputC;
3785LogicalResult tosa::RowGatherOp::verify() {
3790 const FailureOr<int32_t> maybeRowCount =
3792 if (succeeded(maybeRowCount) && maybeRowCount.value() <= 0)
3793 return emitOpError() <<
"requires row_count to be > 0, got "
3794 << maybeRowCount.value();
3796 int64_t n = ShapedType::kDynamic;
3797 int64_t c = ShapedType::kDynamic;
3798 int64_t w = ShapedType::kDynamic;
3800 const ShapeAdaptor valuesShape(getValues().
getType());
3801 if (valuesShape.hasRank()) {
3802 n = valuesShape.getDimSize(0);
3803 c = valuesShape.getDimSize(2);
3807 if (indicesShape.hasRank()) {
3809 "indices",
"batch")))
3811 w = indicesShape.getDimSize(1);
3814 const ShapeAdaptor outputShape(getOutput().
getType());
3815 if (outputShape.hasRank()) {
3817 "output",
"batch")) ||
3819 "output",
"channels")))
3822 if (succeeded(maybeRowCount) && maybeRowCount.value() > 0 &&
3823 ShapedType::isStatic(w)) {
3824 const int64_t expectedOutputRows = w * maybeRowCount.value();
3825 if (ShapedType::isStatic(outputShape.getDimSize(1)) &&
3826 outputShape.getDimSize(1) != expectedOutputRows)
3828 <<
"requires output dimension to be equal to "
3829 "indices[1]*row_count ("
3830 << expectedOutputRows <<
"), got " << outputShape.getDimSize(1);
3837LogicalResult tosa::RowGatherBlockScaledOp::verify() {
3838 const OperandRange values = getValues();
3839 const ResultRange output = getOutput();
3840 if (values.empty() || values.size() > 2)
3842 <<
"expects values tensor list length to be 1 or 2, got "
3844 if (output.size() != values.size())
3846 <<
"expects output tensor list length to match values tensor list "
3848 << output.size() <<
" results for " << values.size()
3849 <<
" input tensors";
3851 const uint32_t blockSize = BlockSizeAttr::getBlockSizeValue(
getBlockSize());
3852 if (values.size() == 1 && blockSize != 1)
3854 <<
"requires block_size to be BLOCK_SIZE_1 when values tensor list "
3856 if (values.size() == 2 && blockSize == 1)
3858 <<
"requires block_size to not be BLOCK_SIZE_1 when values tensor "
3862 output[0].
getType(),
"values[0]",
3867 "values[1]",
"output[1]")))
3871 succeeded(rowCount) && rowCount.value() <= 0)
3872 return emitOpError() <<
"requires row_count to be > 0, got "
3873 << rowCount.value();
3875 int64_t n = ShapedType::kDynamic;
3876 int64_t k = ShapedType::kDynamic;
3877 int64_t c = ShapedType::kDynamic;
3878 int64_t w = ShapedType::kDynamic;
3879 int64_t multiplesOfC = ShapedType::kDynamic;
3881 const ShapeAdaptor valuesDataShape(values[0].
getType());
3882 if (valuesDataShape.hasRank()) {
3883 n = valuesDataShape.getDimSize(0);
3884 k = valuesDataShape.getDimSize(1);
3885 c = valuesDataShape.getDimSize(2);
3888 if (ShapedType::isStatic(c) && c % blockSize != 0)
3889 return emitOpError() <<
"expects channels of values[0] (" << c
3890 <<
") to be divisible by block_size (" << blockSize
3894 if (indicesShape.hasRank()) {
3896 "indices",
"batch")))
3898 w = indicesShape.getDimSize(1);
3901 const ShapeAdaptor outputDataShape(output[0].
getType());
3902 if (outputDataShape.hasRank()) {
3904 "output[0]",
"batch")) ||
3906 "output[0]",
"channels")))
3910 succeeded(rowCount) && rowCount.value() > 0 &&
3911 ShapedType::isStatic(w)) {
3912 const int64_t expectedOutputRows = w * rowCount.value();
3913 if (ShapedType::isStatic(outputDataShape.getDimSize(1)) &&
3914 outputDataShape.getDimSize(1) != expectedOutputRows)
3915 return emitOpError() <<
"requires output[0] dimension 1 to have size "
3916 << expectedOutputRows <<
", got "
3917 << outputDataShape.getDimSize(1);
3921 if (values.size() == 2) {
3922 const ShapeAdaptor valuesScaleShape(values[1].
getType());
3923 if (valuesScaleShape.hasRank()) {
3925 "values[1]",
"batch")) ||
3927 "values[1]",
"rows")))
3929 multiplesOfC = valuesScaleShape.getDimSize(2);
3932 const ShapeAdaptor outputScaleShape(output[1].
getType());
3933 if (outputScaleShape.hasRank()) {
3935 "output[1]",
"batch")))
3939 succeeded(rowCount) && rowCount.value() > 0 &&
3940 ShapedType::isStatic(w)) {
3941 const int64_t expectedOutputRows = w * rowCount.value();
3942 if (ShapedType::isStatic(outputScaleShape.getDimSize(1)) &&
3943 outputScaleShape.getDimSize(1) != expectedOutputRows)
3944 return emitOpError() <<
"requires output[1] dimension 1 to have size "
3945 << expectedOutputRows <<
", got "
3946 << outputScaleShape.getDimSize(1);
3949 if (ShapedType::isDynamic(multiplesOfC))
3950 multiplesOfC = outputScaleShape.getDimSize(2);
3951 else if (ShapedType::isStatic(outputScaleShape.getDimSize(2)) &&
3952 multiplesOfC != outputScaleShape.getDimSize(2))
3954 <<
"expected channels of output[1] to match size "
3955 << multiplesOfC <<
", got " << outputScaleShape.getDimSize(2);
3958 if (ShapedType::isStatic(c) && ShapedType::isStatic(multiplesOfC) &&
3959 multiplesOfC != c / blockSize)
3961 <<
"expects channels of scale tensors to equal C/block_size (" << c
3962 <<
"/" << blockSize <<
"), got " << multiplesOfC;
3968LogicalResult tosa::ResizeOp::inferReturnTypeComponents(
3969 MLIRContext *context, ::std::optional<Location> location,
3970 ResizeOp::Adaptor adaptor,
3971 SmallVectorImpl<ShapedTypeComponents> &inferredReturnShapes) {
3972 llvm::SmallVector<int64_t, 4> outputShape;
3973 outputShape.resize(4, ShapedType::kDynamic);
3975 ShapeAdaptor inputShape(adaptor.getInput().getType());
3976 if (!inputShape.hasRank())
3979 outputShape[0] = inputShape.getDimSize(0);
3980 outputShape[3] = inputShape.getDimSize(3);
3981 int64_t inputHeight = inputShape.getDimSize(1);
3982 int64_t inputWidth = inputShape.getDimSize(2);
3984 if ((inputHeight == ShapedType::kDynamic) ||
3985 (inputWidth == ShapedType::kDynamic))
3988 SmallVector<int64_t> scaleInt, offsetInt, borderInt;
3999 const int64_t outputHeight =
4000 (((inputHeight - 1) * scaleInt[0] - offsetInt[0] + borderInt[0]) /
4004 const int64_t outputWidth =
4005 (((inputWidth - 1) * scaleInt[2] - offsetInt[1] + borderInt[1]) /
4009 if (outputHeight < 0 || outputWidth < 0) {
4012 "calculated output height and width must be non-negative, "
4014 outputHeight,
", width = ", outputWidth);
4017 outputShape[1] = outputHeight;
4018 outputShape[2] = outputWidth;
4019 inferredReturnShapes.push_back(ShapedTypeComponents(outputShape));
4023LogicalResult tosa::ResizeOp::verify() {
4024 const Value input = getInput();
4025 const Value output = getOutput();
4028 if (isa<BlockScaledType>(inputElementType) &&
4029 getMode() != ResizeMode::NEAREST_NEIGHBOR)
4030 return emitOpError(
"requires NEAREST_NEIGHBOR mode for block scaled input");
4032 const RankedTensorType inputType =
4033 llvm::dyn_cast<RankedTensorType>(input.
getType());
4034 const RankedTensorType outputType =
4035 llvm::dyn_cast<RankedTensorType>(output.
getType());
4037 SmallVector<int64_t> scaleValues;
4038 SmallVector<int64_t> offsetValues;
4039 SmallVector<int64_t> borderValues;
4047 if (llvm::any_of(scaleValues, [](int64_t s) {
return s <= 0; }))
4048 return emitOpError(
"expect all scale values to be > 0, got ")
4051 const int64_t scaleYN = scaleValues[0];
4052 const int64_t scaleYD = scaleValues[1];
4053 const int64_t scaleXN = scaleValues[2];
4054 const int64_t scaleXD = scaleValues[3];
4056 const int64_t offsetY = offsetValues[0];
4057 const int64_t offsetX = offsetValues[1];
4059 const int64_t borderY = borderValues[0];
4060 const int64_t borderX = borderValues[1];
4067 const int64_t oh = outputType.getDimSize(1);
4068 const int64_t ow = outputType.getDimSize(2);
4069 const int64_t ih = inputType.getDimSize(1);
4070 const int64_t iw = inputType.getDimSize(2);
4076 if (ih != ShapedType::kDynamic && ih != 1) {
4077 const std::optional<int64_t> calculatedOutHeightMinusOne =
4078 idivCheck((ih - 1) * scaleYN - offsetY + borderY, scaleYD);
4079 if (!calculatedOutHeightMinusOne.has_value())
4080 return emitOpError(
"expected (input_height - 1) * scale_y_n - offset_y + "
4082 <<
"to be wholly divisible by scale_y_d, got ((" << ih
4083 <<
" - 1) * " << scaleYN <<
" - " << offsetY <<
" + " << borderY
4084 <<
") / " << scaleYD;
4085 const int64_t calculatedOutHeight = calculatedOutHeightMinusOne.value() + 1;
4086 if (oh != ShapedType::kDynamic && calculatedOutHeight != oh)
4087 return emitOpError(
"calculated output height did not match expected: ")
4088 <<
"calculated=" << calculatedOutHeight <<
", expected=" << oh;
4095 if (iw != ShapedType::kDynamic && iw != 1) {
4096 const int64_t scaledInWidth = (iw - 1) * scaleXN - offsetX + borderX;
4097 const std::optional<int64_t> calculatedOutWidthMinusOne =
4099 if (!calculatedOutWidthMinusOne.has_value())
4100 return emitOpError(
"expected (input_width - 1) * scale_x_n - offset_x + "
4102 <<
"to be wholly divisible by scale_x_d, got ((" << iw
4103 <<
" - 1) * " << scaleXN <<
" - " << offsetX <<
" + " << borderX
4104 <<
") / " << scaleXD;
4105 const int64_t calculatedOutWidth = calculatedOutWidthMinusOne.value() + 1;
4106 if (ow != ShapedType::kDynamic && calculatedOutWidth != ow)
4107 return emitOpError(
"calculated output width did not match expected: ")
4108 <<
"calculated=" << calculatedOutWidth <<
", expected=" << ow;
4114LogicalResult tosa::ScatterOp::inferReturnTypeComponents(
4115 MLIRContext *context, ::std::optional<Location> location,
4116 ScatterOp::Adaptor adaptor,
4117 SmallVectorImpl<ShapedTypeComponents> &inferredReturnShapes) {
4118 llvm::SmallVector<int64_t> outputShape;
4119 outputShape.resize(3, ShapedType::kDynamic);
4121 ShapeAdaptor valuesInShape(adaptor.getValuesIn().getType());
4122 if (valuesInShape.hasRank()) {
4123 outputShape[0] = valuesInShape.getDimSize(0);
4124 outputShape[1] = valuesInShape.getDimSize(1);
4125 outputShape[2] = valuesInShape.getDimSize(2);
4128 ShapeAdaptor indicesShape(adaptor.getIndices().getType());
4129 if (indicesShape.hasRank()) {
4130 if (outputShape[0] == ShapedType::kDynamic)
4131 outputShape[0] = indicesShape.getDimSize(0);
4134 ShapeAdaptor inputShape(adaptor.getInput().getType());
4135 if (inputShape.hasRank()) {
4136 if (outputShape[0] == ShapedType::kDynamic)
4137 outputShape[0] = inputShape.getDimSize(0);
4138 if (outputShape[2] == ShapedType::kDynamic)
4139 outputShape[2] = inputShape.getDimSize(2);
4142 inferredReturnShapes.push_back(ShapedTypeComponents(outputShape));
4146LogicalResult tosa::ScatterOp::verify() {
4156 const ShapeAdaptor valuesInShape(getValuesIn().
getType());
4158 const ShapeAdaptor inputShape(getInput().
getType());
4159 const ShapeAdaptor outputShape(getValuesOut().
getType());
4161 int64_t n = ShapedType::kDynamic;
4162 int64_t k = ShapedType::kDynamic;
4163 int64_t w = ShapedType::kDynamic;
4164 int64_t c = ShapedType::kDynamic;
4165 if (valuesInShape.hasRank()) {
4166 n = valuesInShape.getDimSize(0);
4167 k = valuesInShape.getDimSize(1);
4168 c = valuesInShape.getDimSize(2);
4170 if (indicesShape.hasRank()) {
4171 const int64_t indicesN = indicesShape.getDimSize(0);
4172 w = indicesShape.getDimSize(1);
4173 if (n == ShapedType::kDynamic)
4175 else if (indicesN != ShapedType::kDynamic && n != indicesN)
4176 return emitOpError() <<
"requires indices dimension 0 to have size " << n
4177 <<
", got " << indicesN;
4179 if (inputShape.hasRank()) {
4180 const int64_t inputN = inputShape.getDimSize(0);
4181 const int64_t inputW = inputShape.getDimSize(1);
4182 const int64_t inputC = inputShape.getDimSize(2);
4183 if (n == ShapedType::kDynamic)
4185 else if (inputN != ShapedType::kDynamic && n != inputN)
4186 return emitOpError() <<
"requires input dimension 0 to have size " << n
4187 <<
", got " << inputN;
4188 if (w == ShapedType::kDynamic)
4190 else if (inputW != ShapedType::kDynamic && w != inputW)
4191 return emitOpError() <<
"requires input dimension 1 to have size " << w
4192 <<
", got " << inputW;
4194 if (c == ShapedType::kDynamic)
4196 else if (inputC != ShapedType::kDynamic && c != inputC)
4197 return emitOpError() <<
"requires input dimension 2 to have size " << c
4198 <<
", got " << inputC;
4200 if (outputShape.hasRank()) {
4201 const int64_t outputN = outputShape.getDimSize(0);
4202 const int64_t outputK = outputShape.getDimSize(1);
4203 const int64_t outputC = outputShape.getDimSize(2);
4204 if (n != ShapedType::kDynamic && outputN != ShapedType::kDynamic &&
4206 return emitOpError() <<
"requires values_out dimension 0 to have size "
4207 << n <<
", got " << outputN;
4208 if (k == ShapedType::kDynamic)
4210 else if (outputK != ShapedType::kDynamic && k != outputK)
4211 return emitOpError() <<
"requires values_out dimension 1 to have size "
4212 << k <<
", got " << outputK;
4213 if (c != ShapedType::kDynamic && outputC != ShapedType::kDynamic &&
4215 return emitOpError() <<
"requires values_out dimension 2 to have size "
4216 << c <<
", got " << outputC;
4218 if (k != ShapedType::kDynamic && w != ShapedType::kDynamic && !(k >= w))
4219 return emitOpError() <<
"requires dimensions K >= W, got K=" << k
4228 int64_t axisVal = axis.getValue().getSExtValue();
4229 if (!operandShape.
hasRank() || operandShape.
getRank() <= axisVal) {
4235 operandShape.
getDims(outputShape);
4236 outputShape[axisVal] = 1;
4241#define COMPATIBLE_RETURN_TYPES(OP) \
4242 bool OP::isCompatibleReturnTypes(TypeRange l, TypeRange r) { \
4243 if (l.size() != r.size() || l.size() != 1) \
4245 if (getElementTypeOrSelf(l[0]) != getElementTypeOrSelf(r[0])) \
4247 return succeeded(verifyCompatibleShape(l[0], r[0])); \
4250#define REDUCE_SHAPE_INFER(OP) \
4251 LogicalResult OP::inferReturnTypeComponents( \
4252 MLIRContext *context, ::std::optional<Location> location, \
4253 OP::Adaptor adaptor, \
4254 SmallVectorImpl<ShapedTypeComponents> &inferredReturnShapes) { \
4256 llvm::cast<TensorType>(adaptor.getInput().getType()).getElementType(); \
4257 ShapeAdaptor inputShape(adaptor.getInput().getType()); \
4258 const Properties &prop = adaptor.getProperties(); \
4259 return ReduceInferReturnTypes(inputShape, inputType, prop.axis, \
4260 inferredReturnShapes); \
4262 COMPATIBLE_RETURN_TYPES(OP)
4270#undef REDUCE_SHAPE_INFER
4272#undef COMPATIBLE_RETURN_TYPES
4274template <
typename T>
4277 TensorType inputType = op.getInput().getType();
4278 TensorType outputType = op.getOutput().getType();
4279 int32_t reduceAxis = op.getAxis();
4281 if (reduceAxis < 0) {
4282 op.emitOpError(
"reduce axis must not be negative");
4286 int64_t inputRank = inputType.getRank();
4289 if (reduceAxis >= inputRank && (reduceAxis != 0 || inputRank != 0)) {
4290 op.emitOpError(
"expect input tensor rank (")
4291 << inputRank <<
") to be larger than reduce axis (" << reduceAxis
4297 int64_t outputRank = outputType.getRank();
4298 if (inputType.
hasRank() && outputRank != inputType.getRank()) {
4300 "expect output tensor rank to be equal to input tensor rank");
4303 if (reduceAxis >= outputRank && (reduceAxis != 0 || outputRank != 0)) {
4304 op.emitOpError(
"expect output tensor rank (")
4305 << outputRank <<
") to be larger than reduce axis (" << reduceAxis
4311 if (outputRank != 0) {
4312 auto outputShape = outputType.
getShape();
4313 if (!outputType.isDynamicDim(reduceAxis) &&
4314 outputShape[reduceAxis] != 1) {
4315 op.emitOpError(
"expect reduced dimension size to be 1, got ")
4316 << outputShape[reduceAxis];
4324LogicalResult tosa::ReduceAllOp::verify() {
return verifyReduceOp(*
this); }
4325LogicalResult tosa::ReduceAnyOp::verify() {
return verifyReduceOp(*
this); }
4326LogicalResult tosa::ReduceMaxOp::verify() {
return verifyReduceOp(*
this); }
4327LogicalResult tosa::ReduceMinOp::verify() {
return verifyReduceOp(*
this); }
4328LogicalResult tosa::ReduceProductOp::verify() {
return verifyReduceOp(*
this); }
4329LogicalResult tosa::ReduceSumOp::verify() {
return verifyReduceOp(*
this); }
4343#define NARY_SHAPE_INFER(OP) \
4344 LogicalResult OP::inferReturnTypeComponents( \
4345 MLIRContext *context, ::std::optional<Location> location, \
4346 ValueShapeRange operands, DictionaryAttr attributes, \
4347 PropertyRef properties, RegionRange regions, \
4348 SmallVectorImpl<ShapedTypeComponents> &inferredReturnShapes) { \
4349 return NAryInferReturnTypes(operands, inferredReturnShapes); \
4389#undef PRED_SHAPE_INFER
4391LogicalResult tosa::NegateOp::inferReturnTypeComponents(
4392 MLIRContext *context, ::std::optional<Location> location,
4393 NegateOp::Adaptor adaptor,
4395 ShapeAdaptor inputShape(adaptor.getInput1().getType());
4400LogicalResult tosa::NegateOp::verify() {
4402 const Type input1Type = getInput1().getType();
4403 const Type outputType = getOutput().getType();
4408 const SmallVector<Type, 2> types = {input1Type, outputType};
4410 return emitOpError() <<
"requires the same shape for input1 and output";
4413 const Type input1ZpEType =
4415 if (input1EType != input1ZpEType) {
4416 return emitOpError(
"expect both input1 and its zero point are the same "
4417 "element type, got ")
4418 << input1EType <<
" and " << input1ZpEType;
4421 const Type outputZpEType =
4423 if (outputEType != outputZpEType) {
4424 return emitOpError(
"expect both output and its zero point are the same "
4425 "element type, got ")
4426 << outputEType <<
" and " << outputZpEType;
4429 FailureOr<int64_t> maybeIZp = getInput1ZeroPoint();
4430 if (succeeded(maybeIZp) && verifyInput1ZeroPoint(*maybeIZp).failed())
4433 FailureOr<int64_t> maybeOZp = getOutputZeroPoint();
4434 if (succeeded(maybeOZp) && verifyOutputZeroPoint(*maybeOZp).failed())
4445 outputShape.resize(4, ShapedType::kDynamic);
4460 if (ShapedType::isStatic(height)) {
4461 int64_t padded = height + pad[0] + pad[1] - kernel[0];
4462 outputShape[1] = padded / stride[0] + 1;
4465 if (ShapedType::isStatic(width)) {
4466 int64_t padded = width + pad[2] + pad[3] - kernel[1];
4467 outputShape[2] = padded / stride[1] + 1;
4474template <
typename AdaptorT>
4480 if (ShapedType::isDynamic(current))
4481 current = candidate;
4490 : adaptor(adaptor) {}
4494 const ShapeAdaptor inputShape(adaptor.getInput().getType());
4502 outputShape[0] = outputBatch;
4503 inputSpatial[0] = inputHeight;
4504 inputSpatial[1] = inputWidth;
4509 const ShapeAdaptor weightShape(adaptor.getWeight().getType());
4517 outputShape[3] = outputChannels;
4518 weightSpatial[0] = kernelHeight;
4519 weightSpatial[1] = kernelWidth;
4528 padValues.assign(adaptor.getPad().begin(), adaptor.getPad().end());
4529 strideValues.assign(adaptor.getStride().begin(), adaptor.getStride().end());
4530 dilationValues.assign(adaptor.getDilation().begin(),
4531 adaptor.getDilation().end());
4536 Conv2DOp::Adaptor adaptor;
4544 : adaptor(adaptor) {}
4548 const ShapeAdaptor inputDataShape(adaptor.getInputData().getType());
4549 if (inputDataShape.
hasRank()) {
4554 outputShape[0] = outputBatch;
4555 inputSpatial[0] = inputHeight;
4556 inputSpatial[1] = inputWidth;
4559 const ShapeAdaptor inputScaleShape(adaptor.getInputScale().getType());
4560 if (!inputScaleShape.
hasRank())
4574 const ShapeAdaptor weightDataShape(adaptor.getWeightData().getType());
4575 if (weightDataShape.
hasRank()) {
4580 outputShape[3] = outputChannels;
4581 weightSpatial[0] = kernelHeight;
4582 weightSpatial[1] = kernelWidth;
4585 const ShapeAdaptor weightScaleShape(adaptor.getWeightScale().getType());
4586 if (!weightScaleShape.
hasRank())
4615 Conv2DBlockScaledOp::Adaptor adaptor;
4623 : adaptor(adaptor) {}
4627 const ShapeAdaptor inputShape(adaptor.getInput().getType());
4636 outputShape[0] = outputBatch;
4637 inputSpatial[0] = inputDepth;
4638 inputSpatial[1] = inputHeight;
4639 inputSpatial[2] = inputWidth;
4644 const ShapeAdaptor weightShape(adaptor.getWeight().getType());
4653 outputShape[4] = outputChannels;
4654 weightSpatial[0] = kernelDepth;
4655 weightSpatial[1] = kernelHeight;
4656 weightSpatial[2] = kernelWidth;
4665 padValues.assign(adaptor.getPad().begin(), adaptor.getPad().end());
4666 strideValues.assign(adaptor.getStride().begin(), adaptor.getStride().end());
4667 dilationValues.assign(adaptor.getDilation().begin(),
4668 adaptor.getDilation().end());
4673 Conv3DOp::Adaptor adaptor;
4676template <
typename AdaptorT>
4682 ShapedType::kDynamic);
4684 ShapedType::kDynamic);
4686 ShapedType::kDynamic);
4688 convShapeAdaptor.inferInputShape(outputShape, inputSpatial);
4689 convShapeAdaptor.inferWeightShape(outputShape, weightSpatial);
4691 const ShapeAdaptor biasShape = adaptor.getBias().getType();
4694 if (biasSize != 1) {
4695 const size_t outputChannelDim = convShapeAdaptor.getOutputRank() - 1;
4696 outputShape[outputChannelDim] =
4697 ShapedType::isDynamic(outputShape[outputChannelDim])
4699 : outputShape[outputChannelDim];
4706 if (failed(convShapeAdaptor.getSpatialParameters(padValues, strideValues,
4712 for (
int64_t dim = 0; dim < convShapeAdaptor.getNumSpatialDims(); ++dim) {
4713 if (!ShapedType::isStatic(inputSpatial[dim]) ||
4714 !ShapedType::isStatic(weightSpatial[dim]))
4717 inputSpatial[dim] + padValues[2 * dim] + padValues[2 * dim + 1];
4719 (weightSpatial[dim] - 1) * dilationValues[dim] + 1;
4720 const int64_t unstridedResult = inputSize - filterSize + 1;
4721 outputShape[dim + 1] = (unstridedResult - 1) / strideValues[dim] + 1;
4728LogicalResult Conv2DOp::inferReturnTypeComponents(
4729 MLIRContext *context, ::std::optional<Location> location,
4730 Conv2DOp::Adaptor adaptor,
4731 SmallVectorImpl<ShapedTypeComponents> &inferredReturnShapes) {
4735LogicalResult Conv2DOp::verify() {
4742LogicalResult Conv2DBlockScaledOp::inferReturnTypeComponents(
4743 MLIRContext *context, ::std::optional<Location> location,
4744 Conv2DBlockScaledOp::Adaptor adaptor,
4745 SmallVectorImpl<ShapedTypeComponents> &inferredReturnShapes) {
4749LogicalResult Conv2DBlockScaledOp::verify() {
4751 getWeightData().
getType(),
"input_data",
4754 getWeightScale().
getType(),
"input_scale",
4757 getOutput().
getType(),
"bias",
"output")))
4761 int64_t N = ShapedType::kDynamic;
4762 int64_t IH = ShapedType::kDynamic;
4763 int64_t IW = ShapedType::kDynamic;
4764 int64_t IC = ShapedType::kDynamic;
4765 int64_t multiplesOfIC = ShapedType::kDynamic;
4766 int64_t OC = ShapedType::kDynamic;
4767 int64_t KH = ShapedType::kDynamic;
4768 int64_t KW = ShapedType::kDynamic;
4770 const ShapeAdaptor inputDataShape(getInputData().
getType());
4771 if (inputDataShape.hasRank()) {
4772 N = inputDataShape.getDimSize(0);
4773 IH = inputDataShape.getDimSize(1);
4774 IW = inputDataShape.getDimSize(2);
4775 IC = inputDataShape.getDimSize(3);
4778 const ShapeAdaptor inputScaleShape(getInputScale().
getType());
4779 if (inputScaleShape.hasRank()) {
4781 "input_scale",
"batch size")) ||
4783 "input_scale",
"input height")) ||
4785 "input_scale",
"input width")))
4787 multiplesOfIC = inputScaleShape.getDimSize(3);
4790 const ShapeAdaptor weightDataShape(getWeightData().
getType());
4791 if (weightDataShape.hasRank()) {
4792 OC = weightDataShape.getDimSize(0);
4793 KH = weightDataShape.getDimSize(1);
4794 KW = weightDataShape.getDimSize(2);
4796 "weight_data",
"input channels")))
4800 const ShapeAdaptor weightScaleShape(getWeightScale().
getType());
4801 if (weightScaleShape.hasRank()) {
4803 "weight_scale",
"output channels")) ||
4805 "weight_scale",
"kernel height")) ||
4807 "weight_scale",
"kernel width")) ||
4809 weightScaleShape.getDimSize(3),
4810 "weight_scale",
"input channel blocks")))
4814 const uint32_t blockSize = BlockSizeAttr::getBlockSizeValue(
getBlockSize());
4815 if (blockSize != BlockSizeAttr::getBlockSizeValue(BlockSize::BLOCK_SIZE_32))
4816 return emitOpError(
"expect block size to be 32, got ") << blockSize;
4818 if (ShapedType::isStatic(IC) && IC % blockSize != 0)
4819 return emitOpError(
"expect IC to be a multiple of block size, got IC=")
4820 << IC <<
", block_size=" << blockSize;
4823 if (ShapedType::isStatic(IC) && ShapedType::isStatic(multiplesOfIC) &&
4824 multiplesOfIC != IC / blockSize)
4826 "expect scale operands dimension 2 to equal IC/block_size (")
4827 << IC <<
"/" << blockSize <<
")"
4828 <<
", got " << multiplesOfIC;
4831 SmallVector<int64_t> padValues;
4833 if (llvm::any_of(padValues, [](int64_t p) {
return p < 0; }))
4834 return emitOpError(
"expect all padding values to be >= 0, got ")
4838 SmallVector<int64_t> strideValues;
4840 if (llvm::any_of(strideValues, [](int64_t s) {
return s < 1; }))
4841 return emitOpError(
"expect all stride values to be >= 1, got ")
4845 SmallVector<int64_t> dilationValues;
4848 if (llvm::any_of(dilationValues, [](int64_t d) {
return d < 1; }))
4849 return emitOpError(
"expect all dilation values to be >= 1, got ")
4854 const ShapeAdaptor outputShape(getOutput().
getType());
4855 if (!padValues.empty() && !strideValues.empty() && !dilationValues.empty() &&
4856 outputShape.hasRank()) {
4858 padValues[0], padValues[1], strideValues[0],
4859 dilationValues[0],
"height",
"y",
"top",
4862 padValues[2], padValues[3], strideValues[1],
4863 dilationValues[1],
"width",
"x",
"left",
4869 const ShapeAdaptor biasShape(getBias().
getType());
4870 if (biasShape.hasRank() && outputShape.hasRank()) {
4871 const int64_t biasChannels = biasShape.getDimSize(0);
4872 const int64_t outputChannels =
4873 outputShape.getDimSize(outputShape.getRank() - 1);
4874 if (biasChannels == ShapedType::kDynamic ||
4875 outputChannels == ShapedType::kDynamic)
4879 if (biasChannels != outputChannels && biasChannels != 1)
4881 "bias channels expected to be equal to output channels (")
4882 << outputChannels <<
") or 1, got " << biasChannels;
4888LogicalResult Conv3DOp::inferReturnTypeComponents(
4889 MLIRContext *context, ::std::optional<Location> location,
4890 Conv3DOp::Adaptor adaptor,
4891 SmallVectorImpl<ShapedTypeComponents> &inferredReturnShapes) {
4895LogicalResult Conv3DOp::verify() {
4902LogicalResult AvgPool2dOp::inferReturnTypeComponents(
4903 MLIRContext *context, ::std::optional<Location> location,
4904 AvgPool2dOp::Adaptor adaptor,
4905 SmallVectorImpl<ShapedTypeComponents> &inferredReturnShapes) {
4906 ShapeAdaptor inputShape(adaptor.getInput().getType());
4907 const Properties &prop = adaptor.getProperties();
4909 inferredReturnShapes);
4912LogicalResult AvgPool2dAdaptiveOp::inferReturnTypeComponents(
4913 MLIRContext *context, ::std::optional<Location> location,
4914 AvgPool2dAdaptiveOp::Adaptor adaptor,
4915 SmallVectorImpl<ShapedTypeComponents> &inferredReturnShapes) {
4916 ShapeAdaptor inputShape(adaptor.getInput().getType());
4918 llvm::SmallVector<int64_t> kernelValues;
4919 llvm::SmallVector<int64_t> strideValues;
4920 llvm::SmallVector<int64_t> padValues;
4927 padValues, inferredReturnShapes);
4930 llvm::SmallVector<int64_t> outputShape(4, ShapedType::kDynamic);
4931 if (inputShape.hasRank()) {
4933 outputShape[0] = inputShape.getDimSize(0);
4934 outputShape[3] = inputShape.getDimSize(3);
4937 inferredReturnShapes.push_back(ShapedTypeComponents(outputShape));
4941LogicalResult MaxPool2dOp::inferReturnTypeComponents(
4942 MLIRContext *context, ::std::optional<Location> location,
4943 MaxPool2dOp::Adaptor adaptor,
4944 SmallVectorImpl<ShapedTypeComponents> &inferredReturnShapes) {
4945 ShapeAdaptor inputShape(adaptor.getInput().getType());
4946 const Properties &prop = adaptor.getProperties();
4948 inferredReturnShapes);
4951LogicalResult MaxPool2dAdaptiveOp::inferReturnTypeComponents(
4952 MLIRContext *context, ::std::optional<Location> location,
4953 MaxPool2dAdaptiveOp::Adaptor adaptor,
4954 SmallVectorImpl<ShapedTypeComponents> &inferredReturnShapes) {
4955 ShapeAdaptor inputShape(adaptor.getInput().getType());
4957 llvm::SmallVector<int64_t> kernelValues;
4958 llvm::SmallVector<int64_t> strideValues;
4959 llvm::SmallVector<int64_t> padValues;
4966 padValues, inferredReturnShapes);
4969 llvm::SmallVector<int64_t> outputShape(4, ShapedType::kDynamic);
4970 if (inputShape.hasRank()) {
4971 outputShape[0] = inputShape.getDimSize(0);
4972 outputShape[3] = inputShape.getDimSize(3);
4974 inferredReturnShapes.push_back(ShapedTypeComponents(outputShape));
4978LogicalResult MaxPool2dOp::verify() {
4989LogicalResult MaxPool2dAdaptiveOp::verify() {
4994 AdaptivePoolingConstShapeValues values;
4998 values.pad, getInput(), getOutput())))
5004LogicalResult DepthwiseConv2DOp::inferReturnTypeComponents(
5005 MLIRContext *context, ::std::optional<Location> location,
5006 DepthwiseConv2DOp::Adaptor adaptor,
5007 SmallVectorImpl<ShapedTypeComponents> &inferredReturnShapes) {
5008 llvm::SmallVector<int64_t> outputShape(4, ShapedType::kDynamic);
5010 int64_t inputWidth = ShapedType::kDynamic;
5011 int64_t inputHeight = ShapedType::kDynamic;
5012 int64_t inputChannels = ShapedType::kDynamic;
5014 int64_t weightWidth = ShapedType::kDynamic;
5015 int64_t weightHeight = ShapedType::kDynamic;
5016 int64_t depthChannels = ShapedType::kDynamic;
5019 ShapeAdaptor inputShape(adaptor.getInput().getType());
5020 if (inputShape.hasRank()) {
5021 outputShape[0] = inputShape.getDimSize(0);
5022 inputHeight = inputShape.getDimSize(1);
5023 inputWidth = inputShape.getDimSize(2);
5024 inputChannels = inputShape.getDimSize(3);
5028 ShapeAdaptor weightShape(adaptor.getWeight().getType());
5029 if (weightShape.hasRank()) {
5030 weightHeight = weightShape.getDimSize(0);
5031 weightWidth = weightShape.getDimSize(1);
5032 inputChannels = ShapedType::isDynamic(inputChannels)
5033 ? weightShape.getDimSize(2)
5035 depthChannels = weightShape.getDimSize(3);
5040 if (ShapedType::isStatic(inputChannels) &&
5041 ShapedType::isStatic(depthChannels)) {
5042 outputShape[3] = inputChannels * depthChannels;
5046 ShapeAdaptor biasShape(adaptor.getBias().getType());
5047 if (biasShape.hasRank() && ShapedType::isDynamic(outputShape[3])) {
5048 int64_t bc = biasShape.getDimSize(0);
5049 if (bc != ShapedType::kDynamic && bc != 1)
5050 outputShape[3] = bc;
5053 llvm::ArrayRef<int64_t> dilation = adaptor.getDilation();
5054 llvm::ArrayRef<int64_t> padding = adaptor.getPad();
5055 llvm::ArrayRef<int64_t> stride = adaptor.getStride();
5057 if (ShapedType::isStatic(inputHeight) && ShapedType::isStatic(weightHeight)) {
5058 int64_t inputSize = inputHeight + padding[0] + padding[1];
5059 int64_t filterSize = (weightHeight - 1) * dilation[0] + 1;
5060 int64_t unstridedResult = inputSize - filterSize + 1;
5061 outputShape[1] = (unstridedResult - 1) / stride[0] + 1;
5064 if (ShapedType::isStatic(inputWidth) && ShapedType::isStatic(weightWidth)) {
5065 int64_t inputSize = inputWidth + padding[2] + padding[3];
5066 int64_t filterSize = (weightWidth - 1) * dilation[1] + 1;
5067 int64_t unstridedResult = inputSize - filterSize + 1;
5068 outputShape[2] = (unstridedResult - 1) / stride[1] + 1;
5071 inferredReturnShapes.push_back(ShapedTypeComponents(outputShape));
5075LogicalResult DepthwiseConv2DOp::verify() {
5082LogicalResult TransposeConv2DOp::inferReturnTypeComponents(
5083 MLIRContext *context, ::std::optional<Location> location,
5084 TransposeConv2DOp::Adaptor adaptor,
5085 SmallVectorImpl<ShapedTypeComponents> &inferredReturnShapes) {
5086 llvm::SmallVector<int64_t> outputShape(4, ShapedType::kDynamic);
5088 int64_t inputWidth = ShapedType::kDynamic;
5089 int64_t inputHeight = ShapedType::kDynamic;
5090 int64_t weightWidth = ShapedType::kDynamic;
5091 int64_t weightHeight = ShapedType::kDynamic;
5094 ShapeAdaptor inputShape(adaptor.getInput().getType());
5095 if (inputShape.hasRank()) {
5096 outputShape[0] = ShapedType::isDynamic(outputShape[0])
5097 ? inputShape.getDimSize(0)
5099 inputHeight = inputShape.getDimSize(1);
5100 inputWidth = inputShape.getDimSize(2);
5104 ShapeAdaptor weightShape(adaptor.getWeight().getType());
5105 if (weightShape.hasRank()) {
5106 outputShape[3] = ShapedType::isDynamic(outputShape[3])
5107 ? weightShape.getDimSize(0)
5109 weightHeight = weightShape.getDimSize(1);
5110 weightWidth = weightShape.getDimSize(2);
5114 ShapeAdaptor biasShape(adaptor.getBias().getType());
5115 if (biasShape.hasRank() && ShapedType::isDynamic(outputShape[3])) {
5116 int64_t bc = biasShape.getDimSize(0);
5117 if (bc != ShapedType::kDynamic && bc != 1)
5118 outputShape[3] = bc;
5121 llvm::ArrayRef<int64_t> padding = adaptor.getOutPad();
5122 llvm::ArrayRef<int64_t> stride = adaptor.getStride();
5124 if (ShapedType::isStatic(inputHeight) && ShapedType::isStatic(weightHeight)) {
5125 int64_t calculateSize =
5126 (inputHeight - 1) * stride[0] + padding[0] + padding[1] + weightHeight;
5128 ShapedType::isDynamic(outputShape[1]) ? calculateSize : outputShape[1];
5131 if (ShapedType::isStatic(inputWidth) && ShapedType::isStatic(weightWidth)) {
5132 int64_t calculateSize =
5133 (inputWidth - 1) * stride[1] + padding[2] + padding[3] + weightWidth;
5135 ShapedType::isDynamic(outputShape[2]) ? calculateSize : outputShape[2];
5138 inferredReturnShapes.push_back(ShapedTypeComponents(outputShape));
5142LogicalResult TransposeConv2DOp::verify() {
5146 const llvm::ArrayRef<int64_t> strides = getStride();
5147 const int64_t strideY = strides[0];
5148 const int64_t strideX = strides[1];
5150 if (strideY < 1 || strideX < 1)
5151 return emitOpError(
"expect all stride values to be >= 1, got [")
5154 const auto checkPadAgainstKernelDim =
5155 [
this](int64_t padValue, int64_t kernelDimSize, llvm::StringRef padName,
5156 llvm::StringRef kernelDimName) -> LogicalResult {
5157 if (padValue <= -kernelDimSize)
5159 << padName <<
" > -" << kernelDimName <<
", but got: " << padName
5160 <<
"=" << padValue <<
" and " << kernelDimName <<
"="
5165 const llvm::ArrayRef<int64_t> padding = getOutPad();
5166 const int64_t outPadTop = padding[0];
5167 const int64_t outPadBottom = padding[1];
5168 const int64_t outPadLeft = padding[2];
5169 const int64_t outPadRight = padding[3];
5171 const auto weightType =
5172 llvm::dyn_cast<RankedTensorType>(getWeight().
getType());
5175 const int64_t kernelHeight = weightType.getDimSize(1);
5176 if (ShapedType::isStatic(kernelHeight)) {
5177 if (
failed(checkPadAgainstKernelDim(outPadTop, kernelHeight,
5178 "out_pad_top",
"KH")))
5181 if (
failed(checkPadAgainstKernelDim(outPadBottom, kernelHeight,
5182 "out_pad_bottom",
"KH")))
5186 const int64_t kernelWidth = weightType.getDimSize(2);
5187 if (ShapedType::isStatic(kernelWidth)) {
5188 if (
failed(checkPadAgainstKernelDim(outPadLeft, kernelWidth,
5189 "out_pad_left",
"KW")))
5192 if (
failed(checkPadAgainstKernelDim(outPadRight, kernelWidth,
5193 "out_pad_right",
"KW")))
5199 const auto outputType =
5200 llvm::dyn_cast<RankedTensorType>(getOutput().
getType());
5204 const auto inputType = llvm::dyn_cast<RankedTensorType>(getInput().
getType());
5205 if (inputType && weightType) {
5206 const int64_t inputHeight = inputType.getDimSize(1);
5207 const int64_t kernelHeight = weightType.getDimSize(1);
5208 const int64_t outputHeight = outputType.getDimSize(1);
5210 if (ShapedType::isStatic(inputHeight) &&
5211 ShapedType::isStatic(outputHeight)) {
5213 (inputHeight - 1) * strideY + outPadTop + outPadBottom + kernelHeight)
5215 "dimension mismatch: expected OH == (IH - 1) * stride_y "
5216 "+ out_pad_top + out_pad_bottom + KH, but got ")
5217 << outputHeight <<
" != (" << inputHeight <<
" - 1) * "
5218 << strideY <<
" + " << outPadTop <<
" + " << outPadBottom
5219 <<
" + " << kernelHeight;
5222 const int64_t inputWidth = inputType.getDimSize(2);
5223 const int64_t kernelWidth = weightType.getDimSize(2);
5224 const int64_t outputWidth = outputType.getDimSize(2);
5226 if (ShapedType::isStatic(inputWidth) && ShapedType::isStatic(outputWidth)) {
5228 (inputWidth - 1) * strideX + outPadLeft + outPadRight + kernelWidth)
5230 "dimension mismatch: expected OW == (IW - 1) * stride_x "
5231 "+ out_pad_left + out_pad_right + KW, but got ")
5232 << outputWidth <<
" != (" << inputWidth <<
" - 1) * " << strideX
5233 <<
" + " << outPadLeft <<
" + " << outPadRight <<
" + "
5238 const auto biasType = llvm::dyn_cast<RankedTensorType>(getBias().
getType());
5243 const int64_t biasChannels = biasType.getDimSize(0);
5246 if (biasChannels == ShapedType::kDynamic)
5249 const int64_t outputChannels = outputType.getDimSize(3);
5250 if (!ShapedType::isDynamic(outputChannels) &&
5251 biasChannels != outputChannels && biasChannels != 1)
5253 "bias channels expected to be equal to output channels (")
5254 << outputChannels <<
") or 1, got " << biasChannels;
5259LogicalResult RescaleOp::verify() {
5260 const auto inputType = llvm::cast<ShapedType>(getInput().
getType());
5261 auto inputElementType =
5263 if (!mlir::isa<IntegerType>(inputElementType)) {
5264 emitOpError(
"expect input to have integer element type, got ")
5265 << inputElementType;
5269 const auto outputType = llvm::cast<ShapedType>(getOutput().
getType());
5270 auto outputElementType =
5272 if (!mlir::isa<IntegerType>(outputElementType)) {
5273 emitOpError(
"expect output to have integer element type, got ")
5274 << outputElementType;
5286 FailureOr<int64_t> maybeIZp = getInputZeroPoint();
5287 if (succeeded(maybeIZp) && verifyInputZeroPoint(*maybeIZp).failed())
5290 FailureOr<int64_t> maybeOZp = getOutputZeroPoint();
5291 if (succeeded(maybeOZp) && verifyOutputZeroPoint(*maybeOZp).failed())
5294 const auto multiplierType = llvm::cast<ShapedType>(getMultiplier().
getType());
5296 if (getScale32() && !multiplierType.getElementType().isInteger(32)) {
5297 emitOpError(
"expect i32 element type for multiplier for scale32=true, got ")
5298 << multiplierType.getElementType();
5303 if (!getScale32() && !multiplierType.getElementType().isInteger(16)) {
5305 "expect i16 element type for multiplier for scale32=false, got ")
5306 << multiplierType.getElementType();
5310 if (!inputType.hasRank())
5316 int64_t numChannels = 1;
5317 if (getPerChannel()) {
5318 if (inputType.getRank() < 1) {
5319 emitOpError(
"requires input to be at least rank 1 when per_channel is "
5320 "true, but got rank ")
5321 << inputType.getRank();
5324 numChannels = inputType.getDimSize(inputType.getRank() - 1);
5327 if (outputType.hasRank()) {
5329 getOperation(), outputType, inputType.getShape())))
5333 if (multiplierType.hasRank()) {
5334 ArrayRef<int64_t> multiplierShape = multiplierType.getShape();
5336 if (multiplierShape[0] != ShapedType::kDynamic &&
5337 multiplierShape[0] != numChannels) {
5339 << numChannels <<
" } for multiplier input, got { "
5340 << multiplierShape[0] <<
" }";
5345 const auto shiftType = llvm::cast<ShapedType>(getShift().
getType());
5346 if (shiftType.hasRank()) {
5347 ArrayRef<int64_t> shiftShape = shiftType.getShape();
5349 if (shiftShape[0] != ShapedType::kDynamic && shiftShape[0] != numChannels) {
5351 << numChannels <<
" } for shift input, got { " << shiftShape[0]
5360LogicalResult RescaleOp::inferReturnTypeComponents(
5361 MLIRContext *context, ::std::optional<Location> location,
5362 RescaleOp::Adaptor adaptor,
5363 SmallVectorImpl<ShapedTypeComponents> &inferredReturnShapes) {
5364 ShapeAdaptor inputShape(adaptor.getInput().getType());
5365 inferredReturnShapes.push_back(ShapedTypeComponents(inputShape));
5369LogicalResult CastOp::verify() {
5370 const ShapedType inputType = llvm::cast<ShapedType>(getInput().
getType());
5371 const ShapedType outputType = llvm::cast<ShapedType>(
getType());
5372 const Type inputElementType = inputType.getElementType();
5373 const Type outputElementType = outputType.getElementType();
5375 const bool inputIsBlockScaled = llvm::isa<BlockScaledType>(inputElementType);
5376 const bool outputIsBlockScaled =
5377 llvm::isa<BlockScaledType>(outputElementType);
5379 const bool isUnsigned = this->getInputUnsigned();
5385 <<
"attribute input_unsigned requires integer type inputs. Got: "
5388 if (!inputIsBlockScaled && !outputIsBlockScaled)
5391 if (inputIsBlockScaled && outputIsBlockScaled)
5393 <<
"requires exactly one of input or output to have block scaled "
5396 const Type scalarElementType =
5397 inputIsBlockScaled ? outputElementType : inputElementType;
5398 if (!llvm::isa<FloatType>(scalarElementType))
5400 <<
"requires non-block-scaled element type to be floating-point "
5401 "when casting to or from block scaled element type, got "
5402 << scalarElementType;
5407LogicalResult CastFromBlockScaledOp::inferReturnTypeComponents(
5408 MLIRContext *context, ::std::optional<Location> location,
5409 CastFromBlockScaledOp::Adaptor adaptor,
5410 SmallVectorImpl<ShapedTypeComponents> &inferredReturnShapes) {
5411 const ShapeAdaptor inputShape(adaptor.getInputData().getType());
5412 inferredReturnShapes.push_back(ShapedTypeComponents(inputShape));
5416LogicalResult CastFromBlockScaledOp::verify() {
5417 const Type inputDataType = getInputData().getType();
5418 const Type outputDataType = getResult().getType();
5420 return emitOpError() <<
"require compatible shapes for input_data ("
5421 << inputDataType <<
") and " <<
"output_data ("
5422 << outputDataType <<
")";
5424 const ShapeAdaptor inputDataShape = ShapeAdaptor(inputDataType);
5426 if (inputDataShape.
hasRank()) {
5427 const unsigned int blockSize =
5429 if (blockSize != BlockSizeAttr::getBlockSizeValue(BlockSize::BLOCK_SIZE_32))
5430 return emitOpError(
"expect block size to be 32, got ") << blockSize;
5431 const int64_t inputDataLastDim =
5433 if (inputDataLastDim % blockSize != 0)
5434 return emitOpError() <<
"expect last dimension of input_data ("
5436 <<
") to be divisible by block_size (" << blockSize
5439 const Type inputScaleType = getInputScale().getType();
5440 const ShapeAdaptor inputScaleShape = ShapeAdaptor(inputScaleType);
5442 if (inputScaleShape.
hasRank()) {
5443 SmallVector<int64_t> inputDataDims, inputScaleDims;
5444 inputDataShape.
getDims(inputDataDims);
5445 inputScaleShape.
getDims(inputScaleDims);
5447 if (inputDataDims.size() != inputScaleDims.size() ||
5449 ArrayRef<int64_t>(inputDataDims).drop_back(1),
5450 ArrayRef<int64_t>(inputScaleDims).drop_back(1))))
5452 <<
"require compatible shapes for input_data (" << inputDataType
5453 <<
") and " <<
"input_scale (" << inputScaleType
5454 <<
") except for the last dimension";
5456 const SmallVector<int64_t, 2> dimsToCheck{inputDataLastDim / blockSize,
5457 inputScaleDims.back()};
5458 if (ShapedType::isStatic(inputDataLastDim) &&
5461 <<
"expect last dimension of input_scale ("
5462 << inputScaleDims.back()
5463 <<
") to be equal to last dimension of input_data / block_size ("
5464 << inputDataDims.back() / blockSize <<
")";
5471LogicalResult CastToBlockScaledOp::inferReturnTypeComponents(
5472 MLIRContext *context, ::std::optional<Location> location,
5473 CastToBlockScaledOp::Adaptor adaptor,
5474 SmallVectorImpl<ShapedTypeComponents> &inferredReturnShapes) {
5475 const ShapeAdaptor inputShape(adaptor.getInputData().getType());
5476 inferredReturnShapes.push_back(ShapedTypeComponents(inputShape));
5477 if (!inputShape.hasRank())
5481 SmallVector<int64_t> outputScaleShape;
5482 inputShape.getDims(outputScaleShape);
5483 const int64_t lastDimLoc = inputShape.getRank() - 1;
5484 const int64_t lastDimSize = inputShape.getDimSize(lastDimLoc);
5485 if (ShapedType::isStatic(lastDimSize)) {
5486 const unsigned int blockSize =
5487 BlockSizeAttr::getBlockSizeValue(adaptor.getBlockSize());
5488 outputScaleShape[lastDimLoc] = lastDimSize / blockSize;
5490 inferredReturnShapes.push_back(ShapedTypeComponents(outputScaleShape));
5494LogicalResult CastToBlockScaledOp::verify() {
5495 const Type inputDataType = getInputData().getType();
5496 const Type outputDataType = getResult(0).getType();
5498 return emitOpError() <<
"require compatible shapes for input_data ("
5499 << inputDataType <<
") and " <<
"output_data ("
5500 << outputDataType <<
")";
5502 const unsigned int blockSize =
5504 if (blockSize != BlockSizeAttr::getBlockSizeValue(BlockSize::BLOCK_SIZE_32))
5505 return emitOpError(
"expect block size to be 32, got ") << blockSize;
5506 const ShapeAdaptor inputDataShape = ShapeAdaptor(inputDataType);
5507 if (inputDataShape.
hasRank()) {
5508 const int64_t inputDataLastDim =
5510 if (ShapedType::isStatic(inputDataLastDim) &&
5511 inputDataLastDim % blockSize != 0)
5512 return emitOpError() <<
"expect last dimension of input_data ("
5514 <<
") to be divisible by block_size (" << blockSize
5518 const ShapeAdaptor outputDataShape = ShapeAdaptor(outputDataType);
5519 const Type outputScaleType = getResult(1).getType();
5520 const ShapeAdaptor outputScaleShape = ShapeAdaptor(outputScaleType);
5522 SmallVector<int64_t> outputDataDims, outputScaleDims;
5523 outputDataShape.
getDims(outputDataDims);
5524 outputScaleShape.
getDims(outputScaleDims);
5526 if (outputDataDims.size() != outputScaleDims.size() ||
5528 ArrayRef<int64_t>(outputDataDims).drop_back(1),
5529 ArrayRef<int64_t>(outputScaleDims).drop_back(1))))
5530 return emitOpError() <<
"require compatible shapes for output_data ("
5531 << outputDataType <<
") and " <<
"output_scale ("
5533 <<
") except for the last dimension";
5535 const int64_t outputDataLastDim = outputDataDims.back();
5536 const SmallVector<int64_t, 2> dimsToCheck{outputDataLastDim / blockSize,
5537 outputScaleDims.back()};
5538 if (ShapedType::isStatic(outputDataLastDim) &&
5541 <<
"expect last dimension of output_scale ("
5542 << outputScaleDims.back()
5543 <<
") to be equal to last dimension of output_data / block_size ("
5544 << outputDataDims.back() / blockSize <<
")";
5550LogicalResult IfOp::inferReturnTypeComponents(
5551 MLIRContext *context, ::std::optional<Location> location,
5552 IfOp::Adaptor adaptor,
5553 SmallVectorImpl<ShapedTypeComponents> &inferredReturnShapes) {
5554 llvm::SmallVector<tosa::YieldOp> yieldOps;
5555 for (Region *region : adaptor.getRegions()) {
5556 for (
auto &block : *region)
5557 if (
auto returnOp = dyn_cast<tosa::YieldOp>(block.getTerminator()))
5558 yieldOps.push_back(returnOp);
5561 if (yieldOps.empty())
5565 llvm::SmallVector<ValueKnowledge> resultKnowledge;
5566 resultKnowledge.reserve(yieldOps.front().getNumOperands());
5567 for (
auto operand : yieldOps.front().getOperands()) {
5568 resultKnowledge.push_back(
5572 for (
auto yieldOp : yieldOps) {
5573 if (resultKnowledge.size() != yieldOp.getNumOperands())
5576 for (
const auto &it : llvm::enumerate(yieldOp.getOperands())) {
5577 int32_t index = it.index();
5579 resultKnowledge[index],
5583 resultKnowledge[index] = meet;
5587 for (
const ValueKnowledge &
result : resultKnowledge) {
5588 inferredReturnShapes.push_back(
result.getShapedTypeComponents());
5594LogicalResult WhileOp::inferReturnTypeComponents(
5595 MLIRContext *context, ::std::optional<Location> location,
5596 WhileOp::Adaptor adaptor,
5597 SmallVectorImpl<ShapedTypeComponents> &inferredReturnShapes) {
5598 llvm::SmallVector<tosa::YieldOp> yieldOps;
5599 for (
auto &block : adaptor.getBodyGraph())
5600 if (
auto returnOp = dyn_cast<tosa::YieldOp>(block.getTerminator()))
5601 yieldOps.push_back(returnOp);
5605 if (yieldOps.empty())
5609 llvm::SmallVector<ValueKnowledge> resultKnowledge;
5610 resultKnowledge.reserve(yieldOps.front().getNumOperands());
5611 for (
auto operand : yieldOps.front().getOperands()) {
5612 resultKnowledge.push_back(
5616 for (
auto yieldOp : yieldOps) {
5617 if (resultKnowledge.size() != yieldOp.getNumOperands())
5620 for (
const auto &it : llvm::enumerate(yieldOp.getOperands())) {
5621 int32_t index = it.index();
5623 resultKnowledge[index],
5625 resultKnowledge[index] = meet;
5630 for (
const ValueKnowledge &
result : resultKnowledge) {
5631 inferredReturnShapes.push_back(
result.getShapedTypeComponents());
5637std::optional<SmallVector<int64_t, 4>> ApplyScaleOp::getShapeForUnroll() {
5638 if (
auto vt = llvm::dyn_cast<VectorType>(
getType()))
5639 return llvm::to_vector<4>(vt.getShape());
5640 return std::nullopt;
5646 StringRef prefix =
"") {
5647 assert(blocksArgs.size() == initializers.size() &&
5648 "expected same length of arguments and initializers");
5649 if (initializers.empty())
5652 parser << prefix <<
'(';
5653 llvm::interleaveComma(
5654 llvm::zip(blocksArgs, initializers), parser,
5655 [&](
auto it) { parser << std::get<0>(it) <<
" = " << std::get<1>(it); });
5660ParseResult IfOp::parse(OpAsmParser &parser, OperationState &
result) {
5662 result.regions.reserve(2);
5663 Region *thenRegion =
result.addRegion();
5664 Region *elseRegion =
result.addRegion();
5666 OpAsmParser::UnresolvedOperand cond;
5671 SmallVector<OpAsmParser::Argument, 4> regionArgs;
5672 SmallVector<OpAsmParser::UnresolvedOperand, 4> operands;
5675 OptionalParseResult listResult =
5683 "expected type for condition operand");
5689 "expected type for condition operand");
5697 FunctionType functionType;
5701 <<
"expected list of types for block arguments "
5702 <<
"followed by arrow type and list of return types";
5704 result.addTypes(functionType.getResults());
5706 if (functionType.getNumInputs() != operands.size()) {
5708 <<
"expected as many input types as operands " <<
"(expected "
5709 << operands.size() <<
" got " << functionType.getNumInputs()
5740void IfOp::print(OpAsmPrinter &p) {
5741 p <<
" " << getCondition();
5744 getInputList(),
" ");
5746 p << getCondition().getType();
5748 if (!getInputList().empty()) {
5750 llvm::interleaveComma(getInputList().getTypes(), p);
5759 auto &elseRegion = getElseGraph();
5760 if (!elseRegion.
empty()) {
5768LogicalResult IfOp::verify() {
5770 "'then_graph' arguments", getInputList(),
5776 "'else_graph' arguments", getInputList(),
5782 if (getThenGraph().front().mightHaveTerminator()) {
5784 dyn_cast<tosa::YieldOp>(getThenGraph().front().getTerminator());
5786 *
this, thenYield.getInputs(),
"'then_graph' results",
5787 getOutputList(),
"'output_list'")
5793 if (getElseGraph().front().mightHaveTerminator()) {
5795 dyn_cast<tosa::YieldOp>(getElseGraph().front().getTerminator());
5797 *
this, elseYield.getInputs(),
"'else_graph' results",
5798 getOutputList(),
"'output_list'")
5803 auto condType = getCondition().getType();
5805 return emitOpError() <<
"'condition' must be a size 1 tensor, got "
5811LogicalResult WhileOp::verify() {
5813 getOutputList(),
"'output_list'")
5818 "'cond_graph' arguments", getInputList(),
5824 "'body_graph' arguments", getInputList(),
5829 if (getBodyGraph().front().mightHaveTerminator()) {
5831 dyn_cast<tosa::YieldOp>(getBodyGraph().front().getTerminator());
5833 "'body_graph' results",
5834 getInputList(),
"'input_list'")
5841 if (!getCondGraph().front().mightHaveTerminator())
5845 dyn_cast<tosa::YieldOp>(getCondGraph().front().getTerminator());
5849 if (condYield.getInputs().size() != 1)
5850 return emitOpError() <<
"require 'cond_graph' only have one result";
5852 auto condOutType = condYield.getInputs()[0].getType();
5854 return emitOpError() <<
"'cond_graph' result must be a size 1 tensor, got "
5858 return emitOpError() <<
"'cond_graph' result must be a boolean tensor, got "
5864LogicalResult ReverseOp::verify() {
5865 TensorType inputType = getInput1().getType();
5866 int32_t reverseAxis = getAxis();
5868 if (reverseAxis < 0)
5869 return emitOpError(
"expected non-negative reverse axis");
5871 int64_t inputRank = inputType.getRank();
5874 if (reverseAxis >= inputRank && (reverseAxis != 0 || inputRank != 0))
5876 << inputRank <<
") to be larger than reverse axis (" << reverseAxis
5883LogicalResult tosa::SelectOp::verify() {
5894 auto predicateType = llvm::dyn_cast<ShapedType>(getPred().
getType());
5895 if (!predicateType) {
5896 return emitOpError(
"expect shaped tensor for input1, got ")
5897 << getInput1().getType();
5899 auto predicateElementType = predicateType.getElementType();
5900 if (!predicateElementType.isInteger(1)) {
5901 return emitOpError(
"expect element type of bool for input1, got ")
5902 << predicateElementType;
5908LogicalResult tosa::VariableReadOp::verify() {
5916LogicalResult tosa::VariableWriteOp::verify() {
5925ParseResult WhileOp::parse(OpAsmParser &parser, OperationState &
result) {
5926 SmallVector<OpAsmParser::Argument, 4> regionArgs;
5927 SmallVector<OpAsmParser::UnresolvedOperand, 4> operands;
5928 Region *cond =
result.addRegion();
5929 Region *body =
result.addRegion();
5931 OptionalParseResult listResult =
5936 FunctionType functionType;
5941 result.addTypes(functionType.getResults());
5943 if (functionType.getNumInputs() != operands.size()) {
5945 <<
"expected as many input types as operands " <<
"(expected "
5946 << operands.size() <<
" got " << functionType.getNumInputs() <<
")";
5956 for (
size_t i = 0, e = regionArgs.size(); i != e; ++i)
5957 regionArgs[i].type = functionType.getInput(i);
5959 return failure(parser.
parseRegion(*cond, regionArgs) ||
5964void WhileOp::print(OpAsmPrinter &parser) {
5966 getInputList(),
" ");
5969 getResults().getTypes());
5983 auto zpType = mlir::RankedTensorType::get({1}, srcElemType);
5984 if (llvm::isa<FloatType>(srcElemType)) {
5986 zpType, builder.
getFloatAttr(srcElemType,
static_cast<double>(zp)));
5987 return tosa::ConstOp::create(builder, loc, zpType, zpAttr);
5989 if (llvm::isa<IntegerType>(srcElemType)) {
5992 return tosa::ConstOp::create(builder, loc, zpType, zpAttr);
5994 llvm::errs() <<
"zero point is not allowed for unsupported data types\n";
5995 return std::nullopt;
6003 return mlir::isa<tosa::shapeType>(t);
6010 return emitError() <<
"invalid rank (must be >= 0): " << rank;
6016 if (mlir::isa<::mlir::tosa::shapeType>(v.getType())) {
6017 Operation *definingOp = v.getDefiningOp();
6019 return op->
emitOpError(
"shape operand is not compile time resolvable");
6032 auto getRank = [](
const Type type) {
6033 return mlir::cast<mlir::tosa::shapeType>(type).getRank();
6039 for (
auto type : operandTypes) {
6040 if (getRank(type) != rank) {
6041 return op->
emitOpError(
"operands don't have matching ranks");
6044 for (
auto type : resultTypes) {
6045 if (getRank(type) != rank) {
6046 return op->
emitOpError(
"result shape has different rank than operands");
6056LogicalResult tosa::ConstShapeOp::verify() {
6058 auto valuesRank = getValues().getType().getRank();
6059 if (valuesRank != 1)
6060 return emitOpError(
"expect elements in attribute values with rank 1");
6062 auto count = getValues().getNumElements();
6063 auto rank = (cast<tosa::shapeType>(getResult().
getType())).getRank();
6064 if (count != rank && (count != 1 || rank != 0)) {
6065 return emitOpError(
"expect number of elements in attribute values (")
6066 << count <<
") to be equal to the rank (" << rank
6067 <<
") for the result shape type";
6072LogicalResult tosa::DimOp::verify() {
6073 const tosa::shapeType outShapeType =
6074 cast<tosa::shapeType>(getResult().
getType());
6075 if (outShapeType.getRank() != 1)
6076 return emitOpError(
"expect output shape type to contain one element, got ")
6081 const int64_t inputRank = inputType.getRank();
6082 const int64_t axis = getAxisAttr().getInt();
6083 if (axis < 0 || axis >= inputRank)
6084 return emitOpError(
"expect axis to be in the range [0, ")
6085 << inputRank <<
"), got " << axis;
6090LogicalResult tosa::ConcatShapeOp::verify() {
6091 const tosa::shapeType outShapeType =
6092 cast<tosa::shapeType>(getResult().
getType());
6093 const int64_t outputRank = outShapeType.getRank();
6096 if (inputList.size() == 0)
6097 return emitOpError(
"requires at least one input shape");
6099 if (llvm::any_of(inputList, [](Value v) {
6100 return cast<tosa::shapeType>(v.
getType()).getRank() == 0;
6102 return emitOpError(
"requires all inputs shapes have a rank greater than 0");
6104 const int64_t inputsRank =
6105 llvm::accumulate(inputList, 0, [](int64_t acc,
const Value &input) {
6106 const tosa::shapeType inShapeType =
6107 cast<tosa::shapeType>(input.
getType());
6108 return acc + inShapeType.getRank();
6110 if (outputRank != inputsRank)
6111 return emitOpError(
"requires output shape rank to be equal to the sum of "
6112 "the input shape ranks (")
6113 << inputsRank <<
"), got " << outputRank;
6118LogicalResult tosa::SliceShapeOp::verify() {
6119 std::optional<int32_t> start;
6120 DenseIntElementsAttr startAttr;
6122 start = startAttr.getValues<int32_t>()[0];
6123 if (start && start.value() < 0)
6124 return emitOpError(
"expected non-negative start index, got ")
6127 std::optional<int32_t> size;
6128 DenseIntElementsAttr sizeAttr;
6130 size = sizeAttr.getValues<int32_t>()[0];
6131 if (size && size.value() <= 0)
6132 return emitOpError(
"expected positive size, got ") << size.value();
6137 const tosa::shapeType outShapeType =
6138 cast<tosa::shapeType>(getResult().
getType());
6139 const int64_t outputRank = outShapeType.getRank();
6140 if (outputRank != size)
6142 "expected output type size to be equal to size attribute, got ")
6143 << outputRank <<
" vs " << size.value();
6148 const tosa::shapeType inShapeType =
6149 cast<tosa::shapeType>(getInput().
getType());
6150 const int64_t inputRank = inShapeType.getRank();
6151 const int64_t sliceSize = start.value() + size.value();
6152 if (sliceSize > inputRank)
6153 return emitOpError(
"expected start + size to be less than or equal to "
6154 "input shape rank (")
6155 << inputRank <<
"), got " << sliceSize;
6164#define GET_ATTRDEF_CLASSES
6165#include "mlir/Dialect/Tosa/IR/TosaAttributes.cpp.inc"
6170#define GET_TYPEDEF_CLASSES
6171#include "mlir/Dialect/Tosa/IR/TosaOpsTypesBase.cpp.inc"
6177#define GET_OP_CLASSES
6178#include "mlir/Dialect/Tosa/IR/TosaOps.cpp.inc"
p<< " : "<< getMemRefType()<< ", "<< getType();}static LogicalResult verifyVectorMemoryOp(Operation *op, MemRefType memrefType, VectorType vectorType) { if(memrefType.getElementType() !=vectorType.getElementType()) return op-> emitOpError("requires memref and vector types of the same elemental type")
Given a list of lists of parsed operands, populates uniqueOperands with unique operands.
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 std::string diag(const llvm::Value &value)
static Type getValueType(Attribute attr)
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")
LogicalResult inferConvReturnTypeComponents(AdaptorT adaptor, SmallVectorImpl< ShapedTypeComponents > &inferredReturnShapes)
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 LogicalResult errorIfShapeNotSizeOne(Operation *op, Type type)
static LogicalResult verifyMatMulZeroPointType(T op, Value input, Value zp, StringRef inputName, StringRef zpName)
static ParseResult parseScaleValues(AsmParser &parser, SmallVector< Attribute > &scaleValues, Type scaleType)
#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.
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 verifyConvOpModes(T op)
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 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 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 void buildMatMulTOpWithQuantInfo(OpBuilder &builder, OperationState &result, Type outputType, Value a, Value b)
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 parseOptionalRBrace()=0
Parse a } token if present.
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 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 parseRBrace()=0
Parse a } token.
virtual ParseResult parseEqual()=0
Parse a = token.
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 parseOptionalComma()=0
Parse a , token if present.
virtual ParseResult parseColon()=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.
virtual ParseResult parseOptionalLBrace()=0
Parse a { token if present.
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
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.
NamedAttrList is array of NamedAttributes that tracks whether it is sorted and does some basic work t...
void push_back(NamedAttribute newAttribute)
Add an attribute with the specified name.
NamedAttribute represents a combination of a name and an Attribute value.
StringAttr getName() const
Return the name of the attribute.
Attribute getValue() const
Return the value of the attribute.
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...
void printOperands(const ContainerType &container)
Print a comma separated list of operands.
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.
ArrayRef< NamedAttribute > getAttrs()
Return all of the attributes on this operation.
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.
Operation * getDefiningOp() const
If this value is the result of an operation, return the operation that defines it.
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)