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/TosaAvailability.cpp.inc"
48#include "mlir/Dialect/Tosa/IR/TosaEnums.cpp.inc"
49#include "mlir/Dialect/Tosa/IR/TosaInterfaces.cpp.inc"
50#include "mlir/Dialect/Tosa/IR/TosaOpAvailabilityImpl.inc"
53#include "mlir/Dialect/Tosa/IR/TosaDialectBytecode.cpp.inc"
58struct TosaInlinerInterface :
public DialectInlinerInterface {
59 using DialectInlinerInterface::DialectInlinerInterface;
67 IRMapping &map)
const final {
73 IRMapping &map)
const final {
74 return (isa<tosa::IfOp>(dest->getParentOp()) ||
75 isa<tosa::WhileOp>(dest->getParentOp()));
80struct TosaDialectBytecodeInterface :
public BytecodeDialectInterface {
81 TosaDialectBytecodeInterface(Dialect *dialect)
82 : BytecodeDialectInterface(dialect) {}
87 Attribute readAttribute(DialectBytecodeReader &reader)
const override {
91 LogicalResult writeAttribute(Attribute attr,
92 DialectBytecodeWriter &writer)
const override {
93 return ::writeAttribute(attr, writer);
99 Type readType(DialectBytecodeReader &reader)
const override {
103 LogicalResult writeType(Type type,
104 DialectBytecodeWriter &writer)
const override {
105 return ::writeType(type, writer);
108 void writeVersion(DialectBytecodeWriter &writer)
const final {
112 std::unique_ptr<DialectVersion>
113 readVersion(DialectBytecodeReader &reader)
const final {
115 reader.
emitError(
"Dialect does not support versioning");
119 LogicalResult upgradeFromVersion(Operation *topLevelOp,
120 const DialectVersion &version)
const final {
133 return {&getBodyGraph()};
142 return dim == -1 ? ShapedType::kDynamic : dim;
148 Type elementType = variableOp.getType();
151 return RankedTensorType::get(
shape, elementType);
158void TosaDialect::initialize() {
160#define GET_TYPEDEF_LIST
161#include "mlir/Dialect/Tosa/IR/TosaOpsTypesBase.cpp.inc"
165#include "mlir/Dialect/Tosa/IR/TosaOps.cpp.inc"
168#define GET_ATTRDEF_LIST
169#include "mlir/Dialect/Tosa/IR/TosaAttributes.cpp.inc"
171 addInterfaces<TosaDialectBytecodeInterface, TosaInlinerInterface>();
172 declarePromisedInterfaces<
173 shard::ShardingInterface, ClampOp, SigmoidOp, TanhOp, AddOp,
174 ArithmeticRightShiftOp, BitwiseAndOp, BitwiseOrOp, BitwiseXorOp, IntDivOp,
175 LogicalAndOp, LogicalLeftShiftOp, LogicalRightShiftOp, LogicalOrOp,
176 LogicalXorOp, MaximumOp, MinimumOp, MulOp, PowOp, SubOp, AbsOp,
177 BitwiseNotOp, CeilOp, ClzOp, ExpOp, FloorOp, LogOp, LogicalNotOp,
178 NegateOp, ReciprocalOp, RsqrtOp, SelectOp, EqualOp, GreaterOp,
179 GreaterEqualOp, MatMulOp>();
186 if (llvm::isa<shapeType>(type) && llvm::isa<DenseIntElementsAttr>(value)) {
187 return tosa::ConstShapeOp::create(builder, loc, type,
188 llvm::cast<DenseIntElementsAttr>(value));
190 if (llvm::isa<ElementsAttr>(value))
191 return tosa::ConstOp::create(builder, loc, type,
192 llvm::cast<ElementsAttr>(value));
202ParseResult getShapeAndElementType(
OpAsmParser &parser,
Type parsedType,
204 TypeAttr &typeAttr) {
205 if (
auto shapedType = dyn_cast<ShapedType>(parsedType)) {
206 if (!shapedType.hasRank())
208 <<
"expected ranked type";
210 auto elementType = shapedType.getElementType();
211 typeAttr = TypeAttr::get(elementType);
218 <<
"expected shaped type";
235 <<
"expected attribute";
237 if (
auto typedAttr = dyn_cast<TypedAttr>(initialValueAttr)) {
238 return getShapeAndElementType(parser, typedAttr.getType(), varShapeAttr,
242 <<
"expected Typed attr";
245 initialValueAttr =
nullptr;
249 <<
"expected type after colon";
251 return getShapeAndElementType(parser, parsedType, varShapeAttr, typeAttr);
256 TypeAttr typeAttr,
Attribute initialValueAttr) {
257 bool needsSpace =
false;
258 if (!dyn_cast_or_null<TypedAttr>(initialValueAttr)) {
261 Type elementType = typeAttr.getValue();
262 RankedTensorType tensorType =
264 auto tensorTypeAttr = TypeAttr::get(tensorType);
269 if (initialValueAttr) {
280template <
typename EnumType>
281ParseResult parseAttrEntryWithEnumHandling(
OpAsmParser &parser,
283 llvm::StringRef name;
290 if constexpr (std::is_same_v<EnumType, tosa::RoundingMode>) {
291 if (name ==
"rounding_mode" &&
293 auto sym = symbolizeRoundingMode(kw);
296 <<
"invalid rounding_mode value: " << kw;
297 auto attr = RoundingModeAttr::get(parser.
getContext(), sym.value());
303 if constexpr (std::is_same_v<EnumType, tosa::ResizeMode>) {
305 auto sym = symbolizeResizeMode(kw);
308 <<
"invalid resize mode value: " << kw;
309 auto attr = ResizeModeAttr::get(parser.
getContext(), sym.value());
316 if constexpr (std::is_same_v<EnumType, tosa::NanPropagationMode>) {
318 auto sym = symbolizeNanPropagationMode(kw);
321 <<
"invalid nan_mode value: " << kw;
322 auto attr = NanPropagationModeAttr::get(parser.
getContext(), sym.value());
329 if constexpr (std::is_same_v<EnumType, tosa::BlockSize>) {
331 auto sym = symbolizeBlockSize(kw);
334 <<
"invalid block_size value: " << kw;
335 auto attr = BlockSizeAttr::get(parser.
getContext(), sym.value());
347template <
typename EnumType>
352 [&]() { return parser.parseOperand(operands.emplace_back()); }))
360 if (parseAttrEntryWithEnumHandling<EnumType>(parser, attrs))
377 result.addTypes(fnTy.getResults());
378 result.addAttributes(attrs);
384 parser << namedAttr.
getName().strref() <<
" = ";
386 if (
auto roundingModeAttr = dyn_cast<tosa::RoundingModeAttr>(attr)) {
387 parser << roundingModeAttr.getValue();
388 }
else if (
auto resizeModeAttr = dyn_cast<tosa::ResizeModeAttr>(attr)) {
389 parser << resizeModeAttr.getValue();
390 }
else if (
auto nanPropagationModeAttr =
391 dyn_cast<tosa::NanPropagationModeAttr>(attr)) {
392 parser << nanPropagationModeAttr.getValue();
393 }
else if (
auto blockSizeAttr = dyn_cast<tosa::BlockSizeAttr>(attr)) {
394 parser << blockSizeAttr.getValue();
407 const auto kDefaultNanValue = NanPropagationMode::PROPAGATE;
409 if (
auto nanAttr = dyn_cast<NanPropagationModeAttr>(attr.getValue())) {
410 if (nanAttr.getValue() == kDefaultNanValue) {
412 toPrint.erase(attr.getName());
418 if (!toPrint.empty()) {
420 llvm::interleaveComma(toPrint, parser, [&](
const NamedAttribute namedAttr) {
421 printNamedAttr(parser, namedAttr);
437 llvm::interleaveComma(op->
getAttrs(), parser,
439 printNamedAttr(parser, namedAttr);
451 return parseWithEnumHandling<tosa::RoundingMode>(parser,
result);
455 printWithEnumHandling(parser, *
this);
459 return parseWithEnumHandling<tosa::RoundingMode>(parser,
result);
463 printWithEnumHandling(parser, *
this);
467 return parseWithEnumHandling<tosa::ResizeMode>(parser,
result);
471 printWithEnumHandling(parser, *
this);
475 return parseWithEnumHandling<tosa::NanPropagationMode>(parser,
result);
479 printWithNanPropagationHandling(parser, *
this);
483 return parseWithEnumHandling<tosa::NanPropagationMode>(parser,
result);
487 printWithNanPropagationHandling(parser, *
this);
490ParseResult MaxPool2dAdaptiveOp::parse(
OpAsmParser &parser,
492 return parseWithEnumHandling<tosa::NanPropagationMode>(parser,
result);
496 printWithNanPropagationHandling(parser, *
this);
500 return parseWithEnumHandling<tosa::NanPropagationMode>(parser,
result);
504 printWithNanPropagationHandling(parser, *
this);
508 return parseWithEnumHandling<tosa::NanPropagationMode>(parser,
result);
512 printWithNanPropagationHandling(parser, *
this);
516 return parseWithEnumHandling<tosa::NanPropagationMode>(parser,
result);
520 printWithNanPropagationHandling(parser, *
this);
524 return parseWithEnumHandling<tosa::NanPropagationMode>(parser,
result);
528 printWithNanPropagationHandling(parser, *
this);
532 return parseWithEnumHandling<tosa::NanPropagationMode>(parser,
result);
536 printWithNanPropagationHandling(parser, *
this);
539ParseResult MatmulTBlockScaledOp::parse(
OpAsmParser &parser,
541 return parseWithEnumHandling<tosa::BlockSize>(parser,
result);
545 printWithEnumHandling(parser, *
this);
548ParseResult CastFromBlockScaledOp::parse(
OpAsmParser &parser,
550 return parseWithEnumHandling<tosa::BlockSize>(parser,
result);
553void CastFromBlockScaledOp::print(
OpAsmPrinter &parser) {
554 printWithEnumHandling(parser, *
this);
557ParseResult CastToBlockScaledOp::parse(
OpAsmParser &parser,
559 return parseWithEnumHandling<tosa::BlockSize>(parser,
result);
563 printWithEnumHandling(parser, *
this);
566ParseResult Conv2DBlockScaledOp::parse(
OpAsmParser &parser,
568 return parseWithEnumHandling<tosa::BlockSize>(parser,
result);
572 printWithEnumHandling(parser, *
this);
587 if (
auto quantType = llvm::dyn_cast<mlir::quant::QuantizedType>(srcType))
597 Value valZp, StringRef name) {
602 mlir::isa<IntegerType>(eType) && mlir::isa<IntegerType>(eZpType);
606 if (!bothInts || !sameBitWidth) {
608 <<
"expected " << name <<
" and " << name
609 <<
"_zp to both be integer of the same bitwidth, but got " << eType
610 <<
" vs. " << eZpType;
617 Value src, int32_t val) {
620 const auto padConstType = mlir::RankedTensorType::get({1}, srcType);
621 const auto padConstEType = mlir::RankedTensorType::get({1}, srcElemType);
622 const auto padConstAttr{
623 llvm::isa<FloatType>(srcElemType)
628 return tosa::ConstOp::create(builder, loc, padConstType, padConstAttr);
632 if (
auto blockScaledTy = dyn_cast<tosa::BlockScaledType>(type))
634 if (dyn_cast<tosa::mxint8Type>(type))
643 const StringRef operandName,
644 const StringRef dimName) {
645 if (ShapedType::isDynamic(currDim)) {
648 }
else if (ShapedType::isStatic(newDim) && currDim != newDim) {
650 << dimName <<
" of " << operandName <<
" to match size " << currDim
651 <<
", got " << newDim;
658 auto printDim = [&](
int64_t dim) {
659 if (ShapedType::isDynamic(dim))
665 llvm::interleaveComma(
shape,
diag, printDim);
671 StringRef outputName =
"output") {
672 assert(outputType.hasRank() &&
"expected output type to be ranked");
678 diag << outputName <<
" shape ";
680 diag <<
" to be compatible with inferred shape ";
688 const int64_t stride,
const int64_t dilation,
const llvm::StringRef dimName,
689 const llvm::StringRef dimAxis,
const llvm::StringRef padBeforeName,
690 const llvm::StringRef padAfterName) {
691 if (inputSize == ShapedType::kDynamic || kernelSize == ShapedType::kDynamic)
696 const std::optional<int64_t> calculatedOutSizeMinusOne =
idivCheck(
697 inputSize - 1 + padBefore + padAfter - (kernelSize - 1) * dilation,
699 if (!calculatedOutSizeMinusOne.has_value())
701 << dimName <<
" - 1 + pad_" << padBeforeName <<
" + pad_"
702 << padAfterName <<
" - (kernel_" << dimName <<
" - 1) * dilation_"
703 << dimAxis <<
" to be wholly divisible by stride_" << dimAxis
704 <<
", got (" << inputSize <<
" - 1 + " << padBefore <<
" + "
705 << padAfter <<
" - (" << kernelSize <<
" - 1) * " << dilation
708 const int64_t calculatedOutSize = calculatedOutSizeMinusOne.value() + 1;
709 if (outputSize != ShapedType::kDynamic && calculatedOutSize != outputSize)
711 << dimName <<
" did not match expected: "
712 <<
"calculated=" << calculatedOutSize <<
", expected=" << outputSize;
720size_t mlir::tosa::mxint8Type::getDenseElementBitSize()
const {
return 8; }
723mlir::tosa::mxint8Type::convertToAttribute(
ArrayRef<char> rawData)
const {
724 assert(rawData.size() == 1 &&
"expected 1 byte for tosa.mxint8 element");
725 const auto intType = IntegerType::get(
getContext(), 8);
726 return intType.convertToAttribute(rawData);
729LogicalResult mlir::tosa::mxint8Type::convertFromAttribute(
731 const auto intAttr = dyn_cast<IntegerAttr>(attr);
734 const Type attrType = intAttr.getType();
737 return cast<IntegerType>(attrType).convertFromAttribute(attr,
result);
745 bool allowScaleValues) {
746 const auto tensorType = llvm::cast<ShapedType>(type);
747 const BlockScaledType elemType =
748 llvm::dyn_cast<BlockScaledType>(tensorType.getElementType());
752 if (!allowScaleValues && elemType.hasScaleValues())
755 if (!tensorType.hasRank())
758 if (tensorType.getRank() == 0)
762 const uint32_t blockSize =
763 BlockShapeAttr::getBlockShapeValue(elemType.getBlockShape());
765 if (allowScaleValues && elemType.hasScaleValues() &&
766 tensorType.hasStaticShape()) {
767 const size_t numBlocks = tensorType.getNumElements() / blockSize;
768 if (elemType.getScaleValues().size() != numBlocks)
772 const int64_t blockedDimension = tensorShape.back();
773 if (ShapedType::isDynamic(blockedDimension))
775 if (blockedDimension % blockSize != 0)
784 const auto parseScaleValue = [&]() -> ParseResult {
791 if (floatValue < 0.0)
792 return parser.
emitError(loc,
"scale value must be non-negative, got ")
795 Type attrType = scaleType;
799 if (attrType != scaleType)
800 return parser.
emitError(loc,
"parsed attribute type ")
801 << attrType <<
" does not match expected scale type " << scaleType;
803 scaleValues.push_back(FloatAttr::get(attrType, floatValue));
812 llvm::interleaveComma(scaleValues, printer, [&](
Attribute scaleValue) {
817size_t mlir::tosa::BlockScaledType::getDenseElementBitSize()
const {
819 if (isa<tosa::mxint8Type>(valueType))
825mlir::tosa::BlockScaledType::convertToAttribute(
ArrayRef<char> rawData)
const {
829 assert(rawData.size() == 1 &&
"expected 1 byte for block_scaled element");
831 if (
const auto mxint8Value = dyn_cast<tosa::mxint8Type>(valueType))
832 return mxint8Value.convertToAttribute(rawData);
833 if (!isa<FloatType>(valueType))
838LogicalResult mlir::tosa::BlockScaledType::convertFromAttribute(
841 if (
const auto mxint8Value = dyn_cast<tosa::mxint8Type>(valueType))
842 return mxint8Value.convertFromAttribute(attr,
result);
844 const auto floatAttr = dyn_cast<FloatAttr>(attr);
845 if (!floatAttr || floatAttr.getType() != valueType)
858 const auto inputType = llvm::dyn_cast<TensorType>(op.getInput().getType());
859 const auto weightType = llvm::dyn_cast<TensorType>(op.getWeight().getType());
861 auto inputEType = inputType.getElementType();
862 auto weightEType = weightType.getElementType();
864 llvm::cast<ShapedType>(op.getBias().getType()).getElementType();
866 llvm::cast<ShapedType>(op.getResult().getType()).getElementType();
867 bool biasIsFloat = llvm::isa<FloatType>(biasEType);
868 bool resultIsFloat = llvm::isa<FloatType>(resultEType);
870 if (
auto quantType = llvm::dyn_cast<mlir::quant::QuantizedType>(inputEType))
873 if (
auto quantType = llvm::dyn_cast<mlir::quant::QuantizedType>(weightEType))
876 if (
auto quantType = llvm::dyn_cast<mlir::quant::QuantizedType>(biasEType))
879 if (
auto quantType = llvm::dyn_cast<mlir::quant::QuantizedType>(resultEType))
882 if (biasIsFloat && resultIsFloat && (biasEType != resultEType)) {
886 "expect both bias and result to have same element type, got ")
887 << biasEType <<
" and " << resultEType;
891 if (isa<Float8E5M2Type>(inputEType) || isa<Float8E4M3FNType>(inputEType) ||
892 isa<Float8E5M2Type>(weightEType) || isa<Float8E4M3FNType>(weightEType)) {
893 if (inputEType != weightEType) {
895 "expect both input and weight to have same element type, got ")
896 << inputEType <<
" and " << weightEType;
901 bool inputIsFloat = llvm::isa<FloatType>(inputEType);
902 bool weightIsFloat = llvm::isa<FloatType>(weightEType);
905 if (inputIsFloat != weightIsFloat) {
907 "expect both input and weight to be float or not together, got ")
908 << inputEType <<
" and " << weightEType;
913 if (inputEType != inputZpEType) {
914 return op.emitOpError(
"expect both input and its zero point are the same "
915 "element type, got ")
916 << inputEType <<
" and " << inputZpEType;
920 if (weightEType != weightZpEType) {
921 return op.emitOpError(
"expect both weight and its zero point are the same "
922 "element type, got ")
923 << weightEType <<
" and " << weightZpEType;
926 FailureOr<int64_t> maybeIZp = op.getInputZeroPoint();
927 if (succeeded(maybeIZp) && op.verifyInputZeroPoint(*maybeIZp).failed())
930 FailureOr<int64_t> maybeWZp = op.getWeightZeroPoint();
931 if (succeeded(maybeWZp) && op.verifyWeightZeroPoint(*maybeWZp).failed())
937LogicalResult tosa::ConstOp::verify() {
939 auto attrType = llvm::dyn_cast<TensorType>(getValuesAttr().
getType());
940 auto outputType = llvm::dyn_cast<TensorType>(getOutput().
getType());
942 if (!attrType || !outputType) {
943 emitOpError(
"expected tensors for attr/result type");
947 const Type attrElemType = attrType.getElementType();
948 const Type resultElemType = outputType.getElementType();
951 llvm::dyn_cast<mlir::quant::QuantizedType>(resultElemType)) {
956 if (
auto attrBlockScaledType =
957 llvm::dyn_cast<mlir::tosa::BlockScaledType>(attrElemType)) {
958 if (!attrBlockScaledType.hasScaleValues())
960 "attribute block scaled type must have scale values");
963 return op.
emitOpError(
"block scaled attribute type is not valid, got ")
966 const BlockScaledType resultBlockScaledType =
967 llvm::dyn_cast<mlir::tosa::BlockScaledType>(resultElemType);
968 if (!resultBlockScaledType)
970 "result type must be block scaled type if attribute is block "
973 if (attrBlockScaledType.getValueType() !=
974 resultBlockScaledType.getValueType() ||
975 attrBlockScaledType.getScaleType() !=
976 resultBlockScaledType.getScaleType() ||
977 attrBlockScaledType.getBlockShape() !=
978 resultBlockScaledType.getBlockShape())
980 "expected block scaled element type to be compatible "
981 "between attr and result, got ")
982 << attrBlockScaledType <<
" vs. " << resultBlockScaledType;
987 if (attrElemType != resultElemType)
988 return emitOpError(
"expected same attr/result element types");
996 llvm::cast<ShapedType>(op.getInput().getType()).getElementType();
998 if (
auto quantType = llvm::dyn_cast<mlir::quant::QuantizedType>(inputEType))
1001 auto accType = op.getAccType();
1002 if (inputEType.isInteger(8) && !accType.isInteger(32))
1003 return op.emitOpError(
"accumulator type for i8 tensor is not i32, got ")
1006 if (inputEType.isInteger(16) && !accType.isInteger(48))
1007 return op.emitOpError(
"accumulator type for i16 tensor is not i48, got ")
1010 if (isa<Float8E5M2Type, Float8E4M3Type>(inputEType) &&
1011 !(accType.isF16() || accType.isF32()))
1012 return op.emitOpError(
"accumulator type for f8 tensor is not f16/f32, got ")
1015 if (inputEType.isF16() && !(accType.isF16() || accType.isF32()))
1016 return op.emitOpError(
1017 "accumulator type for f16 tensor is not f16/f32, got ")
1020 if (inputEType.isBF16() && !accType.isF32())
1021 return op.emitOpError(
"accumulator type for bf16 tensor is not f32, got ")
1024 if (inputEType.isF32() && !accType.isF32())
1025 return op.emitOpError(
"accumulator type for f32 tensor is not f32, got ")
1029 llvm::cast<ShapedType>(op.getResult().getType()).getElementType();
1031 if (
auto quantType = llvm::dyn_cast<mlir::quant::QuantizedType>(resultEType))
1042template <
typename T>
1045 if (llvm::any_of(padding, [](
int64_t p) {
return p < 0; }))
1046 return op.emitOpError(
"expect all padding values to be >= 0, got ")
1050 if (llvm::any_of(strides, [](
int64_t s) {
return s < 1; }))
1051 return op.emitOpError(
"expect all stride values to be >= 1, got ")
1055 if (llvm::any_of(dilations, [](
int64_t d) {
return d < 1; }))
1056 return op.emitOpError(
"expect all dilation values to be >= 1, got ")
1059 const RankedTensorType outputType =
1060 llvm::dyn_cast<RankedTensorType>(op.getOutput().getType());
1065 const RankedTensorType inputType =
1066 llvm::dyn_cast<RankedTensorType>(op.getInput().getType());
1067 const RankedTensorType weightType =
1068 llvm::dyn_cast<RankedTensorType>(op.getWeight().getType());
1070 if (inputType && weightType) {
1072 if constexpr (std::is_same<T, tosa::Conv2DOp>::value) {
1074 op, inputType.getDimSize(1), weightType.getDimSize(1),
1075 outputType.getDimSize(1), padding[0], padding[1], strides[0],
1076 dilations[0],
"height",
"y",
"top",
"bottom")))
1080 op, inputType.getDimSize(2), weightType.getDimSize(2),
1081 outputType.getDimSize(2), padding[2], padding[3], strides[1],
1082 dilations[1],
"width",
"x",
"left",
"right")))
1087 if constexpr (std::is_same<T, tosa::DepthwiseConv2DOp>::value) {
1089 op, inputType.getDimSize(1), weightType.getDimSize(0),
1090 outputType.getDimSize(1), padding[0], padding[1], strides[0],
1091 dilations[0],
"height",
"y",
"top",
"bottom")))
1095 op, inputType.getDimSize(2), weightType.getDimSize(1),
1096 outputType.getDimSize(2), padding[2], padding[3], strides[1],
1097 dilations[1],
"width",
"x",
"left",
"right")))
1102 if constexpr (std::is_same<T, tosa::Conv3DOp>::value) {
1104 op, inputType.getDimSize(1), weightType.getDimSize(1),
1105 outputType.getDimSize(1), padding[0], padding[1], strides[0],
1106 dilations[0],
"depth",
"d",
"front",
"back")))
1110 op, inputType.getDimSize(2), weightType.getDimSize(2),
1111 outputType.getDimSize(2), padding[2], padding[3], strides[1],
1112 dilations[1],
"height",
"y",
"top",
"bottom")))
1116 op, inputType.getDimSize(3), weightType.getDimSize(3),
1117 outputType.getDimSize(3), padding[4], padding[5], strides[2],
1118 dilations[2],
"width",
"x",
"left",
"right")))
1123 const RankedTensorType biasType =
1124 llvm::dyn_cast<RankedTensorType>(op.getBias().getType());
1129 const int64_t biasChannels = biasType.getDimSize(0);
1130 const int64_t outputChannels =
1131 outputType.getDimSize(outputType.getRank() - 1);
1132 if (biasChannels == ShapedType::kDynamic ||
1133 outputChannels == ShapedType::kDynamic)
1137 if (biasChannels != outputChannels && biasChannels != 1)
1138 return op.emitOpError(
1139 "bias channels expected to be equal to output channels (")
1140 << outputChannels <<
") or 1, got " << biasChannels;
1147 StringRef name1,
Type type2,
1149 auto shapeType1 = dyn_cast<ShapedType>(type1);
1150 auto shapeType2 = dyn_cast<ShapedType>(type2);
1151 if (!shapeType1 || !shapeType2)
1154 auto elemType1 = shapeType1.getElementType();
1155 auto elemType2 = shapeType2.getElementType();
1156 if (elemType1 != elemType2)
1158 <<
"require same element type for " << name1 <<
" (" << elemType1
1159 <<
") and " << name2 <<
" (" << elemType2 <<
")";
1163 <<
"require same shapes for " << name1 <<
" (" << type1 <<
") and "
1164 << name2 <<
" (" << type2 <<
")";
1174 if (list1.size() != list2.size())
1176 <<
"require same number of values in " << name1 <<
" ("
1177 << list1.size() <<
") and " << name2 <<
" (" << list2.size() <<
")";
1179 for (
auto [type1, type2] :
1196template <
typename T>
1199 op->template getParentWithTrait<OpTrait::SymbolTable>();
1206 const auto varOp = symTable.
lookup<tosa::VariableOp>(op.getName());
1210 return op->emitOpError(
"'")
1211 << op.getName() <<
"' has not been declared by 'tosa.variable'";
1225 StringRef aName =
"input",
1226 StringRef bName =
"output") {
1227 auto aTType = llvm::dyn_cast<TensorType>(aType);
1228 auto bTType = llvm::dyn_cast<TensorType>(bType);
1230 op->
emitOpError(
"expect shaped tensor for") << aName <<
", got " << aType;
1234 op->
emitOpError(
"expect shaped tensor for") << bName <<
", got" << bType;
1237 auto aElementType = aTType.getElementType();
1238 auto bElementType = bTType.getElementType();
1240 llvm::dyn_cast<mlir::quant::UniformQuantizedType>(aElementType);
1242 llvm::dyn_cast<mlir::quant::UniformQuantizedType>(bElementType);
1243 if ((aElementType.isIntOrIndexOrFloat() || aQuantType) &&
1244 (bElementType.isIntOrIndexOrFloat() || bQuantType) &&
1245 aElementType != bElementType) {
1251 << aName <<
" and " << bName <<
" to have same element type, got "
1252 << aElementType <<
" and " << bElementType;
1258LogicalResult tosa::ArgMaxOp::verify() {
1259 const ShapedType resultType = llvm::cast<ShapedType>(
getType());
1262 if (
const auto resultETy = resultType.getElementType();
1263 !resultETy.isIntOrIndex())
1264 return emitOpError(
"result tensor is not of integer type");
1266 const auto inputType = llvm::cast<ShapedType>(getInput().
getType());
1267 if (!inputType.hasRank())
1271 const int64_t axis = getAxisAttr().getInt();
1272 if (((axis < 0) || axis >= inputType.getRank()))
1273 return emitOpError(
"specified axis is outside the rank of the tensor");
1275 if (!resultType.hasRank())
1281 expectedOutputShape.erase(expectedOutputShape.begin() + axis);
1284 << expectedOutputShape <<
"', got '" << outputShape <<
"'";
1297 const bool hasKernel = kernel.size() > 0;
1298 const bool hasStrides = strides.size() > 0;
1299 const bool hasPad = padding.size() > 0;
1301 if (hasKernel && llvm::any_of(kernel, [](
int64_t s) {
return s < 1; }))
1302 return op->
emitOpError(
"expect all kernel values to be >= 1, got ")
1305 if (hasStrides && llvm::any_of(strides, [](
int64_t s) {
return s < 1; }))
1306 return op->
emitOpError(
"expect all stride values to be >= 1, got ")
1309 if (hasPad && llvm::any_of(padding, [](
int64_t p) {
return p < 0; }))
1310 return op->
emitOpError(
"expect all padding values to be >= 0, got ")
1313 if (hasKernel && hasPad) {
1315 const int64_t kernelX = kernel[1];
1316 const int64_t padLeft = padding[2];
1317 const int64_t padRight = padding[3];
1318 if (padRight >= kernelX || padLeft >= kernelX)
1319 return op->
emitOpError(
"expected left/right padding to be less than the "
1320 "width of the kernel, got pad_left=")
1321 << padLeft <<
", pad_right=" << padRight
1322 <<
", kernel_x=" << kernelX;
1324 const int64_t kernelY = kernel[0];
1325 const int64_t padTop = padding[0];
1326 const int64_t padBottom = padding[1];
1327 if (padTop >= kernelY || padBottom >= kernelY)
1328 return op->
emitOpError(
"expected top/bottom padding to be less than the "
1329 "height of the kernel, got pad_top=")
1330 << padTop <<
", pad_bottom=" << padBottom
1331 <<
", kernel_y=" << kernelY;
1334 const auto inputType = llvm::dyn_cast<RankedTensorType>(input.
getType());
1335 const auto outputType = llvm::dyn_cast<RankedTensorType>(output.
getType());
1336 if (!inputType || !outputType)
1339 if (hasKernel && hasStrides && hasPad) {
1340 const auto verifyOutputSize =
1344 const llvm::StringRef dimName,
const llvm::StringRef dimAxis,
1345 const llvm::StringRef padBeforeName,
1346 const llvm::StringRef padAfterName) -> LogicalResult {
1347 if (ShapedType::isDynamic(inputSize))
1350 const std::optional<int64_t> calculatedOutSizeMinusOne =
1351 idivCheck(inputSize + padBefore + padAfter - kernelSize, strideSize);
1352 if (!calculatedOutSizeMinusOne.has_value())
1354 << dimName <<
" + pad_" << padBeforeName <<
" + pad_"
1355 << padAfterName <<
" - kernel_" << dimAxis
1356 <<
" to be wholly divisible by stride_" << dimAxis <<
", got ("
1357 << inputSize <<
" + " << padBefore <<
" + " << padAfter <<
" - "
1358 << kernelSize <<
") / " << strideSize;
1360 const int64_t calculatedOutSize = calculatedOutSizeMinusOne.value() + 1;
1361 if (ShapedType::isStatic(outputSize) && calculatedOutSize != outputSize)
1363 << dimName <<
" did not match expected: " <<
"calculated="
1364 << calculatedOutSize <<
", expected=" << outputSize;
1369 if (failed(verifyOutputSize(inputType.getDimSize(1),
1370 outputType.getDimSize(1), kernel[0], strides[0],
1371 padding[0], padding[1],
"height",
"y",
"top",
1375 if (failed(verifyOutputSize(
1376 inputType.getDimSize(2), outputType.getDimSize(2), kernel[1],
1377 strides[1], padding[2], padding[3],
"width",
"x",
"left",
"right")))
1383template <
typename T>
1386 op.getPad(), op.getInput(), op.getOutput());
1389template <
typename T>
1393 const Type inputZpETy =
1395 const Type outputZpETy =
1398 auto accType = op.getAccType();
1399 if (llvm::isa<IntegerType>(inputETy) && !accType.isInteger(32))
1400 return op.emitOpError(
"accumulator type for integer tensor is not i32");
1402 if (inputETy.
isF16() && !(accType.isF16() || accType.isF32()))
1403 return op.emitOpError(
"accumulator type for f16 tensor is not f16/f32");
1405 if (inputETy.
isBF16() && !accType.isF32())
1406 return op.emitOpError(
"accumulator type for bf16 tensor is not f32");
1408 if (inputETy.
isF32() && !accType.isF32())
1409 return op.emitOpError(
"accumulator type for f32 tensor is not f32");
1411 if (inputETy != inputZpETy)
1412 return op.emitOpError(
"expect both input and its zero point are the same "
1413 "element type, got ")
1414 << inputETy <<
" and " << inputZpETy;
1416 if (resultETy != outputZpETy)
1417 return op.emitOpError(
"expect both output and its zero point are the same "
1418 "element type, got ")
1419 << resultETy <<
" and " << outputZpETy;
1421 FailureOr<int64_t> maybeIZp = op.getInputZeroPoint();
1422 if (succeeded(maybeIZp) && op.verifyInputZeroPoint(*maybeIZp).failed())
1425 FailureOr<int64_t> maybeOZp = op.getOutputZeroPoint();
1426 if (succeeded(maybeOZp) && op.verifyOutputZeroPoint(*maybeOZp).failed())
1433struct AdaptivePoolingConstShapeValues {
1434 llvm::SmallVector<int64_t> kernel;
1435 llvm::SmallVector<int64_t> stride;
1436 llvm::SmallVector<int64_t> pad;
1440template <
typename T>
1442 std::is_same_v<T, tosa::AvgPool2dAdaptiveOp> ||
1443 std::is_same_v<T, tosa::MaxPool2dAdaptiveOp>;
1445template <
typename T,
1446 typename std::enable_if<IsSupportedAdaptivePoolConstShapeVerifyOp<T>,
1449 T op, AdaptivePoolingConstShapeValues &values) {
1455LogicalResult tosa::AvgPool2dOp::verify() {
1463LogicalResult tosa::AvgPool2dAdaptiveOp::verify() {
1464 AdaptivePoolingConstShapeValues values;
1473 values.pad, getInput(), getOutput())))
1482LogicalResult tosa::ClampOp::verify() {
1484 llvm::cast<ShapedType>(getInput().
getType()).getElementType();
1485 if (
auto quantType =
1486 llvm::dyn_cast<mlir::quant::UniformQuantizedType>(inputETy)) {
1490 llvm::cast<ShapedType>(getOutput().
getType()).getElementType();
1491 if (
auto quantType =
1492 llvm::dyn_cast<mlir::quant::UniformQuantizedType>(outputETy)) {
1495 if (inputETy != outputETy)
1496 return emitOpError(
"input/output element types are incompatible.");
1498 auto maxValAttr = getMaxValAttr();
1499 auto minValAttr = getMinValAttr();
1503 if (inputETy.
isInteger(dataTypeBitWidth)) {
1507 auto intMaxValAttr = mlir::dyn_cast<mlir::IntegerAttr>(maxValAttr);
1508 auto intMinValAttr = mlir::dyn_cast<mlir::IntegerAttr>(minValAttr);
1509 if (!intMaxValAttr || !intMinValAttr ||
1510 (intMaxValAttr.getType() != intMinValAttr.getType()) ||
1511 (intMaxValAttr.getType() != inputETy))
1512 return emitOpError(
"min/max attributes types are incompatible with "
1513 "input/output element types.");
1516 const bool isBoolean = inputETy.
isInteger(1);
1517 const APInt minVal = intMinValAttr.getValue();
1518 const APInt maxVal = intMaxValAttr.getValue();
1519 if ((isUnsigned || isBoolean) ? maxVal.ult(minVal) : maxVal.slt(minVal))
1520 return emitOpError(
"expected min_val <= max_val, got min_val=")
1521 << minValAttr <<
", max_val=" << maxValAttr;
1526 auto floatMaxValAttr = mlir::dyn_cast<mlir::FloatAttr>(maxValAttr);
1527 auto floatMinValAttr = mlir::dyn_cast<mlir::FloatAttr>(minValAttr);
1528 if (!floatMaxValAttr || !floatMinValAttr ||
1529 (floatMaxValAttr.getType() != floatMinValAttr.getType()) ||
1530 (floatMaxValAttr.getType() != inputETy))
1531 return emitOpError(
"min/max attributes types are incompatible with "
1532 "input/output element types.");
1534 const APFloat minVal = floatMinValAttr.getValue();
1535 const APFloat maxVal = floatMaxValAttr.getValue();
1536 if (minVal.isNaN() || maxVal.isNaN())
1537 return emitOpError(
"min/max attributes should not be 'NaN', got min_val=")
1538 << minValAttr <<
", max_val=" << maxValAttr;
1540 if (maxVal < minVal)
1541 return emitOpError(
"expected min_val <= max_val, got min_val=")
1542 << minValAttr <<
", max_val=" << maxValAttr;
1562 result.addOperands({input, weight, bias, zps.first, zps.second});
1563 result.addAttribute(
"pad", pad);
1564 result.addAttribute(
"stride", stride);
1565 result.addAttribute(
"dilation", dilation);
1566 result.addAttribute(
"acc_type", accType);
1567 Type finalOutputType = outputType;
1573 result.addTypes(finalOutputType);
1584 result.addOperands({input, weight, bias, zps.first, zps.second});
1585 result.addAttribute(
"out_pad", outpad);
1586 result.addAttribute(
"stride", stride);
1587 result.addAttribute(
"acc_type", accType);
1588 Type finalOutputType = outputType;
1594 result.addTypes(finalOutputType);
1601 result.addOperands({a,
b, zps.first, zps.second});
1603 Type finalOutputType{outputType};
1606 auto inputBits = eType.getIntOrFloatBitWidth();
1608 auto outputShapedType = llvm::dyn_cast<ShapedType>(outputType);
1609 assert(outputShapedType &&
"Output must be a shaped type");
1611 IntegerType accElementType;
1612 if (inputBits == 16)
1617 finalOutputType = outputShapedType.clone(accElementType);
1619 result.addTypes(finalOutputType);
1640 DenseArrayAttr kernel, DenseArrayAttr stride,
1641 DenseArrayAttr pad, TypeAttr accType) {
1646 if (
auto quantAttr =
1648 inputZp = quantAttr.getInputZp();
1649 outputZp = quantAttr.getOutputZp();
1651 const std::optional<Value> inputZpOp =
1656 "Failed to create input zero point tensor for quantized AVG_POOL2D op");
1658 const std::optional<Value> outputZpOp =
1661 (
void)
emitError(loc,
"Failed to create output zero point tensor for "
1662 "quantized AVG_POOL2D op");
1665 if (inputZpOp && outputZpOp) {
1666 result.addOperands({input, inputZpOp.value(), outputZpOp.value()});
1671 result.addOperands({input});
1673 result.addAttribute(
"kernel", kernel);
1674 result.addAttribute(
"stride", stride);
1675 result.addAttribute(
"pad", pad);
1676 result.addAttribute(
"acc_type", accType);
1677 result.types.push_back(outputType);
1690 if (
auto quantAttr =
1692 inputZp = quantAttr.getInputZp();
1693 outputZp = quantAttr.getOutputZp();
1695 const std::optional<Value> inputZpOp =
1699 "Failed to create input zero point tensor for quantized "
1700 "AVG_POOL2D_ADAPTIVE op");
1702 const std::optional<Value> outputZpOp =
1705 (
void)
emitError(loc,
"Failed to create output zero point tensor for "
1706 "quantized AVG_POOL2D_ADAPTIVE op");
1709 if (inputZpOp && outputZpOp) {
1714 result.addOperands({input, inputZpOp.value(), outputZpOp.value(),
1715 kernelShape, strideShape, padShape});
1720 result.addOperands({input});
1722 result.addAttribute(
"acc_type", accType);
1723 result.types.push_back(outputType);
1737 input1Zp = quantAttr.getInputZp();
1738 outputZp = quantAttr.getOutputZp();
1740 const std::optional<Value> input1ZpOp =
1744 loc,
"Failed to create input1 zero point for quantized NEGATE op");
1747 const std::optional<Value> outputZpOp =
1751 loc,
"Failed to create output zero point for quantized NEGATE op");
1754 if (input1ZpOp && outputZpOp) {
1755 result.addOperands({input, input1ZpOp.value(), outputZpOp.value()});
1760 result.addOperands({input});
1763 result.types.push_back(outputType);
1776 zp =
static_cast<int32_t
>(quantAttr.getInputZp());
1779 result.addOperands({input, paddings, padConstOp});
1780 result.types.push_back(outputType);
1784 StringRef name,
Type variableType,
1789 auto shapedType = dyn_cast<ShapedType>(variableType);
1791 (
void)
emitError(loc,
"variable type must be a shaped type");
1794 if (!shapedType.hasRank()) {
1795 (
void)
emitError(loc,
"variable type must be a ranked type");
1799 auto elementType = shapedType.getElementType();
1800 auto elementTypeAttr = TypeAttr::get(elementType);
1804 result.addAttribute(
"sym_name", nameAttr);
1805 result.addAttribute(
"var_shape", varShapeAttr);
1806 result.addAttribute(
"type", elementTypeAttr);
1807 result.addAttribute(
"initial_value", initialValue);
1820 if (ShapedType::isStatic(dim1) && ShapedType::isStatic(dim2) && dim1 != dim2)
1824 return ShapedType::isDynamic(dim1) ? dim2 : dim1;
1830 for (
int i = 0, e = operands.size(); i != e; ++i) {
1832 if (!
shape.hasRank()) {
1837 outRank = std::max<int64_t>(outRank,
shape.getRank());
1840 outShape.resize(outRank, 1);
1842 for (
int i = 0, e = operands.size(); i != e; ++i) {
1844 auto rankDiff = outShape.size() -
shape.getRank();
1846 for (
size_t i = 0, e =
shape.getRank(); i < e; ++i) {
1847 auto dim1 = outShape[i + rankDiff];
1848 auto dim2 =
shape.getDimSize(i);
1850 const FailureOr<int64_t> maybeResolvedDim =
1852 if (failed(maybeResolvedDim))
1854 const int64_t resolvedDim = *maybeResolvedDim;
1855 outShape[i + rankDiff] = resolvedDim;
1862LogicalResult tosa::ArgMaxOp::inferReturnTypeComponents(
1863 MLIRContext *context, ::std::optional<Location> location,
1864 ArgMaxOp::Adaptor adaptor,
1867 IntegerAttr axis = adaptor.getProperties().axis;
1868 int32_t axisVal = axis.getValue().getSExtValue();
1870 if (!inputShape.hasRank()) {
1876 outShape.reserve(inputShape.getRank() - 1);
1877 for (
int i = 0, s = inputShape.getRank(); i < s; i++) {
1880 outShape.push_back(inputShape.getDimSize(i));
1887LogicalResult tosa::RFFT2dOp::inferReturnTypeComponents(
1888 MLIRContext *context, ::std::optional<Location> location,
1889 RFFT2dOp::Adaptor adaptor,
1891 ShapeAdaptor inputShape(adaptor.getInputReal().getType());
1893 if (!inputShape.hasRank())
1897 outputShape.resize(3, ShapedType::kDynamic);
1898 outputShape[0] = inputShape.getDimSize(0);
1899 outputShape[1] = inputShape.getDimSize(1);
1900 int64_t inWidth = inputShape.getDimSize(2);
1904 if (inWidth != ShapedType::kDynamic)
1905 outputShape[2] = inWidth / 2 + 1;
1914 const llvm::StringRef dimName) {
1915 const bool isPowerOfTwo = (dimSize & (dimSize - 1)) == 0 && dimSize > 0;
1918 << dimName <<
" to be a power of two, got " << dimSize;
1923LogicalResult tosa::RFFT2dOp::verify() {
1924 const auto outputTypes = getResultTypes();
1926 return emitOpError(
"expected output shapes to match, got ") << outputTypes;
1928 const auto inputType =
1929 llvm::dyn_cast<RankedTensorType>(getInputReal().
getType());
1933 const int64_t height = inputType.getDimSize(1);
1934 if (ShapedType::isStatic(height) &&
1938 const int64_t width = inputType.getDimSize(2);
1939 if (ShapedType::isStatic(width) &&
1943 const auto outputType = llvm::dyn_cast<RankedTensorType>(outputTypes[0]);
1949 outputType.getShape().drop_back())))
1950 return emitOpError(
"expected batch and height dimensions of input/output "
1951 "to match, got input=")
1952 << inputType <<
" output=" << outputType;
1955 const int64_t outputWidth = outputType.getDimSize(2);
1956 if (ShapedType::isStatic(width) && ShapedType::isStatic(outputWidth) &&
1957 (outputWidth != (width / 2) + 1))
1959 "expected output width to be equal to input_width / 2 + 1, got ")
1965LogicalResult tosa::FFT2dOp::inferReturnTypeComponents(
1966 MLIRContext *context, ::std::optional<Location> location,
1967 FFT2dOp::Adaptor adaptor,
1969 inferredReturnShapes.push_back(
1971 inferredReturnShapes.push_back(
1976LogicalResult tosa::FFT2dOp::verify() {
1977 const auto inputRealType =
1978 llvm::dyn_cast<RankedTensorType>(getInputReal().
getType());
1979 const auto inputImagType =
1980 llvm::dyn_cast<RankedTensorType>(getInputImag().
getType());
1981 if (!inputRealType || !inputImagType)
1984 const auto trySelectStaticDim = [](
const int64_t a,
const int64_t b) {
1985 return ShapedType::isDynamic(a) ? a :
b;
1988 const int64_t height = trySelectStaticDim(inputRealType.getDimSize(1),
1989 inputImagType.getDimSize(1));
1990 if (ShapedType::isStatic(height) &&
1994 const int64_t width = trySelectStaticDim(inputRealType.getDimSize(2),
1995 inputImagType.getDimSize(2));
1996 if (ShapedType::isStatic(width) &&
2003LogicalResult tosa::ConcatOp::inferReturnTypeComponents(
2004 MLIRContext *context, ::std::optional<Location> location,
2005 ConcatOp::Adaptor adaptor,
2008 const Properties &prop = adaptor.getProperties();
2009 int32_t axis = prop.axis.getValue().getSExtValue();
2011 bool hasRankedInput =
false;
2012 for (
auto operand : adaptor.getOperands()) {
2014 if (!operandShape.hasRank())
2018 if (!hasRankedInput)
2019 outputShape.resize(operandShape.getRank(), ShapedType::kDynamic);
2022 for (
int i = 0, s = operandShape.getRank(); i < s; i++) {
2023 if (i == axis || operandShape.isDynamicDim(i))
2025 if (outputShape[i] == ShapedType::kDynamic)
2026 outputShape[i] = operandShape.getDimSize(i);
2027 if (outputShape[i] != operandShape.getDimSize(i))
2029 "Cannot concat tensors with different sizes"
2030 " on the non-axis dimension ",
2034 hasRankedInput =
true;
2037 if (adaptor.getInput1().empty())
2041 llvm::cast<TensorType>(adaptor.getInput1().getType()[0]).getElementType();
2042 if (!hasRankedInput) {
2049 for (
auto operand : adaptor.getOperands()) {
2054 if (!operandShape.hasRank() || operandShape.isDynamicDim(axis)) {
2055 concatDimSize = ShapedType::kDynamic;
2059 concatDimSize += operandShape.getDimSize(axis);
2062 outputShape[axis] = concatDimSize;
2068LogicalResult tosa::ConcatOp::verify() {
2070 auto outType = getOutput().getType();
2074 if (inputList.empty())
2077 if (!llvm::all_of(inputList, [&](
auto input) {
2079 *
this, input.getType(), outType));
2084 const int32_t axis = getAxis();
2086 for (
const auto &input : inputList) {
2087 const Type inputType = input.getType();
2089 if (currShape.hasRank()) {
2090 firstRankedInputShape = currShape;
2092 if (axis < 0 || axis >= firstRankedInputShape.
getRank())
2093 return emitOpError(
"expect axis to be within range 0 < axis < "
2094 "rank(input1[firstRankedTensorIdx]), got ")
2100 const auto allOperandsHasRank = [](
const Value input) {
2103 if (llvm::all_of(inputList, allOperandsHasRank)) {
2106 for (
const auto &[
index, input] : llvm::enumerate(inputList.drop_front())) {
2108 const int64_t inputRank = inputShape.getRank();
2109 const size_t operandNum =
index + 1;
2112 if (inputRank != firstInputRank)
2114 "expect all operands to have the same rank, but got ")
2115 << firstInputRank <<
" vs " << inputRank <<
" on operands 0 and "
2119 for (
int i = 0; i < inputRank; i++) {
2120 const int64_t inputDim = inputShape.getDimSize(i);
2122 if (i == axis || firstRankedInputShape.
isDynamicDim(i) ||
2123 inputShape.isDynamicDim(i))
2125 if (inputDim != firstInputDim)
2126 return emitOpError(
"expect all operand shapes to have the same sizes "
2127 "on non-axis dimensions, but got ")
2128 << inputDim <<
" vs " << firstInputDim <<
" at index " << i
2129 <<
" on operands 0 and " << operandNum;
2134 if (outputShape.hasRank() && outputShape.getRank() != firstInputRank)
2135 return emitOpError(
"expect output rank to match inputs rank, got ")
2136 << outputShape.getRank() <<
" vs " << firstInputRank;
2140 for (
const auto &input : inputList) {
2142 if (inputShape.isDynamicDim(axis)) {
2147 axisSum += inputShape.getDimSize(axis);
2150 if (axisSum >= 0 && outputShape.hasRank() &&
2151 !outputShape.isDynamicDim(axis) &&
2152 axisSum != outputShape.getDimSize(axis))
2153 return emitOpError(
"requires sum of axis dimensions of input1 "
2154 "equal to output axis dimension, got ")
2155 << axisSum <<
" and " << outputShape.getDimSize(axis);
2161LogicalResult tosa::EqualOp::inferReturnTypeComponents(
2162 MLIRContext *context, ::std::optional<Location> location,
2166 auto elementType = IntegerType::get(context, 1);
2179 if (l.size() != r.size() || l.size() != 1)
2184LogicalResult tosa::MatMulOp::inferReturnTypeComponents(
2185 MLIRContext *context, ::std::optional<Location> location,
2186 MatMulOp::Adaptor adaptor,
2193 outShape.resize(3, ShapedType::kDynamic);
2195 if (lhsShape.hasRank()) {
2196 outShape[0] = lhsShape.getDimSize(0);
2197 outShape[1] = lhsShape.getDimSize(1);
2200 if (rhsShape.hasRank()) {
2201 outShape[0] = outShape[0] == ShapedType::kDynamic ? rhsShape.getDimSize(0)
2203 outShape[2] = rhsShape.getDimSize(2);
2210template <
typename T>
2212 Type bElementType) {
2213 const auto aQuantizedEType =
2214 llvm::dyn_cast<quant::UniformQuantizedType>(aElementType);
2215 const auto bQuantizedEType =
2216 llvm::dyn_cast<quant::UniformQuantizedType>(bElementType);
2218 if (aQuantizedEType || bQuantizedEType) {
2219 if (!aQuantizedEType || !bQuantizedEType) {
2220 return op.emitOpError(
"expect operands to be both quantized or both not "
2222 << aElementType <<
" and " << bElementType;
2225 auto aQuantWidth = aQuantizedEType.getStorageTypeIntegralWidth();
2226 auto bQuantWidth = bQuantizedEType.getStorageTypeIntegralWidth();
2227 if (aQuantWidth != bQuantWidth) {
2228 return op.emitOpError(
"expect quantized operands to have same widths, "
2230 << aQuantWidth <<
" and " << bQuantWidth;
2237template <
typename T>
2239 StringRef inputName,
2244 if (inputStorageElementType != zpElementType)
2245 return op.emitOpError(
"expect input ")
2246 << inputName <<
" and " << zpName
2247 <<
" have the same element type, got " << inputStorageElementType
2248 <<
" and " << zpElementType;
2253LogicalResult MatMulOp::verify() {
2256 const Type aElementType = aShape.getElementType();
2257 const Type bElementType = bShape.getElementType();
2267 FailureOr<int64_t> maybeAZp = getAZeroPoint();
2268 if (succeeded(maybeAZp) && verifyAZeroPoint(*maybeAZp).failed())
2271 FailureOr<int64_t> maybeBZp = getBZeroPoint();
2272 if (succeeded(maybeBZp) && verifyBZeroPoint(*maybeBZp).failed())
2276 int64_t N = ShapedType::kDynamic;
2277 int64_t H = ShapedType::kDynamic;
2281 if (aShape.hasRank()) {
2282 N = aShape.getDimSize(0);
2283 H = aShape.getDimSize(1);
2284 C = aShape.getDimSize(2);
2287 if (bShape.hasRank()) {
2293 W = bShape.getDimSize(2);
2297 const auto outputType = cast<ShapedType>(getResult().
getType());
2298 if (outputType.hasRank() &&
2300 expectedOutputShape)))
2306LogicalResult tosa::MatMulTOp::inferReturnTypeComponents(
2307 MLIRContext *context, ::std::optional<Location> location,
2308 MatMulTOp::Adaptor adaptor,
2315 if (lhsShape.hasRank()) {
2316 outShape[0] = lhsShape.getDimSize(0);
2317 outShape[1] = lhsShape.getDimSize(1);
2320 if (rhsShape.hasRank()) {
2321 const int64_t bBatchSize = rhsShape.getDimSize(0);
2322 if (bBatchSize != 1 && ShapedType::isDynamic(outShape[0]))
2323 outShape[0] = bBatchSize;
2324 outShape[2] = rhsShape.getDimSize(1);
2331LogicalResult MatMulTOp::verify() {
2334 const Type aElementType = aShape.getElementType();
2335 const Type bElementType = bShape.getElementType();
2345 FailureOr<int64_t> maybeAZp = getAZeroPoint();
2346 if (succeeded(maybeAZp) && verifyAZeroPoint(*maybeAZp).failed())
2349 FailureOr<int64_t> maybeBZp = getBZeroPoint();
2350 if (succeeded(maybeBZp) && verifyBZeroPoint(*maybeBZp).failed())
2354 int64_t N = ShapedType::kDynamic;
2355 int64_t D = ShapedType::kDynamic;
2356 int64_t H = ShapedType::kDynamic;
2360 if (aShape.hasRank()) {
2361 N = aShape.getDimSize(0);
2362 H = aShape.getDimSize(1);
2363 C = aShape.getDimSize(2);
2366 if (bShape.hasRank()) {
2367 D = bShape.getDimSize(0);
2368 W = bShape.getDimSize(1);
2375 if (ShapedType::isStatic(N) && ShapedType::isStatic(D) && N != D && D != 1)
2376 return emitOpError(
"expect B matrix batch size to be broadcast compatible "
2378 << D <<
" vs N=" << N;
2380 if (ShapedType::isDynamic(N) && ShapedType::isStatic(D) && D != 1)
2384 const auto outputType = cast<ShapedType>(getResult().
getType());
2385 if (outputType.hasRank() &&
2387 expectedOutputShape)))
2393LogicalResult tosa::MatmulTBlockScaledOp::inferReturnTypeComponents(
2394 MLIRContext *context, ::std::optional<Location> location,
2395 MatmulTBlockScaledOp::Adaptor adaptor,
2399 const auto aDataShape = cast<ShapedType>(adaptor.getAData().getType());
2400 if (aDataShape.hasRank()) {
2401 outShape[0] = aDataShape.getDimSize(0);
2402 outShape[1] = aDataShape.getDimSize(1);
2405 const auto aScaleShape = cast<ShapedType>(adaptor.getAScale().getType());
2406 if (aScaleShape.hasRank()) {
2407 outShape[0] = ShapedType::isDynamic(outShape[0]) ? aScaleShape.getDimSize(0)
2409 outShape[1] = ShapedType::isDynamic(outShape[1]) ? aScaleShape.getDimSize(1)
2414 const auto bDataShape = cast<ShapedType>(adaptor.getBData().getType());
2415 if (bDataShape.hasRank()) {
2416 const int64_t bDataBatchSize = bDataShape.getDimSize(0);
2417 if (bDataBatchSize != 1)
2419 ShapedType::isDynamic(outShape[0]) ? bDataBatchSize : outShape[0];
2420 outShape[2] = bDataShape.getDimSize(1);
2423 const auto bScaleShape = cast<ShapedType>(adaptor.getBScale().getType());
2424 if (bScaleShape.hasRank()) {
2425 const int64_t bScaleBatchSize = bScaleShape.getDimSize(0);
2426 if (bScaleBatchSize != 1)
2428 ShapedType::isDynamic(outShape[0]) ? bScaleBatchSize : outShape[0];
2429 outShape[2] = ShapedType::isDynamic(outShape[2]) ? bScaleShape.getDimSize(1)
2437LogicalResult MatmulTBlockScaledOp::verify() {
2439 const Type aDataType = getAData().getType();
2440 const Type bDataType = getBData().getType();
2446 int64_t N = ShapedType::kDynamic;
2447 int64_t D = ShapedType::kDynamic;
2448 int64_t H = ShapedType::kDynamic;
2451 int64_t multiplesOfC = ShapedType::kDynamic;
2463 "a_scale",
"batch")) ||
2465 "a_scale",
"height")))
2473 "b_data",
"batch")) ||
2475 "b_data",
"channels")))
2483 "b_scale",
"batch")) ||
2485 "b_scale",
"width")) ||
2493 if (ShapedType::isStatic(N) && ShapedType::isStatic(D) && N != D && D != 1)
2494 return emitOpError(
"expect B matrix batch size to be broadcast compatible "
2496 << D <<
" vs N=" << N;
2499 const uint32_t blockSize = BlockSizeAttr::getBlockSizeValue(
getBlockSize());
2500 if (blockSize != BlockSizeAttr::getBlockSizeValue(BlockSize::BLOCK_SIZE_32))
2501 return emitOpError(
"expect block size to be 32, got ") << blockSize;
2502 if (ShapedType::isStatic(C) && C % blockSize != 0)
2503 return emitOpError(
"expect C to be a multiple of block size, got C=")
2504 <<
C <<
", block_size=" << blockSize;
2507 if (ShapedType::isStatic(C) && ShapedType::isStatic(multiplesOfC) &&
2508 multiplesOfC != C / blockSize)
2510 "expect scale operands dimension 2 to equal C/block_size (")
2511 <<
C <<
"/" << blockSize <<
")" <<
", got " << multiplesOfC;
2514 N = ShapedType::isDynamic(N) ? D : N;
2516 const auto outputType = cast<ShapedType>(getResult().
getType());
2517 if (outputType.hasRank() &&
2522 opError <<
" to be compatible with expected output shape ";
2530LogicalResult tosa::PadOp::inferReturnTypeComponents(
2531 MLIRContext *context, ::std::optional<Location> location,
2532 PadOp::Adaptor adaptor,
2534 ShapeAdaptor inputShape(adaptor.getInput1().getType());
2536 cast<tosa::shapeType>(adaptor.getPadding().getType()).getRank();
2541 if (!inputShape.hasRank()) {
2542 outputShape.resize(paddingRank / 2, ShapedType::kDynamic);
2551 outputShape.resize(inputShape.getRank(), ShapedType::kDynamic);
2556 outputShape.reserve(inputShape.getRank());
2557 for (
int i = 0, s = inputShape.getRank(); i < s; i++) {
2558 if (inputShape.isDynamicDim(i)) {
2559 outputShape.push_back(ShapedType::kDynamic);
2562 auto padFront = paddingValues[i * 2];
2563 auto padBack = paddingValues[i * 2 + 1];
2564 if (padFront < 0 || padBack < 0) {
2566 outputShape.push_back(ShapedType::kDynamic);
2570 outputShape.push_back(inputShape.getDimSize(i) + padFront + padBack);
2577LogicalResult tosa::PadOp::verify() {
2584 if (
auto padConst = getPadConst()) {
2592 RankedTensorType inputType =
2593 llvm::dyn_cast<RankedTensorType>(getInput1().
getType());
2594 RankedTensorType outputType =
2595 llvm::dyn_cast<RankedTensorType>(getOutput().
getType());
2596 if (!inputType || !outputType)
2603 auto inputRank = inputType.getRank();
2608 auto paddingValues = paddingAttr.getValues<APInt>();
2609 if (paddingValues.size() !=
static_cast<size_t>(inputRank * 2))
2610 return emitOpError() <<
"padding tensor must have " << inputRank
2611 <<
" * 2 = " << inputRank * 2 <<
" elements, but got "
2612 << paddingValues.size();
2614 auto inputShape = inputType.getShape();
2615 auto outputShape = outputType.getShape();
2617 for (
int64_t i = 0; i < inputRank; ++i) {
2618 int64_t padStart = paddingValues[i * 2].getSExtValue();
2619 int64_t padEnd = paddingValues[i * 2 + 1].getSExtValue();
2621 if ((padStart < 0 && padStart != -1) || (padEnd < 0 && padEnd != -1)) {
2623 <<
"invalid padding values at dimension " << i
2624 <<
": values must be non-negative or -1 for dynamic padding, got ["
2625 << padStart <<
", " << padEnd <<
"]";
2629 if (inputShape[i] == ShapedType::kDynamic ||
2630 outputShape[i] == ShapedType::kDynamic)
2633 if (outputShape[i] != inputShape[i] + padStart + padEnd) {
2634 return emitOpError() <<
"mismatch in output shape at dimension " << i
2635 <<
": expected " << inputShape[i] <<
" + "
2636 << padStart <<
" + " << padEnd <<
" = "
2637 << (inputShape[i] + padStart + padEnd)
2638 <<
", but got " << outputShape[i];
2645LogicalResult tosa::SliceOp::inferReturnTypeComponents(
2646 MLIRContext *context, ::std::optional<Location> location,
2647 SliceOp::Adaptor adaptor,
2656 auto rank = cast<tosa::shapeType>(adaptor.getSize().getType()).getRank();
2664 ShapeAdaptor inputShape(adaptor.getInput1().getType());
2667 if (inputShape.hasRank()) {
2668 for (
size_t i = 0; i < size.size(); i++) {
2669 if (size[i] != 0 && size[i] >= -1 && start[i] >= 0 &&
2670 (ShapedType::isDynamic(inputShape.getDimSize(i)) ||
2671 start[i] < inputShape.getDimSize(i))) {
2673 if (ShapedType::isDynamic(inputShape.getDimSize(i))) {
2676 outputShape[i] = size[i];
2680 if (size[i] == -1) {
2681 outputShape[i] = inputShape.getDimSize(i) - start[i];
2682 }
else if (start[i] + size[i] <= inputShape.getDimSize(i)) {
2684 outputShape[i] = size[i];
2696LogicalResult tosa::SliceOp::verify() {
2697 const Value input = getInput1();
2698 const Value output = getOutput();
2704 const Value start = getStart();
2705 const Value size = getSize();
2709 if (inputShape.hasRank()) {
2710 const auto inputRank = inputShape.getRank();
2711 if (outputShape.hasRank() && inputRank != outputShape.getRank())
2713 "expect input1 and output to have the same ranks, got ")
2714 << inputRank <<
" and " << outputShape.getRank();
2716 const auto startShapeRank =
2717 llvm::cast<tosa::shapeType>(start.
getType()).getRank();
2718 if (inputRank != startShapeRank)
2719 return emitOpError(
"length of start is not equal to rank of input shape");
2721 const auto sizeShapeRank =
2722 llvm::cast<tosa::shapeType>(size.
getType()).getRank();
2723 if (inputRank != sizeShapeRank)
2724 return emitOpError(
"length of size is not equal to rank of input shape");
2729 if (startValues.size()) {
2730 if (llvm::any_of(startValues, [](
const int64_t v) {
2733 return emitOpError(
"start values must be non-negative, got [")
2734 << startValues <<
"]";
2741 if (llvm::any_of(sizeValues, [](
const int64_t v) {
2744 return emitOpError(
"size values must be > 0, got [") << sizeValues <<
"]";
2745 if (outputShape.hasRank()) {
2747 outputShape.getDims(outputDims);
2748 const bool hasNoInferableDims = llvm::all_of(
2750 if (hasNoInferableDims &&
2752 return emitOpError(
"expected output shape to match size values, got ")
2753 << output.
getType() <<
" vs [" << sizeValues <<
"]";
2756 if (inputShape.hasRank() && startValues.size()) {
2758 inputShape.getDims(inputDims);
2759 for (
const auto &[
index, vals] :
2760 llvm::enumerate(llvm::zip_equal(startValues, sizeValues, inputDims))) {
2761 const auto &[start, size, inputDim] = vals;
2763 ShapedType::isDynamic(inputDim))
2765 if (start + size > inputDim)
2766 return emitOpError(
"start + size must be less than or equal to input "
2767 "dimension size, got start=")
2768 << start <<
", size=" << size
2769 <<
" vs input dim size=" << inputDim <<
" at dimension "
2777LogicalResult tosa::MulOp::inferReturnTypeComponents(
2778 MLIRContext *context, ::std::optional<Location> location,
2793LogicalResult tosa::MulOp::verify() {
2794 const Value output = getOutput();
2799 if (
auto resIntType = dyn_cast<IntegerType>(resElemType)) {
2800 IntegerType lhsIntType =
2802 IntegerType rhsIntType =
2804 if (!lhsIntType || !rhsIntType || lhsIntType != rhsIntType)
2805 return emitOpError(
"requires the same element type for all operands");
2810 if (lhsIntType.getWidth() > resIntType.getWidth())
2811 return emitOpError(
"invalid data type size for operands or result");
2816 for (
int i = 0; i < 2; ++i) {
2819 "requires the same element type for all operands and results");
2823 ElementsAttr shiftElem;
2825 int32_t shift = shiftElem.getValues<IntegerAttr>()[0].getInt();
2827 return emitOpError() <<
"require shift to be 0 for float type";
2835 TypeRange operandTypes = getOperandTypes();
2836 ShapedType aType = cast<ShapedType>(operandTypes[0]);
2837 ShapedType bType = cast<ShapedType>(operandTypes[1]);
2839 const bool aHasRank = aType.hasRank();
2840 const bool bHasRank = bType.hasRank();
2842 bool hasExpectedOutputShape =
false;
2845 if (aHasRank && bHasRank) {
2846 const int64_t aRank = aType.getRank();
2847 const int64_t bRank = bType.getRank();
2849 return emitOpError(
"a and b operands don't have matching ranks, got ")
2850 << aRank <<
" and " << bRank;
2854 aType.getShape(), bType.getShape(), expectedOutputShape))
2855 return emitOpError(
"a and b operands don't have broadcast-compatible "
2857 << aType <<
" and " << bType;
2858 hasExpectedOutputShape =
true;
2861 ShapedType resultType = cast<ShapedType>(output.
getType());
2862 if (!resultType.hasRank())
2865 const int64_t resultRank = resultType.getRank();
2866 if (aHasRank && resultRank != aType.getRank())
2867 return emitOpError(
"result type has different rank than a, got ")
2868 << resultRank <<
" vs " << aType.getRank();
2869 if (bHasRank && resultRank != bType.getRank())
2870 return emitOpError(
"result type has different rank than b, got ")
2871 << resultRank <<
" vs " << bType.getRank();
2873 if (hasExpectedOutputShape &&
2875 expectedOutputShape)))
2881LogicalResult tosa::TableOp::inferReturnTypeComponents(
2882 MLIRContext *context, ::std::optional<Location> location,
2883 TableOp::Adaptor adaptor,
2885 ShapeAdaptor inputShape(adaptor.getInput1().getType());
2887 if (!inputShape.hasRank()) {
2892 inferredReturnShapes.resize(1);
2893 inputShape.getDims(inferredReturnShapes[0]);
2897LogicalResult tosa::TableOp::verify() {
2898 const TensorType inputType = getInput1().getType();
2899 const TensorType outputType = getOutput().getType();
2908 auto inputDims = inputType.
getShape();
2909 auto outputDims = outputType.
getShape();
2910 for (
auto it : llvm::enumerate(llvm::zip(inputDims, outputDims))) {
2912 auto [inputDim, outputDim] = it.value();
2913 if (ShapedType::isStatic(outputDim) && outputDim != inputDim) {
2914 return emitOpError() <<
"dim(result, " << dim <<
") = " << outputDim
2915 <<
" doesn't match dim(input, " << dim
2916 <<
") = " << inputDim;
2929 llvm::map_to_vector(multiplesAttr.getValues<APInt>(),
2930 [](
const APInt &val) { return val.getSExtValue(); });
2934LogicalResult tosa::TileOp::inferReturnTypeComponents(
2935 MLIRContext *context, ::std::optional<Location> location,
2936 TileOp::Adaptor adaptor,
2943 cast<tosa::shapeType>(adaptor.getMultiples().getType()).getRank();
2950 ShapeAdaptor inputShape(adaptor.getInput1().getType());
2952 if (!inputShape.hasRank()) {
2953 outputShape.resize(multiples.size(), ShapedType::kDynamic);
2954 inferredReturnShapes.push_back(
2958 if (
static_cast<size_t>(inputShape.getRank()) != multiples.size())
2962 outputShape.reserve(multiples.size());
2963 for (
int i = 0, s = inputShape.getRank(); i < s; i++) {
2964 if (multiples[i] == ShapedType::kDynamic) {
2965 outputShape.push_back(ShapedType::kDynamic);
2967 int64_t dim = inputShape.getDimSize(i);
2968 if (dim != ShapedType::kDynamic)
2969 dim *= multiples[i];
2970 outputShape.push_back(dim);
2978LogicalResult tosa::TileOp::verify() {
2984 ShapedType inputType = llvm::cast<ShapedType>(getInput1().
getType());
2985 ShapedType outputType = llvm::cast<ShapedType>(
getType());
2987 shapeType multiplesType =
2988 llvm::cast<tosa::shapeType>(getMultiples().
getType());
2990 auto multiplesRank = multiplesType.getRank();
2992 if (inputType.hasRank()) {
2993 if (inputType.getRank() != multiplesRank)
2994 return emitOpError(
"expect 'multiples' to have rank ")
2995 << inputType.getRank() <<
" but got " << multiplesRank <<
".";
2996 if (outputType.hasRank() &&
3000 }
else if (outputType.hasRank() && outputType.getRank() != multiplesRank)
3001 return emitOpError(
"expect 'multiples' array to have length ")
3002 << outputType.getRank() <<
" but got " << multiplesRank <<
".";
3005 if (getConstantMultiples(multiples).succeeded() &&
3006 llvm::any_of(multiples, [](
int64_t v) {
return v <= 0 && v != -1; }))
3008 "expect element of 'multiples' to be positive integer or -1.");
3014 if (l.size() != r.size() || l.size() != 1)
3019LogicalResult tosa::ReshapeOp::inferReturnTypeComponents(
3020 MLIRContext *context, ::std::optional<Location> location,
3021 ReshapeOp::Adaptor adaptor,
3023 ShapeAdaptor inputShape(adaptor.getInput1().getType());
3028 auto rank = cast<tosa::shapeType>(adaptor.getShape().getType()).getRank();
3037 if (!inputShape.hasRank() || !inputShape.hasStaticShape()) {
3038 inferredReturnShapes.push_back(
3046 int64_t numElements = inputShape.getNumElements();
3048 for (
auto val : newShapeValue) {
3049 if (ShapedType::isStatic(val)) {
3055 for (
auto &val : newShapeValue) {
3056 if (ShapedType::isDynamic(val))
3057 val = numElements / staticMul;
3060 inferredReturnShapes.push_back(
3065llvm::LogicalResult tosa::ReshapeOp::verify() {
3071 TensorType inputType = getInput1().getType();
3076 return mlir::success();
3080 if (missingDims > 1)
3081 return emitOpError() <<
"expected at most one target dimension to be "
3084 const auto outputType = dyn_cast<RankedTensorType>(
getType());
3088 if ((
int64_t)shapeValues.size() != outputType.getRank())
3089 return emitOpError() <<
"new shape does not match result rank";
3091 for (
auto [newShapeDim, outputShapeDim] :
3092 zip(shapeValues, outputType.getShape())) {
3094 newShapeDim != ShapedType::kDynamic &&
3095 outputShapeDim != ShapedType::kDynamic && newShapeDim != outputShapeDim)
3096 return emitOpError() <<
"new shape is inconsistent with result shape";
3099 return emitOpError() <<
"new shape has invalid tensor dimension size "
3103 if (inputType.hasStaticShape()) {
3104 int64_t inputElementsNum = inputType.getNumElements();
3105 if (outputType.hasStaticShape()) {
3106 int64_t outputElementsNum = outputType.getNumElements();
3107 if (inputElementsNum != outputElementsNum) {
3108 return emitOpError() <<
"cannot reshape " << inputElementsNum
3109 <<
" elements into " << outputElementsNum;
3115 return (dim > 0) ?
acc * dim :
acc;
3117 bool isStaticNewShape =
3118 llvm::all_of(shapeValues, [](
int64_t s) {
return s > 0; });
3119 if ((isStaticNewShape && inputElementsNum != newShapeElementsNum) ||
3120 (!isStaticNewShape && newShapeElementsNum > inputElementsNum)) {
3121 return emitOpError() <<
"cannot reshape " << inputElementsNum
3122 <<
" elements into " << newShapeElementsNum;
3126 return mlir::success();
3129bool tosa::ReshapeBlockScaledOp::isCompatibleReturnTypes(
TypeRange l,
3131 if (l.size() != r.size() || l.size() < 1 || l.size() > 2)
3139LogicalResult tosa::ReshapeBlockScaledOp::inferReturnTypeComponents(
3140 MLIRContext *context, ::std::optional<Location> location,
3141 ReshapeBlockScaledOp::Adaptor adaptor,
3144 const auto numInputs = adaptor.getInput().size();
3145 ShapeAdaptor inputShape(adaptor.getInput()[0].getType());
3148 const auto newShape = adaptor.getNewValueShape();
3150 auto rank = cast<tosa::shapeType>(newShape.getType()).getRank();
3159 const uint32_t blockSize =
3160 BlockSizeAttr::getBlockSizeValue(adaptor.getBlockSize());
3163 if (numInputs == 2) {
3164 newScaleShapeValue.assign(newShapeValue.begin(), newShapeValue.end());
3165 if (ShapedType::isStatic(newScaleShapeValue.back()))
3166 newScaleShapeValue.back() /= blockSize;
3169 inferredReturnShapes.push_back(
3171 if (numInputs == 2) {
3173 for (
size_t idx = 0; idx < newShapeValue.size(); idx++) {
3174 if (ShapedType::isDynamic(newScaleShapeValue[idx])) {
3175 newScaleShapeValue[idx] = newShapeValue[idx];
3176 if (idx == (newShapeValue.size() - 1))
3177 newScaleShapeValue[idx] /= blockSize;
3188llvm::LogicalResult tosa::ReshapeBlockScaledOp::verify() {
3192 if (inputList.size() == 0)
3193 return emitOpError(
"requires at least one input");
3195 if (inputList.size() > 2)
3196 return emitOpError(
"requires at most two inputs");
3198 if (inputList.size() != outputList.size())
3199 return emitOpError(
"requires number of results to match inputs");
3207 const auto inputType = llvm::cast<ShapedType>(inputList[0].
getType());
3208 if (!inputType.hasRank())
3210 const uint32_t blockSize = BlockSizeAttr::getBlockSizeValue(
getBlockSize());
3212 if (inputList.size() == 2) {
3213 if (blockSize != BlockSizeAttr::getBlockSizeValue(BlockSize::BLOCK_SIZE_32))
3214 return emitOpError(
"expect block size to be 32, got ") << blockSize;
3215 if (llvm::any_of(inputList, [](
Value v) {
3216 const auto input = cast<ShapedType>(v.
getType());
3217 return input.hasRank() && input.getRank() == 0;
3220 "requires all input shapes have a rank greater than 0");
3221 if (llvm::any_of(outputList, [](
Value v) {
3222 const auto output = cast<ShapedType>(v.
getType());
3223 return output.hasRank() && output.getRank() == 0;
3226 "requires all result shapes have a rank greater than 0");
3234 const auto inputScaleType = llvm::cast<ShapedType>(inputList[1].
getType());
3235 if (inputScaleType.hasRank()) {
3236 if (inputType.getRank() != inputScaleType.getRank())
3237 return emitOpError(
"input shapes do not have same rank");
3240 for (
auto dimIdx = 0; dimIdx < inputType.getRank() - 1; dimIdx++) {
3241 const int64_t inputValueDim = inputType.getDimSize(dimIdx);
3242 const int64_t inputScaleDim = inputScaleType.getShape()[dimIdx];
3243 if (ShapedType::isStatic(inputValueDim) &&
3244 ShapedType::isStatic(inputScaleDim) &&
3245 inputValueDim != inputScaleDim)
3246 return emitOpError(
"input shapes for data and scale do not match on "
3253 inputType.getDimSize(inputType.getRank() - 1);
3254 if (ShapedType::isStatic(lastValueDim)) {
3255 if (lastValueDim % blockSize != 0)
3256 return emitOpError(
"expect last dimension of input_data (")
3257 << lastValueDim <<
") to be divisible by block_size ("
3258 << blockSize <<
")";
3261 inputScaleType.getDimSize(inputScaleType.getRank() - 1);
3263 if (ShapedType::isStatic(lastScaleDim) &&
3264 lastScaleDim != lastValueDim / blockSize)
3265 return emitOpError(
"expect last dimension of scale_data (")
3266 << lastScaleDim <<
") to be " << lastValueDim <<
"/"
3271 if (blockSize != BlockSizeAttr::getBlockSizeValue(BlockSize::BLOCK_SIZE_1))
3272 return emitOpError(
"expect block size to be 1, got ") << blockSize;
3280 return mlir::success();
3283 if (inputList.size() == 2) {
3284 if (
static_cast<int64_t>(shapeValues.size()) == 0)
3285 return emitOpError(
"requires new shape to have a rank greater than 0");
3287 const int64_t lastShapeDim = shapeValues.back();
3288 if (ShapedType::isStatic(lastShapeDim) && lastShapeDim % blockSize != 0)
3289 return emitOpError(
"expect last dimension of new shape (")
3290 << lastShapeDim <<
") to be divisible by block_size (" << blockSize
3294 const auto outputType = llvm::cast<ShapedType>(outputList[0].
getType());
3295 if (!outputType.hasRank())
3298 if (
static_cast<int64_t>(shapeValues.size()) != outputType.getRank())
3299 return emitOpError() <<
"result does not match new shape rank";
3301 for (
auto [newShapeDim, outputShapeDim] :
3302 zip(shapeValues, outputType.getShape())) {
3303 if (ShapedType::isStatic(newShapeDim) &&
3304 ShapedType::isStatic(outputShapeDim) && newShapeDim != outputShapeDim)
3305 return emitOpError() <<
"result shape is inconsistent with new shape";
3308 if (outputList.size() == 2) {
3312 scaleShapeValues.back() /= blockSize;
3314 const auto outputScaleType =
3315 llvm::cast<ShapedType>(outputList[1].
getType());
3316 if (outputScaleType.hasRank()) {
3317 if ((
int64_t)scaleShapeValues.size() != outputScaleType.getRank())
3318 return emitOpError() <<
"result scale does not match new shape rank";
3320 for (
auto [newScaleShapeDim, outputScaleShapeDim] :
3321 zip(scaleShapeValues, outputScaleType.getShape())) {
3322 if (ShapedType::isStatic(newScaleShapeDim) &&
3323 ShapedType::isStatic(outputScaleShapeDim) &&
3324 newScaleShapeDim != outputScaleShapeDim)
3326 <<
"result scale shape is inconsistent with new shape";
3331 if (inputType.hasStaticShape()) {
3332 int64_t inputElementsNum = inputType.getNumElements();
3333 if (outputType.hasStaticShape()) {
3334 int64_t outputElementsNum = outputType.getNumElements();
3335 if (inputElementsNum != outputElementsNum) {
3336 return emitOpError() <<
"cannot reshape " << inputElementsNum
3337 <<
" elements into " << outputElementsNum;
3343 return (dim > 0) ?
acc * dim :
acc;
3345 bool isStaticNewShape =
3346 llvm::all_of(shapeValues, [](
int64_t s) {
return s > 0; });
3347 if ((isStaticNewShape && inputElementsNum != newShapeElementsNum) ||
3348 (!isStaticNewShape && newShapeElementsNum > inputElementsNum)) {
3349 return emitOpError() <<
"cannot reshape " << inputElementsNum
3350 <<
" elements into " << newShapeElementsNum;
3354 return mlir::success();
3361 ElementsAttr zpAttr;
3366 Type zpElemType = zpAttr.getElementType();
3368 if (llvm::isa<FloatType>(zpElemType)) {
3369 if (zpAttr.getValues<APFloat>()[0].isZero()) {
3376 if (llvm::isa<IntegerType>(zpElemType)) {
3378 return zpAttr.getValues<APInt>()[0].getSExtValue();
3379 return zpAttr.getValues<APInt>()[0].getZExtValue();
3386template <
typename T>
3388 const std::string &operand) {
3391 if (!zpElemType.
isInteger(8) && zp != 0) {
3393 std::string lower = operand;
3394 llvm::transform(lower, lower.begin(), ::tolower);
3395 return op.emitOpError()
3396 << lower <<
" zero point must be zero for non-int8 integer types";
3404 const std::string &operand) {
3405 bool isInputZp = (operand ==
"Input");
3407 bool tensorUnsigned =
3408 isInputZp ? op.getInputUnsigned() : op.getOutputUnsigned();
3409 StringRef tensorName = isInputZp ?
"input" :
"output";
3415 !(zpElemType.
isInteger(16) && tensorUnsigned)) {
3416 return op.emitOpError()
3417 <<
"expect " << tensorName <<
"_zp of 0, got " << zp;
3419 if (zpElemType.
isInteger(16) && tensorUnsigned && zp != 32768) {
3420 return op.emitOpError() <<
"expect " << tensorName
3421 <<
"_zp of 0 or 32768 for unsigned int16 "
3422 << tensorName <<
", got " << zp;
3429#define ZERO_POINT_HELPER(OP, OPERAND_NAME, SIGN_EXTEND) \
3430 FailureOr<int64_t> tosa::OP::get##OPERAND_NAME##ZeroPoint() { \
3431 return getZeroPoint(get##OPERAND_NAME##Zp(), SIGN_EXTEND); \
3433 LogicalResult tosa::OP::verify##OPERAND_NAME##ZeroPoint(int64_t zp) { \
3434 return verifyZeroPoint(*this, get##OPERAND_NAME##Zp(), zp, #OPERAND_NAME); \
3457#undef ZERO_POINT_HELPER
3459LogicalResult tosa::TransposeOp::inferReturnTypeComponents(
3460 MLIRContext *context, ::std::optional<Location> location,
3461 TransposeOp::Adaptor adaptor,
3463 ShapeAdaptor inputShape(adaptor.getInput1().getType());
3472 const auto inputRank = inputShape.
getRank();
3476 if (adaptor.getPerms().size() !=
static_cast<size_t>(inputRank)) {
3482 if (inputRank == 0) {
3488 bool allTheSame =
true;
3489 for (
int i = 1, s = inputRank; i < s; i++) {
3499 outputShape.resize(inputRank, inputShape.
getDimSize(0));
3504 outputShape.resize(inputRank, ShapedType::kDynamic);
3507 if (llvm::any_of(adaptor.getPerms(),
3508 [inputRank](
const auto i) { return i >= inputRank; }))
3511 outputShape.reserve(inputRank);
3512 for (
int i = 0, s = inputRank; i < s; i++) {
3513 outputShape[i] = inputShape.
getDimSize(adaptor.getPerms()[i]);
3520LogicalResult tosa::TransposeOp::verify() {
3532 if (inputShape.hasRank() &&
3533 constantPerms.size() !=
static_cast<size_t>(inputShape.getRank()))
3534 return emitOpError() <<
"expected perms attribute to have size "
3535 << inputShape.getRank()
3536 <<
" (input rank) but got size "
3537 << constantPerms.size();
3539 if (inputShape.hasRank() && outputShape.hasRank() &&
3540 inputShape.getRank() != outputShape.getRank())
3542 <<
"expected input tensor rank to equal result tensor rank";
3544 if (outputShape.hasRank() &&
3545 constantPerms.size() !=
static_cast<size_t>(outputShape.getRank()))
3546 return emitOpError() <<
"expected perms attribute to have size "
3547 << outputShape.getRank()
3548 <<
" (output rank) but got size "
3549 << constantPerms.size();
3551 if (!llvm::all_of(constantPerms,
3552 [&constantPerms](int32_t s) {
3554 static_cast<size_t>(s) < constantPerms.size();
3557 constantPerms, [](int32_t v) ->
int64_t {
return v; })))
3558 return emitOpError() <<
"expected valid permutation indices";
3561 if (inputShape.hasStaticShape() && outputShape.hasStaticShape() &&
3562 inputShape.getNumElements() != outputShape.getNumElements())
3563 return emitOpError() <<
"expected input1 and output to have same numbers "
3565 << inputShape.getNumElements() <<
" and "
3566 << outputShape.getNumElements();
3570 if (inputShape.hasRank() && outputShape.hasRank()) {
3571 for (
auto i = 0; i < outputShape.getRank(); i++) {
3572 if (inputShape.isDynamicDim(constantPerms[i]) ||
3573 outputShape.isDynamicDim(i))
3576 if (inputShape.getDimSize(constantPerms[i]) != outputShape.getDimSize(i))
3578 <<
"expected output tensor dim " << i <<
" to match "
3579 <<
"input dim " << constantPerms[i] <<
" with value of "
3580 << inputShape.getDimSize(constantPerms[i]);
3587LogicalResult TransposeOp::reifyResultShapes(
3590 const llvm::ArrayRef<int32_t> transposePerms = getPerms();
3592 Value input = getInput1();
3593 auto inputType = cast<TensorType>(input.
getType());
3595 SmallVector<OpFoldResult> returnedDims(inputType.getRank());
3596 for (
auto dim : transposePerms) {
3597 int32_t dimInInput = transposePerms[dim];
3598 if (inputType.isDynamicDim(dimInInput))
3600 tensor::DimOp::create(builder, getLoc(), input, dimInInput)
3604 builder.
getIndexAttr(inputType.getDimSize(dimInInput));
3607 reifiedReturnShapes.emplace_back(std::move(returnedDims));
3611LogicalResult tosa::GatherOp::inferReturnTypeComponents(
3612 MLIRContext *context, ::std::optional<Location> location,
3613 GatherOp::Adaptor adaptor,
3614 SmallVectorImpl<ShapedTypeComponents> &inferredReturnShapes) {
3615 llvm::SmallVector<int64_t> outputShape;
3616 outputShape.resize(3, ShapedType::kDynamic);
3618 ShapeAdaptor valuesShape(adaptor.getValues().getType());
3619 if (valuesShape.hasRank()) {
3620 outputShape[0] = valuesShape.getDimSize(0);
3621 outputShape[2] = valuesShape.getDimSize(2);
3624 ShapeAdaptor indicesShape(adaptor.getIndices().getType());
3625 if (indicesShape.hasRank()) {
3626 if (outputShape[0] == ShapedType::kDynamic)
3627 outputShape[0] = indicesShape.getDimSize(0);
3628 if (outputShape[1] == ShapedType::kDynamic)
3629 outputShape[1] = indicesShape.getDimSize(1);
3632 inferredReturnShapes.push_back(ShapedTypeComponents(outputShape));
3636LogicalResult tosa::RowGatherOp::inferReturnTypeComponents(
3637 MLIRContext *context, ::std::optional<Location> location,
3638 RowGatherOp::Adaptor adaptor,
3639 SmallVectorImpl<ShapedTypeComponents> &inferredReturnShapes) {
3640 llvm::SmallVector<int64_t> outputShape;
3641 outputShape.resize(3, ShapedType::kDynamic);
3643 const ShapeAdaptor valuesShape(adaptor.getValues().getType());
3644 if (valuesShape.hasRank()) {
3645 outputShape[0] = valuesShape.getDimSize(0);
3646 outputShape[2] = valuesShape.getDimSize(2);
3649 const ShapeAdaptor indicesShape(adaptor.getIndices().getType());
3650 if (indicesShape.hasRank()) {
3651 if (outputShape[0] == ShapedType::kDynamic)
3652 outputShape[0] = indicesShape.getDimSize(0);
3654 const FailureOr<int32_t> maybeRowCount =
3656 if (succeeded(maybeRowCount)) {
3657 const int64_t indicesW = indicesShape.getDimSize(1);
3658 if (ShapedType::isStatic(indicesW))
3659 outputShape[1] = indicesW * maybeRowCount.value();
3663 inferredReturnShapes.push_back(ShapedTypeComponents(outputShape));
3667LogicalResult tosa::RowGatherBlockScaledOp::inferReturnTypeComponents(
3668 MLIRContext *context, ::std::optional<Location> location,
3669 RowGatherBlockScaledOp::Adaptor adaptor,
3670 SmallVectorImpl<ShapedTypeComponents> &inferredReturnShapes) {
3671 const auto values = adaptor.getValues();
3675 SmallVector<int64_t> dataShape(3, ShapedType::kDynamic);
3676 const ShapeAdaptor valuesShape(values.front().getType());
3677 if (valuesShape.hasRank()) {
3678 dataShape[0] = valuesShape.getDimSize(0);
3679 dataShape[2] = valuesShape.getDimSize(2);
3682 const ShapeAdaptor indicesShape(adaptor.getIndices().getType());
3683 if (indicesShape.hasRank()) {
3684 if (dataShape[0] == ShapedType::kDynamic)
3685 dataShape[0] = indicesShape.getDimSize(0);
3689 succeeded(rowCount) && rowCount.value() > 0) {
3690 const int64_t indicesW = indicesShape.getDimSize(1);
3691 if (ShapedType::isStatic(indicesW))
3692 dataShape[1] = indicesW * rowCount.value();
3696 inferredReturnShapes.push_back(ShapedTypeComponents(dataShape));
3697 if (values.size() == 1)
3700 SmallVector<int64_t> scaleShape = dataShape;
3701 const uint32_t blockSize =
3702 BlockSizeAttr::getBlockSizeValue(adaptor.getBlockSize());
3703 if (ShapedType::isStatic(dataShape[2]))
3704 scaleShape[2] = dataShape[2] / blockSize;
3706 inferredReturnShapes.push_back(ShapedTypeComponents(scaleShape));
3710LogicalResult tosa::GatherOp::verify() {
3717 const ShapeAdaptor valuesShape(getValues().
getType());
3719 const ShapeAdaptor outputShape(getOutput().
getType());
3721 int64_t n = ShapedType::kDynamic;
3722 int64_t w = ShapedType::kDynamic;
3723 int64_t c = ShapedType::kDynamic;
3725 if (valuesShape.hasRank()) {
3726 n = valuesShape.getDimSize(0);
3727 c = valuesShape.getDimSize(2);
3729 if (indicesShape.hasRank()) {
3730 const int64_t indicesN = indicesShape.getDimSize(0);
3731 w = indicesShape.getDimSize(1);
3732 if (n == ShapedType::kDynamic)
3734 else if (indicesN != ShapedType::kDynamic && n != indicesN)
3735 return emitOpError() <<
"requires indices dimension 0 to have size " << n
3736 <<
", got " << indicesN;
3738 if (outputShape.hasRank()) {
3739 const int64_t outputN = outputShape.getDimSize(0);
3740 const int64_t outputW = outputShape.getDimSize(1);
3741 const int64_t outputC = outputShape.getDimSize(2);
3742 if (n != ShapedType::kDynamic && outputN != ShapedType::kDynamic &&
3744 return emitOpError() <<
"requires output dimension 0 to have size " << n
3745 <<
", got " << outputN;
3747 if (w != ShapedType::kDynamic && outputW != ShapedType::kDynamic &&
3749 return emitOpError() <<
"requires output dimension 1 to have size " << w
3750 <<
", got " << outputW;
3751 if (c != ShapedType::kDynamic && outputC != ShapedType::kDynamic &&
3753 return emitOpError() <<
"requires output dimension 2 to have size " << c
3754 <<
", got " << outputC;
3759LogicalResult tosa::RowGatherOp::verify() {
3764 const FailureOr<int32_t> maybeRowCount =
3766 if (succeeded(maybeRowCount) && maybeRowCount.value() <= 0)
3767 return emitOpError() <<
"requires row_count to be > 0, got "
3768 << maybeRowCount.value();
3770 int64_t n = ShapedType::kDynamic;
3771 int64_t c = ShapedType::kDynamic;
3772 int64_t w = ShapedType::kDynamic;
3774 const ShapeAdaptor valuesShape(getValues().
getType());
3775 if (valuesShape.hasRank()) {
3776 n = valuesShape.getDimSize(0);
3777 c = valuesShape.getDimSize(2);
3781 if (indicesShape.hasRank()) {
3783 "indices",
"batch")))
3785 w = indicesShape.getDimSize(1);
3788 const ShapeAdaptor outputShape(getOutput().
getType());
3789 if (outputShape.hasRank()) {
3791 "output",
"batch")) ||
3793 "output",
"channels")))
3796 if (succeeded(maybeRowCount) && maybeRowCount.value() > 0 &&
3797 ShapedType::isStatic(w)) {
3798 const int64_t expectedOutputRows = w * maybeRowCount.value();
3799 if (ShapedType::isStatic(outputShape.getDimSize(1)) &&
3800 outputShape.getDimSize(1) != expectedOutputRows)
3802 <<
"requires output dimension to be equal to "
3803 "indices[1]*row_count ("
3804 << expectedOutputRows <<
"), got " << outputShape.getDimSize(1);
3811LogicalResult tosa::RowGatherBlockScaledOp::verify() {
3812 const OperandRange values = getValues();
3813 const ResultRange output = getOutput();
3814 if (values.empty() || values.size() > 2)
3816 <<
"expects values tensor list length to be 1 or 2, got "
3818 if (output.size() != values.size())
3820 <<
"expects output tensor list length to match values tensor list "
3822 << output.size() <<
" results for " << values.size()
3823 <<
" input tensors";
3825 const uint32_t blockSize = BlockSizeAttr::getBlockSizeValue(
getBlockSize());
3826 if (values.size() == 1 && blockSize != 1)
3828 <<
"requires block_size to be BLOCK_SIZE_1 when values tensor list "
3830 if (values.size() == 2 && blockSize == 1)
3832 <<
"requires block_size to not be BLOCK_SIZE_1 when values tensor "
3836 output[0].
getType(),
"values[0]",
3841 "values[1]",
"output[1]")))
3845 succeeded(rowCount) && rowCount.value() <= 0)
3846 return emitOpError() <<
"requires row_count to be > 0, got "
3847 << rowCount.value();
3849 int64_t n = ShapedType::kDynamic;
3850 int64_t k = ShapedType::kDynamic;
3851 int64_t c = ShapedType::kDynamic;
3852 int64_t w = ShapedType::kDynamic;
3853 int64_t multiplesOfC = ShapedType::kDynamic;
3855 const ShapeAdaptor valuesDataShape(values[0].
getType());
3856 if (valuesDataShape.hasRank()) {
3857 n = valuesDataShape.getDimSize(0);
3858 k = valuesDataShape.getDimSize(1);
3859 c = valuesDataShape.getDimSize(2);
3862 if (ShapedType::isStatic(c) && c % blockSize != 0)
3863 return emitOpError() <<
"expects channels of values[0] (" << c
3864 <<
") to be divisible by block_size (" << blockSize
3868 if (indicesShape.hasRank()) {
3870 "indices",
"batch")))
3872 w = indicesShape.getDimSize(1);
3875 const ShapeAdaptor outputDataShape(output[0].
getType());
3876 if (outputDataShape.hasRank()) {
3878 "output[0]",
"batch")) ||
3880 "output[0]",
"channels")))
3884 succeeded(rowCount) && rowCount.value() > 0 &&
3885 ShapedType::isStatic(w)) {
3886 const int64_t expectedOutputRows = w * rowCount.value();
3887 if (ShapedType::isStatic(outputDataShape.getDimSize(1)) &&
3888 outputDataShape.getDimSize(1) != expectedOutputRows)
3889 return emitOpError() <<
"requires output[0] dimension 1 to have size "
3890 << expectedOutputRows <<
", got "
3891 << outputDataShape.getDimSize(1);
3895 if (values.size() == 2) {
3896 const ShapeAdaptor valuesScaleShape(values[1].
getType());
3897 if (valuesScaleShape.hasRank()) {
3899 "values[1]",
"batch")) ||
3901 "values[1]",
"rows")))
3903 multiplesOfC = valuesScaleShape.getDimSize(2);
3906 const ShapeAdaptor outputScaleShape(output[1].
getType());
3907 if (outputScaleShape.hasRank()) {
3909 "output[1]",
"batch")))
3913 succeeded(rowCount) && rowCount.value() > 0 &&
3914 ShapedType::isStatic(w)) {
3915 const int64_t expectedOutputRows = w * rowCount.value();
3916 if (ShapedType::isStatic(outputScaleShape.getDimSize(1)) &&
3917 outputScaleShape.getDimSize(1) != expectedOutputRows)
3918 return emitOpError() <<
"requires output[1] dimension 1 to have size "
3919 << expectedOutputRows <<
", got "
3920 << outputScaleShape.getDimSize(1);
3923 if (ShapedType::isDynamic(multiplesOfC))
3924 multiplesOfC = outputScaleShape.getDimSize(2);
3925 else if (ShapedType::isStatic(outputScaleShape.getDimSize(2)) &&
3926 multiplesOfC != outputScaleShape.getDimSize(2))
3928 <<
"expected channels of output[1] to match size "
3929 << multiplesOfC <<
", got " << outputScaleShape.getDimSize(2);
3932 if (ShapedType::isStatic(c) && ShapedType::isStatic(multiplesOfC) &&
3933 multiplesOfC != c / blockSize)
3935 <<
"expects channels of scale tensors to equal C/block_size (" << c
3936 <<
"/" << blockSize <<
"), got " << multiplesOfC;
3942LogicalResult tosa::ResizeOp::inferReturnTypeComponents(
3943 MLIRContext *context, ::std::optional<Location> location,
3944 ResizeOp::Adaptor adaptor,
3945 SmallVectorImpl<ShapedTypeComponents> &inferredReturnShapes) {
3946 llvm::SmallVector<int64_t, 4> outputShape;
3947 outputShape.resize(4, ShapedType::kDynamic);
3949 ShapeAdaptor inputShape(adaptor.getInput().getType());
3950 if (!inputShape.hasRank())
3953 outputShape[0] = inputShape.getDimSize(0);
3954 outputShape[3] = inputShape.getDimSize(3);
3955 int64_t inputHeight = inputShape.getDimSize(1);
3956 int64_t inputWidth = inputShape.getDimSize(2);
3958 if ((inputHeight == ShapedType::kDynamic) ||
3959 (inputWidth == ShapedType::kDynamic))
3962 SmallVector<int64_t> scaleInt, offsetInt, borderInt;
3973 const int64_t outputHeight =
3974 (((inputHeight - 1) * scaleInt[0] - offsetInt[0] + borderInt[0]) /
3978 const int64_t outputWidth =
3979 (((inputWidth - 1) * scaleInt[2] - offsetInt[1] + borderInt[1]) /
3983 if (outputHeight < 0 || outputWidth < 0) {
3986 "calculated output height and width must be non-negative, "
3988 outputHeight,
", width = ", outputWidth);
3991 outputShape[1] = outputHeight;
3992 outputShape[2] = outputWidth;
3993 inferredReturnShapes.push_back(ShapedTypeComponents(outputShape));
3997LogicalResult tosa::ResizeOp::verify() {
3998 const Value input = getInput();
3999 const Value output = getOutput();
4000 const RankedTensorType inputType =
4001 llvm::dyn_cast<RankedTensorType>(input.
getType());
4002 const RankedTensorType outputType =
4003 llvm::dyn_cast<RankedTensorType>(output.
getType());
4005 SmallVector<int64_t> scaleValues;
4006 SmallVector<int64_t> offsetValues;
4007 SmallVector<int64_t> borderValues;
4015 if (llvm::any_of(scaleValues, [](int64_t s) {
return s <= 0; }))
4016 return emitOpError(
"expect all scale values to be > 0, got ")
4019 const int64_t scaleYN = scaleValues[0];
4020 const int64_t scaleYD = scaleValues[1];
4021 const int64_t scaleXN = scaleValues[2];
4022 const int64_t scaleXD = scaleValues[3];
4024 const int64_t offsetY = offsetValues[0];
4025 const int64_t offsetX = offsetValues[1];
4027 const int64_t borderY = borderValues[0];
4028 const int64_t borderX = borderValues[1];
4035 const int64_t oh = outputType.getDimSize(1);
4036 const int64_t ow = outputType.getDimSize(2);
4037 const int64_t ih = inputType.getDimSize(1);
4038 const int64_t iw = inputType.getDimSize(2);
4044 if (ih != ShapedType::kDynamic && ih != 1) {
4045 const std::optional<int64_t> calculatedOutHeightMinusOne =
4046 idivCheck((ih - 1) * scaleYN - offsetY + borderY, scaleYD);
4047 if (!calculatedOutHeightMinusOne.has_value())
4048 return emitOpError(
"expected (input_height - 1) * scale_y_n - offset_y + "
4050 <<
"to be wholly divisible by scale_y_d, got ((" << ih
4051 <<
" - 1) * " << scaleYN <<
" - " << offsetY <<
" + " << borderY
4052 <<
") / " << scaleYD;
4053 const int64_t calculatedOutHeight = calculatedOutHeightMinusOne.value() + 1;
4054 if (oh != ShapedType::kDynamic && calculatedOutHeight != oh)
4055 return emitOpError(
"calculated output height did not match expected: ")
4056 <<
"calculated=" << calculatedOutHeight <<
", expected=" << oh;
4063 if (iw != ShapedType::kDynamic && iw != 1) {
4064 const int64_t scaledInWidth = (iw - 1) * scaleXN - offsetX + borderX;
4065 const std::optional<int64_t> calculatedOutWidthMinusOne =
4067 if (!calculatedOutWidthMinusOne.has_value())
4068 return emitOpError(
"expected (input_width - 1) * scale_x_n - offset_x + "
4070 <<
"to be wholly divisible by scale_x_d, got ((" << iw
4071 <<
" - 1) * " << scaleXN <<
" - " << offsetX <<
" + " << borderX
4072 <<
") / " << scaleXD;
4073 const int64_t calculatedOutWidth = calculatedOutWidthMinusOne.value() + 1;
4074 if (ow != ShapedType::kDynamic && calculatedOutWidth != ow)
4075 return emitOpError(
"calculated output width did not match expected: ")
4076 <<
"calculated=" << calculatedOutWidth <<
", expected=" << ow;
4082LogicalResult tosa::ScatterOp::inferReturnTypeComponents(
4083 MLIRContext *context, ::std::optional<Location> location,
4084 ScatterOp::Adaptor adaptor,
4085 SmallVectorImpl<ShapedTypeComponents> &inferredReturnShapes) {
4086 llvm::SmallVector<int64_t> outputShape;
4087 outputShape.resize(3, ShapedType::kDynamic);
4089 ShapeAdaptor valuesInShape(adaptor.getValuesIn().getType());
4090 if (valuesInShape.hasRank()) {
4091 outputShape[0] = valuesInShape.getDimSize(0);
4092 outputShape[1] = valuesInShape.getDimSize(1);
4093 outputShape[2] = valuesInShape.getDimSize(2);
4096 ShapeAdaptor indicesShape(adaptor.getIndices().getType());
4097 if (indicesShape.hasRank()) {
4098 if (outputShape[0] == ShapedType::kDynamic)
4099 outputShape[0] = indicesShape.getDimSize(0);
4102 ShapeAdaptor inputShape(adaptor.getInput().getType());
4103 if (inputShape.hasRank()) {
4104 if (outputShape[0] == ShapedType::kDynamic)
4105 outputShape[0] = inputShape.getDimSize(0);
4106 if (outputShape[2] == ShapedType::kDynamic)
4107 outputShape[2] = inputShape.getDimSize(2);
4110 inferredReturnShapes.push_back(ShapedTypeComponents(outputShape));
4114LogicalResult tosa::ScatterOp::verify() {
4124 const ShapeAdaptor valuesInShape(getValuesIn().
getType());
4126 const ShapeAdaptor inputShape(getInput().
getType());
4127 const ShapeAdaptor outputShape(getValuesOut().
getType());
4129 int64_t n = ShapedType::kDynamic;
4130 int64_t k = ShapedType::kDynamic;
4131 int64_t w = ShapedType::kDynamic;
4132 int64_t c = ShapedType::kDynamic;
4133 if (valuesInShape.hasRank()) {
4134 n = valuesInShape.getDimSize(0);
4135 k = valuesInShape.getDimSize(1);
4136 c = valuesInShape.getDimSize(2);
4138 if (indicesShape.hasRank()) {
4139 const int64_t indicesN = indicesShape.getDimSize(0);
4140 w = indicesShape.getDimSize(1);
4141 if (n == ShapedType::kDynamic)
4143 else if (indicesN != ShapedType::kDynamic && n != indicesN)
4144 return emitOpError() <<
"requires indices dimension 0 to have size " << n
4145 <<
", got " << indicesN;
4147 if (inputShape.hasRank()) {
4148 const int64_t inputN = inputShape.getDimSize(0);
4149 const int64_t inputW = inputShape.getDimSize(1);
4150 const int64_t inputC = inputShape.getDimSize(2);
4151 if (n == ShapedType::kDynamic)
4153 else if (inputN != ShapedType::kDynamic && n != inputN)
4154 return emitOpError() <<
"requires input dimension 0 to have size " << n
4155 <<
", got " << inputN;
4156 if (w == ShapedType::kDynamic)
4158 else if (inputW != ShapedType::kDynamic && w != inputW)
4159 return emitOpError() <<
"requires input dimension 1 to have size " << w
4160 <<
", got " << inputW;
4162 if (c == ShapedType::kDynamic)
4164 else if (inputC != ShapedType::kDynamic && c != inputC)
4165 return emitOpError() <<
"requires input dimension 2 to have size " << c
4166 <<
", got " << inputC;
4168 if (outputShape.hasRank()) {
4169 const int64_t outputN = outputShape.getDimSize(0);
4170 const int64_t outputK = outputShape.getDimSize(1);
4171 const int64_t outputC = outputShape.getDimSize(2);
4172 if (n != ShapedType::kDynamic && outputN != ShapedType::kDynamic &&
4174 return emitOpError() <<
"requires values_out dimension 0 to have size "
4175 << n <<
", got " << outputN;
4176 if (k == ShapedType::kDynamic)
4178 else if (outputK != ShapedType::kDynamic && k != outputK)
4179 return emitOpError() <<
"requires values_out dimension 1 to have size "
4180 << k <<
", got " << outputK;
4181 if (c != ShapedType::kDynamic && outputC != ShapedType::kDynamic &&
4183 return emitOpError() <<
"requires values_out dimension 2 to have size "
4184 << c <<
", got " << outputC;
4186 if (k != ShapedType::kDynamic && w != ShapedType::kDynamic && !(k >= w))
4187 return emitOpError() <<
"requires dimensions K >= W, got K=" << k
4196 int64_t axisVal = axis.getValue().getSExtValue();
4197 if (!operandShape.
hasRank() || operandShape.
getRank() <= axisVal) {
4203 operandShape.
getDims(outputShape);
4204 outputShape[axisVal] = 1;
4209#define COMPATIBLE_RETURN_TYPES(OP) \
4210 bool OP::isCompatibleReturnTypes(TypeRange l, TypeRange r) { \
4211 if (l.size() != r.size() || l.size() != 1) \
4213 if (getElementTypeOrSelf(l[0]) != getElementTypeOrSelf(r[0])) \
4215 return succeeded(verifyCompatibleShape(l[0], r[0])); \
4218#define REDUCE_SHAPE_INFER(OP) \
4219 LogicalResult OP::inferReturnTypeComponents( \
4220 MLIRContext *context, ::std::optional<Location> location, \
4221 OP::Adaptor adaptor, \
4222 SmallVectorImpl<ShapedTypeComponents> &inferredReturnShapes) { \
4224 llvm::cast<TensorType>(adaptor.getInput().getType()).getElementType(); \
4225 ShapeAdaptor inputShape(adaptor.getInput().getType()); \
4226 const Properties &prop = adaptor.getProperties(); \
4227 return ReduceInferReturnTypes(inputShape, inputType, prop.axis, \
4228 inferredReturnShapes); \
4230 COMPATIBLE_RETURN_TYPES(OP)
4238#undef REDUCE_SHAPE_INFER
4240#undef COMPATIBLE_RETURN_TYPES
4242template <
typename T>
4245 TensorType inputType = op.getInput().getType();
4246 TensorType outputType = op.getOutput().getType();
4247 int32_t reduceAxis = op.getAxis();
4249 if (reduceAxis < 0) {
4250 op.emitOpError(
"reduce axis must not be negative");
4254 int64_t inputRank = inputType.getRank();
4257 if (reduceAxis >= inputRank && (reduceAxis != 0 || inputRank != 0)) {
4258 op.emitOpError(
"expect input tensor rank (")
4259 << inputRank <<
") to be larger than reduce axis (" << reduceAxis
4265 int64_t outputRank = outputType.getRank();
4266 if (inputType.
hasRank() && outputRank != inputType.getRank()) {
4268 "expect output tensor rank to be equal to input tensor rank");
4271 if (reduceAxis >= outputRank && (reduceAxis != 0 || outputRank != 0)) {
4272 op.emitOpError(
"expect output tensor rank (")
4273 << outputRank <<
") to be larger than reduce axis (" << reduceAxis
4279 if (outputRank != 0) {
4280 auto outputShape = outputType.
getShape();
4281 if (!outputType.isDynamicDim(reduceAxis) &&
4282 outputShape[reduceAxis] != 1) {
4283 op.emitOpError(
"expect reduced dimension size to be 1, got ")
4284 << outputShape[reduceAxis];
4292LogicalResult tosa::ReduceAllOp::verify() {
return verifyReduceOp(*
this); }
4293LogicalResult tosa::ReduceAnyOp::verify() {
return verifyReduceOp(*
this); }
4294LogicalResult tosa::ReduceMaxOp::verify() {
return verifyReduceOp(*
this); }
4295LogicalResult tosa::ReduceMinOp::verify() {
return verifyReduceOp(*
this); }
4296LogicalResult tosa::ReduceProductOp::verify() {
return verifyReduceOp(*
this); }
4297LogicalResult tosa::ReduceSumOp::verify() {
return verifyReduceOp(*
this); }
4311#define NARY_SHAPE_INFER(OP) \
4312 LogicalResult OP::inferReturnTypeComponents( \
4313 MLIRContext *context, ::std::optional<Location> location, \
4314 ValueShapeRange operands, DictionaryAttr attributes, \
4315 PropertyRef properties, RegionRange regions, \
4316 SmallVectorImpl<ShapedTypeComponents> &inferredReturnShapes) { \
4317 return NAryInferReturnTypes(operands, inferredReturnShapes); \
4357#undef PRED_SHAPE_INFER
4359LogicalResult tosa::NegateOp::inferReturnTypeComponents(
4360 MLIRContext *context, ::std::optional<Location> location,
4361 NegateOp::Adaptor adaptor,
4363 ShapeAdaptor inputShape(adaptor.getInput1().getType());
4368LogicalResult tosa::NegateOp::verify() {
4370 const Type input1Type = getInput1().getType();
4371 const Type outputType = getOutput().getType();
4376 const SmallVector<Type, 2> types = {input1Type, outputType};
4378 return emitOpError() <<
"requires the same shape for input1 and output";
4381 const Type input1ZpEType =
4383 if (input1EType != input1ZpEType) {
4384 return emitOpError(
"expect both input1 and its zero point are the same "
4385 "element type, got ")
4386 << input1EType <<
" and " << input1ZpEType;
4389 const Type outputZpEType =
4391 if (outputEType != outputZpEType) {
4392 return emitOpError(
"expect both output and its zero point are the same "
4393 "element type, got ")
4394 << outputEType <<
" and " << outputZpEType;
4397 FailureOr<int64_t> maybeIZp = getInput1ZeroPoint();
4398 if (succeeded(maybeIZp) && verifyInput1ZeroPoint(*maybeIZp).failed())
4401 FailureOr<int64_t> maybeOZp = getOutputZeroPoint();
4402 if (succeeded(maybeOZp) && verifyOutputZeroPoint(*maybeOZp).failed())
4413 outputShape.resize(4, ShapedType::kDynamic);
4428 if (ShapedType::isStatic(height)) {
4429 int64_t padded = height + pad[0] + pad[1] - kernel[0];
4430 outputShape[1] = padded / stride[0] + 1;
4433 if (ShapedType::isStatic(width)) {
4434 int64_t padded = width + pad[2] + pad[3] - kernel[1];
4435 outputShape[2] = padded / stride[1] + 1;
4442template <
typename AdaptorT>
4448 if (ShapedType::isDynamic(current))
4449 current = candidate;
4458 : adaptor(adaptor) {}
4462 const ShapeAdaptor inputShape(adaptor.getInput().getType());
4470 outputShape[0] = outputBatch;
4471 inputSpatial[0] = inputHeight;
4472 inputSpatial[1] = inputWidth;
4477 const ShapeAdaptor weightShape(adaptor.getWeight().getType());
4485 outputShape[3] = outputChannels;
4486 weightSpatial[0] = kernelHeight;
4487 weightSpatial[1] = kernelWidth;
4496 padValues.assign(adaptor.getPad().begin(), adaptor.getPad().end());
4497 strideValues.assign(adaptor.getStride().begin(), adaptor.getStride().end());
4498 dilationValues.assign(adaptor.getDilation().begin(),
4499 adaptor.getDilation().end());
4504 Conv2DOp::Adaptor adaptor;
4512 : adaptor(adaptor) {}
4516 const ShapeAdaptor inputDataShape(adaptor.getInputData().getType());
4517 if (inputDataShape.
hasRank()) {
4522 outputShape[0] = outputBatch;
4523 inputSpatial[0] = inputHeight;
4524 inputSpatial[1] = inputWidth;
4527 const ShapeAdaptor inputScaleShape(adaptor.getInputScale().getType());
4528 if (!inputScaleShape.
hasRank())
4542 const ShapeAdaptor weightDataShape(adaptor.getWeightData().getType());
4543 if (weightDataShape.
hasRank()) {
4548 outputShape[3] = outputChannels;
4549 weightSpatial[0] = kernelHeight;
4550 weightSpatial[1] = kernelWidth;
4553 const ShapeAdaptor weightScaleShape(adaptor.getWeightScale().getType());
4554 if (!weightScaleShape.
hasRank())
4583 Conv2DBlockScaledOp::Adaptor adaptor;
4591 : adaptor(adaptor) {}
4595 const ShapeAdaptor inputShape(adaptor.getInput().getType());
4604 outputShape[0] = outputBatch;
4605 inputSpatial[0] = inputDepth;
4606 inputSpatial[1] = inputHeight;
4607 inputSpatial[2] = inputWidth;
4612 const ShapeAdaptor weightShape(adaptor.getWeight().getType());
4621 outputShape[4] = outputChannels;
4622 weightSpatial[0] = kernelDepth;
4623 weightSpatial[1] = kernelHeight;
4624 weightSpatial[2] = kernelWidth;
4633 padValues.assign(adaptor.getPad().begin(), adaptor.getPad().end());
4634 strideValues.assign(adaptor.getStride().begin(), adaptor.getStride().end());
4635 dilationValues.assign(adaptor.getDilation().begin(),
4636 adaptor.getDilation().end());
4641 Conv3DOp::Adaptor adaptor;
4644template <
typename AdaptorT>
4650 ShapedType::kDynamic);
4652 ShapedType::kDynamic);
4654 ShapedType::kDynamic);
4656 convShapeAdaptor.inferInputShape(outputShape, inputSpatial);
4657 convShapeAdaptor.inferWeightShape(outputShape, weightSpatial);
4659 const ShapeAdaptor biasShape = adaptor.getBias().getType();
4662 if (biasSize != 1) {
4663 const size_t outputChannelDim = convShapeAdaptor.getOutputRank() - 1;
4664 outputShape[outputChannelDim] =
4665 ShapedType::isDynamic(outputShape[outputChannelDim])
4667 : outputShape[outputChannelDim];
4674 if (failed(convShapeAdaptor.getSpatialParameters(padValues, strideValues,
4680 for (
int64_t dim = 0; dim < convShapeAdaptor.getNumSpatialDims(); ++dim) {
4681 if (!ShapedType::isStatic(inputSpatial[dim]) ||
4682 !ShapedType::isStatic(weightSpatial[dim]))
4685 inputSpatial[dim] + padValues[2 * dim] + padValues[2 * dim + 1];
4687 (weightSpatial[dim] - 1) * dilationValues[dim] + 1;
4688 const int64_t unstridedResult = inputSize - filterSize + 1;
4689 outputShape[dim + 1] = (unstridedResult - 1) / strideValues[dim] + 1;
4696LogicalResult Conv2DOp::inferReturnTypeComponents(
4697 MLIRContext *context, ::std::optional<Location> location,
4698 Conv2DOp::Adaptor adaptor,
4699 SmallVectorImpl<ShapedTypeComponents> &inferredReturnShapes) {
4703LogicalResult Conv2DOp::verify() {
4710LogicalResult Conv2DBlockScaledOp::inferReturnTypeComponents(
4711 MLIRContext *context, ::std::optional<Location> location,
4712 Conv2DBlockScaledOp::Adaptor adaptor,
4713 SmallVectorImpl<ShapedTypeComponents> &inferredReturnShapes) {
4717LogicalResult Conv2DBlockScaledOp::verify() {
4719 getWeightData().
getType(),
"input_data",
4722 getWeightScale().
getType(),
"input_scale",
4725 getOutput().
getType(),
"bias",
"output")))
4729 int64_t N = ShapedType::kDynamic;
4730 int64_t IH = ShapedType::kDynamic;
4731 int64_t IW = ShapedType::kDynamic;
4732 int64_t IC = ShapedType::kDynamic;
4733 int64_t multiplesOfIC = ShapedType::kDynamic;
4734 int64_t OC = ShapedType::kDynamic;
4735 int64_t KH = ShapedType::kDynamic;
4736 int64_t KW = ShapedType::kDynamic;
4738 const ShapeAdaptor inputDataShape(getInputData().
getType());
4739 if (inputDataShape.hasRank()) {
4740 N = inputDataShape.getDimSize(0);
4741 IH = inputDataShape.getDimSize(1);
4742 IW = inputDataShape.getDimSize(2);
4743 IC = inputDataShape.getDimSize(3);
4746 const ShapeAdaptor inputScaleShape(getInputScale().
getType());
4747 if (inputScaleShape.hasRank()) {
4749 "input_scale",
"batch size")) ||
4751 "input_scale",
"input height")) ||
4753 "input_scale",
"input width")))
4755 multiplesOfIC = inputScaleShape.getDimSize(3);
4758 const ShapeAdaptor weightDataShape(getWeightData().
getType());
4759 if (weightDataShape.hasRank()) {
4760 OC = weightDataShape.getDimSize(0);
4761 KH = weightDataShape.getDimSize(1);
4762 KW = weightDataShape.getDimSize(2);
4764 "weight_data",
"input channels")))
4768 const ShapeAdaptor weightScaleShape(getWeightScale().
getType());
4769 if (weightScaleShape.hasRank()) {
4771 "weight_scale",
"output channels")) ||
4773 "weight_scale",
"kernel height")) ||
4775 "weight_scale",
"kernel width")) ||
4777 weightScaleShape.getDimSize(3),
4778 "weight_scale",
"input channel blocks")))
4782 const uint32_t blockSize = BlockSizeAttr::getBlockSizeValue(
getBlockSize());
4783 if (blockSize != BlockSizeAttr::getBlockSizeValue(BlockSize::BLOCK_SIZE_32))
4784 return emitOpError(
"expect block size to be 32, got ") << blockSize;
4786 if (ShapedType::isStatic(IC) && IC % blockSize != 0)
4787 return emitOpError(
"expect IC to be a multiple of block size, got IC=")
4788 << IC <<
", block_size=" << blockSize;
4791 if (ShapedType::isStatic(IC) && ShapedType::isStatic(multiplesOfIC) &&
4792 multiplesOfIC != IC / blockSize)
4794 "expect scale operands dimension 2 to equal IC/block_size (")
4795 << IC <<
"/" << blockSize <<
")"
4796 <<
", got " << multiplesOfIC;
4799 SmallVector<int64_t> padValues;
4801 if (llvm::any_of(padValues, [](int64_t p) {
return p < 0; }))
4802 return emitOpError(
"expect all padding values to be >= 0, got ")
4806 SmallVector<int64_t> strideValues;
4808 if (llvm::any_of(strideValues, [](int64_t s) {
return s < 1; }))
4809 return emitOpError(
"expect all stride values to be >= 1, got ")
4813 SmallVector<int64_t> dilationValues;
4816 if (llvm::any_of(dilationValues, [](int64_t d) {
return d < 1; }))
4817 return emitOpError(
"expect all dilation values to be >= 1, got ")
4822 const ShapeAdaptor outputShape(getOutput().
getType());
4823 if (!padValues.empty() && !strideValues.empty() && !dilationValues.empty() &&
4824 outputShape.hasRank()) {
4826 padValues[0], padValues[1], strideValues[0],
4827 dilationValues[0],
"height",
"y",
"top",
4830 padValues[2], padValues[3], strideValues[1],
4831 dilationValues[1],
"width",
"x",
"left",
4837 const ShapeAdaptor biasShape(getBias().
getType());
4838 if (biasShape.hasRank() && outputShape.hasRank()) {
4839 const int64_t biasChannels = biasShape.getDimSize(0);
4840 const int64_t outputChannels =
4841 outputShape.getDimSize(outputShape.getRank() - 1);
4842 if (biasChannels == ShapedType::kDynamic ||
4843 outputChannels == ShapedType::kDynamic)
4847 if (biasChannels != outputChannels && biasChannels != 1)
4849 "bias channels expected to be equal to output channels (")
4850 << outputChannels <<
") or 1, got " << biasChannels;
4856LogicalResult Conv3DOp::inferReturnTypeComponents(
4857 MLIRContext *context, ::std::optional<Location> location,
4858 Conv3DOp::Adaptor adaptor,
4859 SmallVectorImpl<ShapedTypeComponents> &inferredReturnShapes) {
4863LogicalResult Conv3DOp::verify() {
4870LogicalResult AvgPool2dOp::inferReturnTypeComponents(
4871 MLIRContext *context, ::std::optional<Location> location,
4872 AvgPool2dOp::Adaptor adaptor,
4873 SmallVectorImpl<ShapedTypeComponents> &inferredReturnShapes) {
4874 ShapeAdaptor inputShape(adaptor.getInput().getType());
4875 const Properties &prop = adaptor.getProperties();
4877 inferredReturnShapes);
4880LogicalResult AvgPool2dAdaptiveOp::inferReturnTypeComponents(
4881 MLIRContext *context, ::std::optional<Location> location,
4882 AvgPool2dAdaptiveOp::Adaptor adaptor,
4883 SmallVectorImpl<ShapedTypeComponents> &inferredReturnShapes) {
4884 ShapeAdaptor inputShape(adaptor.getInput().getType());
4886 llvm::SmallVector<int64_t> kernelValues;
4887 llvm::SmallVector<int64_t> strideValues;
4888 llvm::SmallVector<int64_t> padValues;
4895 padValues, inferredReturnShapes);
4898 llvm::SmallVector<int64_t> outputShape(4, ShapedType::kDynamic);
4899 if (inputShape.hasRank()) {
4901 outputShape[0] = inputShape.getDimSize(0);
4902 outputShape[3] = inputShape.getDimSize(3);
4905 inferredReturnShapes.push_back(ShapedTypeComponents(outputShape));
4909LogicalResult MaxPool2dOp::inferReturnTypeComponents(
4910 MLIRContext *context, ::std::optional<Location> location,
4911 MaxPool2dOp::Adaptor adaptor,
4912 SmallVectorImpl<ShapedTypeComponents> &inferredReturnShapes) {
4913 ShapeAdaptor inputShape(adaptor.getInput().getType());
4914 const Properties &prop = adaptor.getProperties();
4916 inferredReturnShapes);
4919LogicalResult MaxPool2dAdaptiveOp::inferReturnTypeComponents(
4920 MLIRContext *context, ::std::optional<Location> location,
4921 MaxPool2dAdaptiveOp::Adaptor adaptor,
4922 SmallVectorImpl<ShapedTypeComponents> &inferredReturnShapes) {
4923 ShapeAdaptor inputShape(adaptor.getInput().getType());
4925 llvm::SmallVector<int64_t> kernelValues;
4926 llvm::SmallVector<int64_t> strideValues;
4927 llvm::SmallVector<int64_t> padValues;
4934 padValues, inferredReturnShapes);
4937 llvm::SmallVector<int64_t> outputShape(4, ShapedType::kDynamic);
4938 if (inputShape.hasRank()) {
4939 outputShape[0] = inputShape.getDimSize(0);
4940 outputShape[3] = inputShape.getDimSize(3);
4942 inferredReturnShapes.push_back(ShapedTypeComponents(outputShape));
4946LogicalResult MaxPool2dOp::verify() {
4957LogicalResult MaxPool2dAdaptiveOp::verify() {
4962 AdaptivePoolingConstShapeValues values;
4966 values.pad, getInput(), getOutput())))
4972LogicalResult DepthwiseConv2DOp::inferReturnTypeComponents(
4973 MLIRContext *context, ::std::optional<Location> location,
4974 DepthwiseConv2DOp::Adaptor adaptor,
4975 SmallVectorImpl<ShapedTypeComponents> &inferredReturnShapes) {
4976 llvm::SmallVector<int64_t> outputShape(4, ShapedType::kDynamic);
4978 int64_t inputWidth = ShapedType::kDynamic;
4979 int64_t inputHeight = ShapedType::kDynamic;
4980 int64_t inputChannels = ShapedType::kDynamic;
4982 int64_t weightWidth = ShapedType::kDynamic;
4983 int64_t weightHeight = ShapedType::kDynamic;
4984 int64_t depthChannels = ShapedType::kDynamic;
4987 ShapeAdaptor inputShape(adaptor.getInput().getType());
4988 if (inputShape.hasRank()) {
4989 outputShape[0] = inputShape.getDimSize(0);
4990 inputHeight = inputShape.getDimSize(1);
4991 inputWidth = inputShape.getDimSize(2);
4992 inputChannels = inputShape.getDimSize(3);
4996 ShapeAdaptor weightShape(adaptor.getWeight().getType());
4997 if (weightShape.hasRank()) {
4998 weightHeight = weightShape.getDimSize(0);
4999 weightWidth = weightShape.getDimSize(1);
5000 inputChannels = ShapedType::isDynamic(inputChannels)
5001 ? weightShape.getDimSize(2)
5003 depthChannels = weightShape.getDimSize(3);
5008 if (ShapedType::isStatic(inputChannels) &&
5009 ShapedType::isStatic(depthChannels)) {
5010 outputShape[3] = inputChannels * depthChannels;
5014 ShapeAdaptor biasShape(adaptor.getBias().getType());
5015 if (biasShape.hasRank() && ShapedType::isDynamic(outputShape[3])) {
5016 int64_t bc = biasShape.getDimSize(0);
5017 if (bc != ShapedType::kDynamic && bc != 1)
5018 outputShape[3] = bc;
5021 llvm::ArrayRef<int64_t> dilation = adaptor.getDilation();
5022 llvm::ArrayRef<int64_t> padding = adaptor.getPad();
5023 llvm::ArrayRef<int64_t> stride = adaptor.getStride();
5025 if (ShapedType::isStatic(inputHeight) && ShapedType::isStatic(weightHeight)) {
5026 int64_t inputSize = inputHeight + padding[0] + padding[1];
5027 int64_t filterSize = (weightHeight - 1) * dilation[0] + 1;
5028 int64_t unstridedResult = inputSize - filterSize + 1;
5029 outputShape[1] = (unstridedResult - 1) / stride[0] + 1;
5032 if (ShapedType::isStatic(inputWidth) && ShapedType::isStatic(weightWidth)) {
5033 int64_t inputSize = inputWidth + padding[2] + padding[3];
5034 int64_t filterSize = (weightWidth - 1) * dilation[1] + 1;
5035 int64_t unstridedResult = inputSize - filterSize + 1;
5036 outputShape[2] = (unstridedResult - 1) / stride[1] + 1;
5039 inferredReturnShapes.push_back(ShapedTypeComponents(outputShape));
5043LogicalResult DepthwiseConv2DOp::verify() {
5050LogicalResult TransposeConv2DOp::inferReturnTypeComponents(
5051 MLIRContext *context, ::std::optional<Location> location,
5052 TransposeConv2DOp::Adaptor adaptor,
5053 SmallVectorImpl<ShapedTypeComponents> &inferredReturnShapes) {
5054 llvm::SmallVector<int64_t> outputShape(4, ShapedType::kDynamic);
5056 int64_t inputWidth = ShapedType::kDynamic;
5057 int64_t inputHeight = ShapedType::kDynamic;
5058 int64_t weightWidth = ShapedType::kDynamic;
5059 int64_t weightHeight = ShapedType::kDynamic;
5062 ShapeAdaptor inputShape(adaptor.getInput().getType());
5063 if (inputShape.hasRank()) {
5064 outputShape[0] = ShapedType::isDynamic(outputShape[0])
5065 ? inputShape.getDimSize(0)
5067 inputHeight = inputShape.getDimSize(1);
5068 inputWidth = inputShape.getDimSize(2);
5072 ShapeAdaptor weightShape(adaptor.getWeight().getType());
5073 if (weightShape.hasRank()) {
5074 outputShape[3] = ShapedType::isDynamic(outputShape[3])
5075 ? weightShape.getDimSize(0)
5077 weightHeight = weightShape.getDimSize(1);
5078 weightWidth = weightShape.getDimSize(2);
5082 ShapeAdaptor biasShape(adaptor.getBias().getType());
5083 if (biasShape.hasRank() && ShapedType::isDynamic(outputShape[3])) {
5084 int64_t bc = biasShape.getDimSize(0);
5085 if (bc != ShapedType::kDynamic && bc != 1)
5086 outputShape[3] = bc;
5089 llvm::ArrayRef<int64_t> padding = adaptor.getOutPad();
5090 llvm::ArrayRef<int64_t> stride = adaptor.getStride();
5092 if (ShapedType::isStatic(inputHeight) && ShapedType::isStatic(weightHeight)) {
5093 int64_t calculateSize =
5094 (inputHeight - 1) * stride[0] + padding[0] + padding[1] + weightHeight;
5096 ShapedType::isDynamic(outputShape[1]) ? calculateSize : outputShape[1];
5099 if (ShapedType::isStatic(inputWidth) && ShapedType::isStatic(weightWidth)) {
5100 int64_t calculateSize =
5101 (inputWidth - 1) * stride[1] + padding[2] + padding[3] + weightWidth;
5103 ShapedType::isDynamic(outputShape[2]) ? calculateSize : outputShape[2];
5106 inferredReturnShapes.push_back(ShapedTypeComponents(outputShape));
5110LogicalResult TransposeConv2DOp::verify() {
5114 const llvm::ArrayRef<int64_t> strides = getStride();
5115 const int64_t strideY = strides[0];
5116 const int64_t strideX = strides[1];
5118 if (strideY < 1 || strideX < 1)
5119 return emitOpError(
"expect all stride values to be >= 1, got [")
5122 const auto checkPadAgainstKernelDim =
5123 [
this](int64_t padValue, int64_t kernelDimSize, llvm::StringRef padName,
5124 llvm::StringRef kernelDimName) -> LogicalResult {
5125 if (padValue <= -kernelDimSize)
5127 << padName <<
" > -" << kernelDimName <<
", but got: " << padName
5128 <<
"=" << padValue <<
" and " << kernelDimName <<
"="
5133 const llvm::ArrayRef<int64_t> padding = getOutPad();
5134 const int64_t outPadTop = padding[0];
5135 const int64_t outPadBottom = padding[1];
5136 const int64_t outPadLeft = padding[2];
5137 const int64_t outPadRight = padding[3];
5139 const auto weightType =
5140 llvm::dyn_cast<RankedTensorType>(getWeight().
getType());
5143 const int64_t kernelHeight = weightType.getDimSize(1);
5144 if (ShapedType::isStatic(kernelHeight)) {
5145 if (
failed(checkPadAgainstKernelDim(outPadTop, kernelHeight,
5146 "out_pad_top",
"KH")))
5149 if (
failed(checkPadAgainstKernelDim(outPadBottom, kernelHeight,
5150 "out_pad_bottom",
"KH")))
5154 const int64_t kernelWidth = weightType.getDimSize(2);
5155 if (ShapedType::isStatic(kernelWidth)) {
5156 if (
failed(checkPadAgainstKernelDim(outPadLeft, kernelWidth,
5157 "out_pad_left",
"KW")))
5160 if (
failed(checkPadAgainstKernelDim(outPadRight, kernelWidth,
5161 "out_pad_right",
"KW")))
5167 const auto outputType =
5168 llvm::dyn_cast<RankedTensorType>(getOutput().
getType());
5172 const auto inputType = llvm::dyn_cast<RankedTensorType>(getInput().
getType());
5173 if (inputType && weightType) {
5174 const int64_t inputHeight = inputType.getDimSize(1);
5175 const int64_t kernelHeight = weightType.getDimSize(1);
5176 const int64_t outputHeight = outputType.getDimSize(1);
5178 if (ShapedType::isStatic(inputHeight) &&
5179 ShapedType::isStatic(outputHeight)) {
5181 (inputHeight - 1) * strideY + outPadTop + outPadBottom + kernelHeight)
5183 "dimension mismatch: expected OH == (IH - 1) * stride_y "
5184 "+ out_pad_top + out_pad_bottom + KH, but got ")
5185 << outputHeight <<
" != (" << inputHeight <<
" - 1) * "
5186 << strideY <<
" + " << outPadTop <<
" + " << outPadBottom
5187 <<
" + " << kernelHeight;
5190 const int64_t inputWidth = inputType.getDimSize(2);
5191 const int64_t kernelWidth = weightType.getDimSize(2);
5192 const int64_t outputWidth = outputType.getDimSize(2);
5194 if (ShapedType::isStatic(inputWidth) && ShapedType::isStatic(outputWidth)) {
5196 (inputWidth - 1) * strideX + outPadLeft + outPadRight + kernelWidth)
5198 "dimension mismatch: expected OW == (IW - 1) * stride_x "
5199 "+ out_pad_left + out_pad_right + KW, but got ")
5200 << outputWidth <<
" != (" << inputWidth <<
" - 1) * " << strideX
5201 <<
" + " << outPadLeft <<
" + " << outPadRight <<
" + "
5206 const auto biasType = llvm::dyn_cast<RankedTensorType>(getBias().
getType());
5211 const int64_t biasChannels = biasType.getDimSize(0);
5214 if (biasChannels == ShapedType::kDynamic)
5217 const int64_t outputChannels = outputType.getDimSize(3);
5218 if (!ShapedType::isDynamic(outputChannels) &&
5219 biasChannels != outputChannels && biasChannels != 1)
5221 "bias channels expected to be equal to output channels (")
5222 << outputChannels <<
") or 1, got " << biasChannels;
5227LogicalResult RescaleOp::verify() {
5228 const auto inputType = llvm::cast<ShapedType>(getInput().
getType());
5229 auto inputElementType =
5231 if (!mlir::isa<IntegerType>(inputElementType)) {
5232 emitOpError(
"expect input to have integer element type, got ")
5233 << inputElementType;
5237 const auto outputType = llvm::cast<ShapedType>(getOutput().
getType());
5238 auto outputElementType =
5240 if (!mlir::isa<IntegerType>(outputElementType)) {
5241 emitOpError(
"expect output to have integer element type, got ")
5242 << outputElementType;
5254 FailureOr<int64_t> maybeIZp = getInputZeroPoint();
5255 if (succeeded(maybeIZp) && verifyInputZeroPoint(*maybeIZp).failed())
5258 FailureOr<int64_t> maybeOZp = getOutputZeroPoint();
5259 if (succeeded(maybeOZp) && verifyOutputZeroPoint(*maybeOZp).failed())
5262 const auto multiplierType = llvm::cast<ShapedType>(getMultiplier().
getType());
5264 if (getScale32() && !multiplierType.getElementType().isInteger(32)) {
5265 emitOpError(
"expect i32 element type for multiplier for scale32=true, got ")
5266 << multiplierType.getElementType();
5271 if (!getScale32() && !multiplierType.getElementType().isInteger(16)) {
5273 "expect i16 element type for multiplier for scale32=false, got ")
5274 << multiplierType.getElementType();
5278 if (!inputType.hasRank())
5284 int64_t numChannels = 1;
5285 if (getPerChannel()) {
5286 if (inputType.getRank() < 1) {
5287 emitOpError(
"requires input to be at least rank 1 when per_channel is "
5288 "true, but got rank ")
5289 << inputType.getRank();
5292 numChannels = inputType.getDimSize(inputType.getRank() - 1);
5295 if (outputType.hasRank()) {
5297 getOperation(), outputType, inputType.getShape())))
5301 if (multiplierType.hasRank()) {
5302 ArrayRef<int64_t> multiplierShape = multiplierType.getShape();
5304 if (multiplierShape[0] != ShapedType::kDynamic &&
5305 multiplierShape[0] != numChannels) {
5307 << numChannels <<
" } for multiplier input, got { "
5308 << multiplierShape[0] <<
" }";
5313 const auto shiftType = llvm::cast<ShapedType>(getShift().
getType());
5314 if (shiftType.hasRank()) {
5315 ArrayRef<int64_t> shiftShape = shiftType.getShape();
5317 if (shiftShape[0] != ShapedType::kDynamic && shiftShape[0] != numChannels) {
5319 << numChannels <<
" } for shift input, got { " << shiftShape[0]
5328LogicalResult RescaleOp::inferReturnTypeComponents(
5329 MLIRContext *context, ::std::optional<Location> location,
5330 RescaleOp::Adaptor adaptor,
5331 SmallVectorImpl<ShapedTypeComponents> &inferredReturnShapes) {
5332 ShapeAdaptor inputShape(adaptor.getInput().getType());
5333 inferredReturnShapes.push_back(ShapedTypeComponents(inputShape));
5337LogicalResult CastOp::verify() {
5338 const ShapedType inputType = llvm::cast<ShapedType>(getInput().
getType());
5339 const ShapedType outputType = llvm::cast<ShapedType>(
getType());
5340 const Type inputElementType = inputType.getElementType();
5341 const Type outputElementType = outputType.getElementType();
5343 const bool inputIsBlockScaled = llvm::isa<BlockScaledType>(inputElementType);
5344 const bool outputIsBlockScaled =
5345 llvm::isa<BlockScaledType>(outputElementType);
5346 if (!inputIsBlockScaled && !outputIsBlockScaled)
5349 if (inputIsBlockScaled && outputIsBlockScaled)
5351 <<
"requires exactly one of input or output to have block scaled "
5354 const Type scalarElementType =
5355 inputIsBlockScaled ? outputElementType : inputElementType;
5356 if (!llvm::isa<FloatType>(scalarElementType))
5358 <<
"requires non-block-scaled element type to be floating-point "
5359 "when casting to or from block scaled element type, got "
5360 << scalarElementType;
5365LogicalResult CastFromBlockScaledOp::inferReturnTypeComponents(
5366 MLIRContext *context, ::std::optional<Location> location,
5367 CastFromBlockScaledOp::Adaptor adaptor,
5368 SmallVectorImpl<ShapedTypeComponents> &inferredReturnShapes) {
5369 const ShapeAdaptor inputShape(adaptor.getInputData().getType());
5370 inferredReturnShapes.push_back(ShapedTypeComponents(inputShape));
5374LogicalResult CastFromBlockScaledOp::verify() {
5375 const Type inputDataType = getInputData().getType();
5376 const Type outputDataType = getResult().getType();
5378 return emitOpError() <<
"require compatible shapes for input_data ("
5379 << inputDataType <<
") and " <<
"output_data ("
5380 << outputDataType <<
")";
5382 const ShapeAdaptor inputDataShape = ShapeAdaptor(inputDataType);
5384 if (inputDataShape.
hasRank()) {
5385 const unsigned int blockSize =
5387 if (blockSize != BlockSizeAttr::getBlockSizeValue(BlockSize::BLOCK_SIZE_32))
5388 return emitOpError(
"expect block size to be 32, got ") << blockSize;
5389 const int64_t inputDataLastDim =
5391 if (inputDataLastDim % blockSize != 0)
5392 return emitOpError() <<
"expect last dimension of input_data ("
5394 <<
") to be divisible by block_size (" << blockSize
5397 const Type inputScaleType = getInputScale().getType();
5398 const ShapeAdaptor inputScaleShape = ShapeAdaptor(inputScaleType);
5400 if (inputScaleShape.
hasRank()) {
5401 SmallVector<int64_t> inputDataDims, inputScaleDims;
5402 inputDataShape.
getDims(inputDataDims);
5403 inputScaleShape.
getDims(inputScaleDims);
5405 if (inputDataDims.size() != inputScaleDims.size() ||
5407 ArrayRef<int64_t>(inputDataDims).drop_back(1),
5408 ArrayRef<int64_t>(inputScaleDims).drop_back(1))))
5410 <<
"require compatible shapes for input_data (" << inputDataType
5411 <<
") and " <<
"input_scale (" << inputScaleType
5412 <<
") except for the last dimension";
5414 const SmallVector<int64_t, 2> dimsToCheck{inputDataLastDim / blockSize,
5415 inputScaleDims.back()};
5416 if (ShapedType::isStatic(inputDataLastDim) &&
5419 <<
"expect last dimension of input_scale ("
5420 << inputScaleDims.back()
5421 <<
") to be equal to last dimension of input_data / block_size ("
5422 << inputDataDims.back() / blockSize <<
")";
5429LogicalResult CastToBlockScaledOp::inferReturnTypeComponents(
5430 MLIRContext *context, ::std::optional<Location> location,
5431 CastToBlockScaledOp::Adaptor adaptor,
5432 SmallVectorImpl<ShapedTypeComponents> &inferredReturnShapes) {
5433 const ShapeAdaptor inputShape(adaptor.getInputData().getType());
5434 inferredReturnShapes.push_back(ShapedTypeComponents(inputShape));
5435 if (!inputShape.hasRank())
5439 SmallVector<int64_t> outputScaleShape;
5440 inputShape.getDims(outputScaleShape);
5441 const int64_t lastDimLoc = inputShape.getRank() - 1;
5442 const int64_t lastDimSize = inputShape.getDimSize(lastDimLoc);
5443 if (ShapedType::isStatic(lastDimSize)) {
5444 const unsigned int blockSize =
5445 BlockSizeAttr::getBlockSizeValue(adaptor.getBlockSize());
5446 outputScaleShape[lastDimLoc] = lastDimSize / blockSize;
5448 inferredReturnShapes.push_back(ShapedTypeComponents(outputScaleShape));
5452LogicalResult CastToBlockScaledOp::verify() {
5453 const Type inputDataType = getInputData().getType();
5454 const Type outputDataType = getResult(0).getType();
5456 return emitOpError() <<
"require compatible shapes for input_data ("
5457 << inputDataType <<
") and " <<
"output_data ("
5458 << outputDataType <<
")";
5460 const unsigned int blockSize =
5462 if (blockSize != BlockSizeAttr::getBlockSizeValue(BlockSize::BLOCK_SIZE_32))
5463 return emitOpError(
"expect block size to be 32, got ") << blockSize;
5464 const ShapeAdaptor inputDataShape = ShapeAdaptor(inputDataType);
5465 if (inputDataShape.
hasRank()) {
5466 const int64_t inputDataLastDim =
5468 if (ShapedType::isStatic(inputDataLastDim) &&
5469 inputDataLastDim % blockSize != 0)
5470 return emitOpError() <<
"expect last dimension of input_data ("
5472 <<
") to be divisible by block_size (" << blockSize
5476 const ShapeAdaptor outputDataShape = ShapeAdaptor(outputDataType);
5477 const Type outputScaleType = getResult(1).getType();
5478 const ShapeAdaptor outputScaleShape = ShapeAdaptor(outputScaleType);
5480 SmallVector<int64_t> outputDataDims, outputScaleDims;
5481 outputDataShape.
getDims(outputDataDims);
5482 outputScaleShape.
getDims(outputScaleDims);
5484 if (outputDataDims.size() != outputScaleDims.size() ||
5486 ArrayRef<int64_t>(outputDataDims).drop_back(1),
5487 ArrayRef<int64_t>(outputScaleDims).drop_back(1))))
5488 return emitOpError() <<
"require compatible shapes for output_data ("
5489 << outputDataType <<
") and " <<
"output_scale ("
5491 <<
") except for the last dimension";
5493 const int64_t outputDataLastDim = outputDataDims.back();
5494 const SmallVector<int64_t, 2> dimsToCheck{outputDataLastDim / blockSize,
5495 outputScaleDims.back()};
5496 if (ShapedType::isStatic(outputDataLastDim) &&
5499 <<
"expect last dimension of output_scale ("
5500 << outputScaleDims.back()
5501 <<
") to be equal to last dimension of output_data / block_size ("
5502 << outputDataDims.back() / blockSize <<
")";
5508LogicalResult IfOp::inferReturnTypeComponents(
5509 MLIRContext *context, ::std::optional<Location> location,
5510 IfOp::Adaptor adaptor,
5511 SmallVectorImpl<ShapedTypeComponents> &inferredReturnShapes) {
5512 llvm::SmallVector<tosa::YieldOp> yieldOps;
5513 for (Region *region : adaptor.getRegions()) {
5514 for (
auto &block : *region)
5515 if (
auto returnOp = dyn_cast<tosa::YieldOp>(block.getTerminator()))
5516 yieldOps.push_back(returnOp);
5519 if (yieldOps.empty())
5523 llvm::SmallVector<ValueKnowledge> resultKnowledge;
5524 resultKnowledge.reserve(yieldOps.front().getNumOperands());
5525 for (
auto operand : yieldOps.front().getOperands()) {
5526 resultKnowledge.push_back(
5530 for (
auto yieldOp : yieldOps) {
5531 if (resultKnowledge.size() != yieldOp.getNumOperands())
5534 for (
const auto &it : llvm::enumerate(yieldOp.getOperands())) {
5535 int32_t index = it.index();
5537 resultKnowledge[index],
5541 resultKnowledge[index] = meet;
5545 for (
const ValueKnowledge &
result : resultKnowledge) {
5546 inferredReturnShapes.push_back(
result.getShapedTypeComponents());
5552LogicalResult WhileOp::inferReturnTypeComponents(
5553 MLIRContext *context, ::std::optional<Location> location,
5554 WhileOp::Adaptor adaptor,
5555 SmallVectorImpl<ShapedTypeComponents> &inferredReturnShapes) {
5556 llvm::SmallVector<tosa::YieldOp> yieldOps;
5557 for (
auto &block : adaptor.getBodyGraph())
5558 if (
auto returnOp = dyn_cast<tosa::YieldOp>(block.getTerminator()))
5559 yieldOps.push_back(returnOp);
5563 if (yieldOps.empty())
5567 llvm::SmallVector<ValueKnowledge> resultKnowledge;
5568 resultKnowledge.reserve(yieldOps.front().getNumOperands());
5569 for (
auto operand : yieldOps.front().getOperands()) {
5570 resultKnowledge.push_back(
5574 for (
auto yieldOp : yieldOps) {
5575 if (resultKnowledge.size() != yieldOp.getNumOperands())
5578 for (
const auto &it : llvm::enumerate(yieldOp.getOperands())) {
5579 int32_t index = it.index();
5581 resultKnowledge[index],
5583 resultKnowledge[index] = meet;
5588 for (
const ValueKnowledge &
result : resultKnowledge) {
5589 inferredReturnShapes.push_back(
result.getShapedTypeComponents());
5595std::optional<SmallVector<int64_t, 4>> ApplyScaleOp::getShapeForUnroll() {
5596 if (
auto vt = llvm::dyn_cast<VectorType>(
getType()))
5597 return llvm::to_vector<4>(vt.getShape());
5598 return std::nullopt;
5604 StringRef prefix =
"") {
5605 assert(blocksArgs.size() == initializers.size() &&
5606 "expected same length of arguments and initializers");
5607 if (initializers.empty())
5610 parser << prefix <<
'(';
5611 llvm::interleaveComma(
5612 llvm::zip(blocksArgs, initializers), parser,
5613 [&](
auto it) { parser << std::get<0>(it) <<
" = " << std::get<1>(it); });
5618ParseResult IfOp::parse(OpAsmParser &parser, OperationState &
result) {
5620 result.regions.reserve(2);
5621 Region *thenRegion =
result.addRegion();
5622 Region *elseRegion =
result.addRegion();
5624 OpAsmParser::UnresolvedOperand cond;
5629 SmallVector<OpAsmParser::Argument, 4> regionArgs;
5630 SmallVector<OpAsmParser::UnresolvedOperand, 4> operands;
5633 OptionalParseResult listResult =
5641 "expected type for condition operand");
5647 "expected type for condition operand");
5655 FunctionType functionType;
5659 <<
"expected list of types for block arguments "
5660 <<
"followed by arrow type and list of return types";
5662 result.addTypes(functionType.getResults());
5664 if (functionType.getNumInputs() != operands.size()) {
5666 <<
"expected as many input types as operands " <<
"(expected "
5667 << operands.size() <<
" got " << functionType.getNumInputs()
5698void IfOp::print(OpAsmPrinter &p) {
5699 p <<
" " << getCondition();
5702 getInputList(),
" ");
5704 p << getCondition().getType();
5706 if (!getInputList().empty()) {
5708 llvm::interleaveComma(getInputList().getTypes(), p);
5717 auto &elseRegion = getElseGraph();
5718 if (!elseRegion.
empty()) {
5726LogicalResult IfOp::verify() {
5728 "'then_graph' arguments", getInputList(),
5734 "'else_graph' arguments", getInputList(),
5740 if (getThenGraph().front().mightHaveTerminator()) {
5742 dyn_cast<tosa::YieldOp>(getThenGraph().front().getTerminator());
5744 *
this, thenYield.getInputs(),
"'then_graph' results",
5745 getOutputList(),
"'output_list'")
5751 if (getElseGraph().front().mightHaveTerminator()) {
5753 dyn_cast<tosa::YieldOp>(getElseGraph().front().getTerminator());
5755 *
this, elseYield.getInputs(),
"'else_graph' results",
5756 getOutputList(),
"'output_list'")
5761 auto condType = getCondition().getType();
5763 return emitOpError() <<
"'condition' must be a size 1 tensor, got "
5769LogicalResult WhileOp::verify() {
5771 getOutputList(),
"'output_list'")
5776 "'cond_graph' arguments", getInputList(),
5782 "'body_graph' arguments", getInputList(),
5787 if (getBodyGraph().front().mightHaveTerminator()) {
5789 dyn_cast<tosa::YieldOp>(getBodyGraph().front().getTerminator());
5791 "'body_graph' results",
5792 getInputList(),
"'input_list'")
5799 if (!getCondGraph().front().mightHaveTerminator())
5803 dyn_cast<tosa::YieldOp>(getCondGraph().front().getTerminator());
5807 if (condYield.getInputs().size() != 1)
5808 return emitOpError() <<
"require 'cond_graph' only have one result";
5810 auto condOutType = condYield.getInputs()[0].getType();
5812 return emitOpError() <<
"'cond_graph' result must be a size 1 tensor, got "
5816 return emitOpError() <<
"'cond_graph' result must be a boolean tensor, got "
5822LogicalResult ReverseOp::verify() {
5823 TensorType inputType = getInput1().getType();
5824 int32_t reverseAxis = getAxis();
5826 if (reverseAxis < 0)
5827 return emitOpError(
"expected non-negative reverse axis");
5829 int64_t inputRank = inputType.getRank();
5832 if (reverseAxis >= inputRank && (reverseAxis != 0 || inputRank != 0))
5834 << inputRank <<
") to be larger than reverse axis (" << reverseAxis
5841LogicalResult tosa::SelectOp::verify() {
5852 auto predicateType = llvm::dyn_cast<ShapedType>(getPred().
getType());
5853 if (!predicateType) {
5854 return emitOpError(
"expect shaped tensor for input1, got ")
5855 << getInput1().getType();
5857 auto predicateElementType = predicateType.getElementType();
5858 if (!predicateElementType.isInteger(1)) {
5859 return emitOpError(
"expect element type of bool for input1, got ")
5860 << predicateElementType;
5866LogicalResult tosa::VariableReadOp::verify() {
5874LogicalResult tosa::VariableWriteOp::verify() {
5883ParseResult WhileOp::parse(OpAsmParser &parser, OperationState &
result) {
5884 SmallVector<OpAsmParser::Argument, 4> regionArgs;
5885 SmallVector<OpAsmParser::UnresolvedOperand, 4> operands;
5886 Region *cond =
result.addRegion();
5887 Region *body =
result.addRegion();
5889 OptionalParseResult listResult =
5894 FunctionType functionType;
5899 result.addTypes(functionType.getResults());
5901 if (functionType.getNumInputs() != operands.size()) {
5903 <<
"expected as many input types as operands " <<
"(expected "
5904 << operands.size() <<
" got " << functionType.getNumInputs() <<
")";
5914 for (
size_t i = 0, e = regionArgs.size(); i != e; ++i)
5915 regionArgs[i].type = functionType.getInput(i);
5917 return failure(parser.
parseRegion(*cond, regionArgs) ||
5922void WhileOp::print(OpAsmPrinter &parser) {
5924 getInputList(),
" ");
5927 getResults().getTypes());
5941 auto zpType = mlir::RankedTensorType::get({1}, srcElemType);
5942 if (llvm::isa<FloatType>(srcElemType)) {
5944 zpType, builder.
getFloatAttr(srcElemType,
static_cast<double>(zp)));
5945 return tosa::ConstOp::create(builder, loc, zpType, zpAttr);
5947 if (llvm::isa<IntegerType>(srcElemType)) {
5950 return tosa::ConstOp::create(builder, loc, zpType, zpAttr);
5952 llvm::errs() <<
"zero point is not allowed for unsupported data types\n";
5953 return std::nullopt;
5961 return mlir::isa<tosa::shapeType>(t);
5968 return emitError() <<
"invalid rank (must be >= 0): " << rank;
5974 if (mlir::isa<::mlir::tosa::shapeType>(v.getType())) {
5975 Operation *definingOp = v.getDefiningOp();
5977 return op->
emitOpError(
"shape operand is not compile time resolvable");
5990 auto getRank = [](
const Type type) {
5991 return mlir::cast<mlir::tosa::shapeType>(type).getRank();
5997 for (
auto type : operandTypes) {
5998 if (getRank(type) != rank) {
5999 return op->
emitOpError(
"operands don't have matching ranks");
6002 for (
auto type : resultTypes) {
6003 if (getRank(type) != rank) {
6004 return op->
emitOpError(
"result shape has different rank than operands");
6014LogicalResult tosa::ConstShapeOp::verify() {
6016 auto valuesRank = getValues().getType().getRank();
6017 if (valuesRank != 1)
6018 return emitOpError(
"expect elements in attribute values with rank 1");
6020 auto count = getValues().getNumElements();
6021 auto rank = (cast<tosa::shapeType>(getResult().
getType())).getRank();
6022 if (count != rank && (count != 1 || rank != 0)) {
6023 return emitOpError(
"expect number of elements in attribute values (")
6024 << count <<
") to be equal to the rank (" << rank
6025 <<
") for the result shape type";
6030LogicalResult tosa::DimOp::verify() {
6031 const tosa::shapeType outShapeType =
6032 cast<tosa::shapeType>(getResult().
getType());
6033 if (outShapeType.getRank() != 1)
6034 return emitOpError(
"expect output shape type to contain one element, got ")
6039 const int64_t inputRank = inputType.getRank();
6040 const int64_t axis = getAxisAttr().getInt();
6041 if (axis < 0 || axis >= inputRank)
6042 return emitOpError(
"expect axis to be in the range [0, ")
6043 << inputRank <<
"), got " << axis;
6048LogicalResult tosa::ConcatShapeOp::verify() {
6049 const tosa::shapeType outShapeType =
6050 cast<tosa::shapeType>(getResult().
getType());
6051 const int64_t outputRank = outShapeType.getRank();
6054 if (inputList.size() == 0)
6055 return emitOpError(
"requires at least one input shape");
6057 if (llvm::any_of(inputList, [](Value v) {
6058 return cast<tosa::shapeType>(v.
getType()).getRank() == 0;
6060 return emitOpError(
"requires all inputs shapes have a rank greater than 0");
6062 const int64_t inputsRank =
6063 llvm::accumulate(inputList, 0, [](int64_t acc,
const Value &input) {
6064 const tosa::shapeType inShapeType =
6065 cast<tosa::shapeType>(input.
getType());
6066 return acc + inShapeType.getRank();
6068 if (outputRank != inputsRank)
6069 return emitOpError(
"requires output shape rank to be equal to the sum of "
6070 "the input shape ranks (")
6071 << inputsRank <<
"), got " << outputRank;
6076LogicalResult tosa::SliceShapeOp::verify() {
6077 std::optional<int32_t> start;
6078 DenseIntElementsAttr startAttr;
6080 start = startAttr.getValues<int32_t>()[0];
6081 if (start && start.value() < 0)
6082 return emitOpError(
"expected non-negative start index, got ")
6085 std::optional<int32_t> size;
6086 DenseIntElementsAttr sizeAttr;
6088 size = sizeAttr.getValues<int32_t>()[0];
6089 if (size && size.value() <= 0)
6090 return emitOpError(
"expected positive size, got ") << size.value();
6095 const tosa::shapeType outShapeType =
6096 cast<tosa::shapeType>(getResult().
getType());
6097 const int64_t outputRank = outShapeType.getRank();
6098 if (outputRank != size)
6100 "expected output type size to be equal to size attribute, got ")
6101 << outputRank <<
" vs " << size.value();
6106 const tosa::shapeType inShapeType =
6107 cast<tosa::shapeType>(getInput().
getType());
6108 const int64_t inputRank = inShapeType.getRank();
6109 const int64_t sliceSize = start.value() + size.value();
6110 if (sliceSize > inputRank)
6111 return emitOpError(
"expected start + size to be less than or equal to "
6112 "input shape rank (")
6113 << inputRank <<
"), got " << sliceSize;
6122#define GET_ATTRDEF_CLASSES
6123#include "mlir/Dialect/Tosa/IR/TosaAttributes.cpp.inc"
6128#define GET_TYPEDEF_CLASSES
6129#include "mlir/Dialect/Tosa/IR/TosaOpsTypesBase.cpp.inc"
6135#define GET_OP_CLASSES
6136#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.
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.
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...
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)
LogicalResult verifyBlockScaledTensorType(mlir::Type type, bool allowScaleValues)
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)
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)