30#include "llvm/ADT/APFloat.h"
31#include "llvm/ADT/SmallVectorExtras.h"
32#include "llvm/ADT/TypeSwitch.h"
40#include "mlir/Dialect/Tosa/IR/TosaOpsDialect.cpp.inc"
47#include "mlir/Dialect/Tosa/IR/TosaEnums.cpp.inc"
48#include "mlir/Dialect/Tosa/IR/TosaInterfaces.cpp.inc"
51#include "mlir/Dialect/Tosa/IR/TosaDialectBytecode.cpp.inc"
56struct TosaInlinerInterface :
public DialectInlinerInterface {
57 using DialectInlinerInterface::DialectInlinerInterface;
65 IRMapping &map)
const final {
71 IRMapping &map)
const final {
72 return (isa<tosa::IfOp>(dest->getParentOp()) ||
73 isa<tosa::WhileOp>(dest->getParentOp()));
78struct TosaDialectBytecodeInterface :
public BytecodeDialectInterface {
79 TosaDialectBytecodeInterface(Dialect *dialect)
80 : BytecodeDialectInterface(dialect) {}
85 Attribute readAttribute(DialectBytecodeReader &reader)
const override {
89 LogicalResult writeAttribute(Attribute attr,
90 DialectBytecodeWriter &writer)
const override {
91 return ::writeAttribute(attr, writer);
97 Type readType(DialectBytecodeReader &reader)
const override {
101 LogicalResult writeType(Type type,
102 DialectBytecodeWriter &writer)
const override {
103 return ::writeType(type, writer);
106 void writeVersion(DialectBytecodeWriter &writer)
const final {
110 std::unique_ptr<DialectVersion>
111 readVersion(DialectBytecodeReader &reader)
const final {
113 reader.
emitError(
"Dialect does not support versioning");
117 LogicalResult upgradeFromVersion(Operation *topLevelOp,
118 const DialectVersion &version)
const final {
131 return {&getBodyGraph()};
140 return dim == -1 ? ShapedType::kDynamic : dim;
146 Type elementType = variableOp.getType();
149 return RankedTensorType::get(
shape, elementType);
156void TosaDialect::initialize() {
158#define GET_TYPEDEF_LIST
159#include "mlir/Dialect/Tosa/IR/TosaOpsTypesBase.cpp.inc"
163#include "mlir/Dialect/Tosa/IR/TosaOps.cpp.inc"
166#define GET_ATTRDEF_LIST
167#include "mlir/Dialect/Tosa/IR/TosaAttributes.cpp.inc"
169 addInterfaces<TosaDialectBytecodeInterface, TosaInlinerInterface>();
170 declarePromisedInterfaces<
171 shard::ShardingInterface, ClampOp, SigmoidOp, TanhOp, AddOp,
172 ArithmeticRightShiftOp, BitwiseAndOp, BitwiseOrOp, BitwiseXorOp, IntDivOp,
173 LogicalAndOp, LogicalLeftShiftOp, LogicalRightShiftOp, LogicalOrOp,
174 LogicalXorOp, MaximumOp, MinimumOp, MulOp, PowOp, SubOp, AbsOp,
175 BitwiseNotOp, CeilOp, ClzOp, ExpOp, FloorOp, LogOp, LogicalNotOp,
176 NegateOp, ReciprocalOp, RsqrtOp, SelectOp, EqualOp, GreaterOp,
177 GreaterEqualOp, MatMulOp>();
184 if (llvm::isa<shapeType>(type) && llvm::isa<DenseIntElementsAttr>(value)) {
185 return tosa::ConstShapeOp::create(builder, loc, type,
186 llvm::cast<DenseIntElementsAttr>(value));
188 if (llvm::isa<ElementsAttr>(value))
189 return tosa::ConstOp::create(builder, loc, type,
190 llvm::cast<ElementsAttr>(value));
200ParseResult getShapeAndElementType(
OpAsmParser &parser,
Type parsedType,
202 TypeAttr &typeAttr) {
203 if (
auto shapedType = dyn_cast<ShapedType>(parsedType)) {
204 if (!shapedType.hasRank())
206 <<
"expected ranked type";
208 auto elementType = shapedType.getElementType();
209 typeAttr = TypeAttr::get(elementType);
216 <<
"expected shaped type";
233 <<
"expected attribute";
235 if (
auto typedAttr = dyn_cast<TypedAttr>(initialValueAttr)) {
236 return getShapeAndElementType(parser, typedAttr.getType(), varShapeAttr,
240 <<
"expected Typed attr";
243 initialValueAttr =
nullptr;
247 <<
"expected type after colon";
249 return getShapeAndElementType(parser, parsedType, varShapeAttr, typeAttr);
254 TypeAttr typeAttr,
Attribute initialValueAttr) {
255 bool needsSpace =
false;
256 if (!dyn_cast_or_null<TypedAttr>(initialValueAttr)) {
259 Type elementType = typeAttr.getValue();
260 RankedTensorType tensorType =
262 auto tensorTypeAttr = TypeAttr::get(tensorType);
267 if (initialValueAttr) {
278template <
typename EnumType>
279ParseResult parseAttrEntryWithEnumHandling(
OpAsmParser &parser,
281 llvm::StringRef name;
288 if constexpr (std::is_same_v<EnumType, tosa::RoundingMode>) {
289 if (name ==
"rounding_mode" &&
291 auto sym = symbolizeRoundingMode(kw);
294 <<
"invalid rounding_mode value: " << kw;
295 auto attr = RoundingModeAttr::get(parser.
getContext(), sym.value());
301 if constexpr (std::is_same_v<EnumType, tosa::ResizeMode>) {
303 auto sym = symbolizeResizeMode(kw);
306 <<
"invalid resize mode value: " << kw;
307 auto attr = ResizeModeAttr::get(parser.
getContext(), sym.value());
314 if constexpr (std::is_same_v<EnumType, tosa::NanPropagationMode>) {
316 auto sym = symbolizeNanPropagationMode(kw);
319 <<
"invalid nan_mode value: " << kw;
320 auto attr = NanPropagationModeAttr::get(parser.
getContext(), sym.value());
327 if constexpr (std::is_same_v<EnumType, tosa::BlockSize>) {
329 auto sym = symbolizeBlockSize(kw);
332 <<
"invalid block_size value: " << kw;
333 auto attr = BlockSizeAttr::get(parser.
getContext(), sym.value());
345template <
typename EnumType>
350 [&]() { return parser.parseOperand(operands.emplace_back()); }))
358 if (parseAttrEntryWithEnumHandling<EnumType>(parser, attrs))
375 result.addTypes(fnTy.getResults());
376 result.addAttributes(attrs);
382 parser << namedAttr.
getName().strref() <<
" = ";
384 if (
auto roundingModeAttr = dyn_cast<tosa::RoundingModeAttr>(attr)) {
385 parser << roundingModeAttr.getValue();
386 }
else if (
auto resizeModeAttr = dyn_cast<tosa::ResizeModeAttr>(attr)) {
387 parser << resizeModeAttr.getValue();
388 }
else if (
auto nanPropagationModeAttr =
389 dyn_cast<tosa::NanPropagationModeAttr>(attr)) {
390 parser << nanPropagationModeAttr.getValue();
391 }
else if (
auto blockSizeAttr = dyn_cast<tosa::BlockSizeAttr>(attr)) {
392 parser << blockSizeAttr.getValue();
405 op, [&](StringRef name,
Attribute &attr) { toPrint.append(name, attr); });
407 const auto kDefaultNanValue = NanPropagationMode::PROPAGATE;
408 for (
auto attr : toPrint) {
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 op, [&](StringRef name,
Attribute &attr) { toPrint.append(name, attr); });
438 if (!toPrint.empty()) {
441 printNamedAttr(parser, attr);
453 return parseWithEnumHandling<tosa::RoundingMode>(parser,
result);
457 printWithEnumHandling(parser, *
this);
461 return parseWithEnumHandling<tosa::RoundingMode>(parser,
result);
465 printWithEnumHandling(parser, *
this);
469 return parseWithEnumHandling<tosa::ResizeMode>(parser,
result);
473 printWithEnumHandling(parser, *
this);
477 return parseWithEnumHandling<tosa::NanPropagationMode>(parser,
result);
481 printWithNanPropagationHandling(parser, *
this);
485 return parseWithEnumHandling<tosa::NanPropagationMode>(parser,
result);
489 printWithNanPropagationHandling(parser, *
this);
492ParseResult MaxPool2dAdaptiveOp::parse(
OpAsmParser &parser,
494 return parseWithEnumHandling<tosa::NanPropagationMode>(parser,
result);
498 printWithNanPropagationHandling(parser, *
this);
502 return parseWithEnumHandling<tosa::NanPropagationMode>(parser,
result);
506 printWithNanPropagationHandling(parser, *
this);
510 return parseWithEnumHandling<tosa::NanPropagationMode>(parser,
result);
514 printWithNanPropagationHandling(parser, *
this);
518 return parseWithEnumHandling<tosa::NanPropagationMode>(parser,
result);
522 printWithNanPropagationHandling(parser, *
this);
526 return parseWithEnumHandling<tosa::NanPropagationMode>(parser,
result);
530 printWithNanPropagationHandling(parser, *
this);
534 return parseWithEnumHandling<tosa::NanPropagationMode>(parser,
result);
538 printWithNanPropagationHandling(parser, *
this);
541ParseResult MatmulTBlockScaledOp::parse(
OpAsmParser &parser,
543 return parseWithEnumHandling<tosa::BlockSize>(parser,
result);
547 printWithEnumHandling(parser, *
this);
550ParseResult CastFromBlockScaledOp::parse(
OpAsmParser &parser,
552 return parseWithEnumHandling<tosa::BlockSize>(parser,
result);
555void CastFromBlockScaledOp::print(
OpAsmPrinter &parser) {
556 printWithEnumHandling(parser, *
this);
559ParseResult CastToBlockScaledOp::parse(
OpAsmParser &parser,
561 return parseWithEnumHandling<tosa::BlockSize>(parser,
result);
565 printWithEnumHandling(parser, *
this);
568ParseResult Conv2DBlockScaledOp::parse(
OpAsmParser &parser,
570 return parseWithEnumHandling<tosa::BlockSize>(parser,
result);
574 printWithEnumHandling(parser, *
this);
589 if (
auto quantType = llvm::dyn_cast<mlir::quant::QuantizedType>(srcType))
599 Value valZp, StringRef name) {
604 mlir::isa<IntegerType>(eType) && mlir::isa<IntegerType>(eZpType);
608 if (!bothInts || !sameBitWidth) {
610 <<
"expected " << name <<
" and " << name
611 <<
"_zp to both be integer of the same bitwidth, but got " << eType
612 <<
" vs. " << eZpType;
619 Value src, int32_t val) {
622 const auto padConstType = mlir::RankedTensorType::get({1}, srcType);
623 const auto padConstEType = mlir::RankedTensorType::get({1}, srcElemType);
624 const auto padConstAttr{
625 llvm::isa<FloatType>(srcElemType)
630 return tosa::ConstOp::create(builder, loc, padConstType, padConstAttr);
634 if (
auto blockScaledTy = dyn_cast<tosa::BlockScaledType>(type))
636 if (dyn_cast<tosa::mxint8Type>(type))
645 const StringRef operandName,
646 const StringRef dimName) {
647 if (ShapedType::isDynamic(currDim)) {
650 }
else if (ShapedType::isStatic(newDim) && currDim != newDim) {
652 << dimName <<
" of " << operandName <<
" to match size " << currDim
653 <<
", got " << newDim;
660 auto printDim = [&](
int64_t dim) {
661 if (ShapedType::isDynamic(dim))
667 llvm::interleaveComma(
shape,
diag, printDim);
673 StringRef outputName =
"output") {
674 assert(outputType.hasRank() &&
"expected output type to be ranked");
680 diag << outputName <<
" shape ";
682 diag <<
" to be compatible with inferred shape ";
690 const int64_t stride,
const int64_t dilation,
const llvm::StringRef dimName,
691 const llvm::StringRef dimAxis,
const llvm::StringRef padBeforeName,
692 const llvm::StringRef padAfterName) {
693 if (inputSize == ShapedType::kDynamic || kernelSize == ShapedType::kDynamic)
698 const std::optional<int64_t> calculatedOutSizeMinusOne =
idivCheck(
699 inputSize - 1 + padBefore + padAfter - (kernelSize - 1) * dilation,
701 if (!calculatedOutSizeMinusOne.has_value())
703 << dimName <<
" - 1 + pad_" << padBeforeName <<
" + pad_"
704 << padAfterName <<
" - (kernel_" << dimName <<
" - 1) * dilation_"
705 << dimAxis <<
" to be wholly divisible by stride_" << dimAxis
706 <<
", got (" << inputSize <<
" - 1 + " << padBefore <<
" + "
707 << padAfter <<
" - (" << kernelSize <<
" - 1) * " << dilation
710 const int64_t calculatedOutSize = calculatedOutSizeMinusOne.value() + 1;
711 if (outputSize != ShapedType::kDynamic && calculatedOutSize != outputSize)
713 << dimName <<
" did not match expected: "
714 <<
"calculated=" << calculatedOutSize <<
", expected=" << outputSize;
722size_t mlir::tosa::mxint8Type::getDenseElementBitSize()
const {
return 8; }
725mlir::tosa::mxint8Type::convertToAttribute(
ArrayRef<char> rawData)
const {
726 assert(rawData.size() == 1 &&
"expected 1 byte for tosa.mxint8 element");
727 const auto intType = IntegerType::get(
getContext(), 8);
728 return intType.convertToAttribute(rawData);
731LogicalResult mlir::tosa::mxint8Type::convertFromAttribute(
733 const auto intAttr = dyn_cast<IntegerAttr>(attr);
736 const Type attrType = intAttr.getType();
739 return cast<IntegerType>(attrType).convertFromAttribute(attr,
result);
748 bool allowScaleValues) {
749 const auto tensorType = llvm::cast<ShapedType>(type);
750 const BlockScaledType elemType =
751 llvm::dyn_cast<BlockScaledType>(tensorType.getElementType());
755 if (!allowScaleValues && elemType.hasScaleValues()) {
758 <<
"block scaled tensor type with scale values is not allowed";
762 if (!tensorType.hasRank())
765 if (tensorType.getRank() == 0) {
767 emitError() <<
"block scaled tensor type must have rank greater than "
773 const uint32_t blockSize =
774 BlockShapeAttr::getBlockShapeValue(elemType.getBlockShape());
776 if (allowScaleValues && elemType.hasScaleValues() &&
777 tensorType.hasStaticShape()) {
778 const size_t numBlocks = tensorType.getNumElements() / blockSize;
779 if (elemType.getScaleValues().size() != numBlocks) {
781 emitError() <<
"block scaled tensor type with scale values must have "
782 "scale values for each block, expected "
783 << numBlocks <<
", got "
784 << elemType.getScaleValues().size();
789 const int64_t blockedDimension = tensorShape.back();
790 if (ShapedType::isDynamic(blockedDimension))
793 if (blockedDimension % blockSize != 0) {
795 emitError() <<
"last dimension of block scaled tensor type ("
796 << blockedDimension <<
") must be divisible by block size ("
812 type, [ctx] {
return emitError(UnknownLoc::get(ctx)); })) &&
814 return ": " + message;
823 const auto parseScaleValue = [&]() -> ParseResult {
830 if (floatValue < 0.0)
831 return parser.
emitError(loc,
"scale value must be non-negative, got ")
834 Type attrType = scaleType;
838 if (attrType != scaleType)
839 return parser.
emitError(loc,
"parsed attribute type ")
840 << attrType <<
" does not match expected scale type " << scaleType;
842 scaleValues.push_back(FloatAttr::get(attrType, floatValue));
851 llvm::interleaveComma(scaleValues, printer, [&](
Attribute scaleValue) {
856size_t mlir::tosa::BlockScaledType::getDenseElementBitSize()
const {
858 if (isa<tosa::mxint8Type>(valueType))
864mlir::tosa::BlockScaledType::convertToAttribute(
ArrayRef<char> rawData)
const {
868 assert(rawData.size() == 1 &&
"expected 1 byte for block_scaled element");
870 if (
const auto mxint8Value = dyn_cast<tosa::mxint8Type>(valueType))
871 return mxint8Value.convertToAttribute(rawData);
872 if (!isa<FloatType>(valueType))
877LogicalResult mlir::tosa::BlockScaledType::convertFromAttribute(
880 if (
const auto mxint8Value = dyn_cast<tosa::mxint8Type>(valueType))
881 return mxint8Value.convertFromAttribute(attr,
result);
883 const auto floatAttr = dyn_cast<FloatAttr>(attr);
884 if (!floatAttr || floatAttr.getType() != valueType)
897 const auto inputType = llvm::dyn_cast<TensorType>(op.getInput().getType());
898 const auto weightType = llvm::dyn_cast<TensorType>(op.getWeight().getType());
900 auto inputEType = inputType.getElementType();
901 auto weightEType = weightType.getElementType();
903 llvm::cast<ShapedType>(op.getBias().getType()).getElementType();
905 llvm::cast<ShapedType>(op.getResult().getType()).getElementType();
906 bool biasIsFloat = llvm::isa<FloatType>(biasEType);
907 bool resultIsFloat = llvm::isa<FloatType>(resultEType);
909 if (
auto quantType = llvm::dyn_cast<mlir::quant::QuantizedType>(inputEType))
912 if (
auto quantType = llvm::dyn_cast<mlir::quant::QuantizedType>(weightEType))
915 if (
auto quantType = llvm::dyn_cast<mlir::quant::QuantizedType>(biasEType))
918 if (
auto quantType = llvm::dyn_cast<mlir::quant::QuantizedType>(resultEType))
921 if (biasIsFloat && resultIsFloat && (biasEType != resultEType)) {
925 "expect both bias and result to have same element type, got ")
926 << biasEType <<
" and " << resultEType;
930 const bool isInputBlockScaled = llvm::isa<BlockScaledType>(inputEType);
931 const bool isWeightBlockScaled = llvm::isa<BlockScaledType>(weightEType);
932 const bool isInputFloat = llvm::isa<FloatType>(inputEType);
933 const bool isWeightFloat = llvm::isa<FloatType>(weightEType);
935 const bool isInputBSorFloat = isInputBlockScaled || isInputFloat;
936 const bool isWeightBSorFloat = isWeightBlockScaled || isWeightFloat;
939 if (isInputBSorFloat != isWeightBSorFloat) {
941 "expect both input and weight to be float or not together, got ")
942 << inputEType <<
" and " << weightEType;
947 if (!isInputBlockScaled && inputEType != inputZpEType) {
948 return op.emitOpError(
"expect both input and its zero point are the same "
949 "element type, got ")
950 << inputEType <<
" and " << inputZpEType;
952 if (isInputBlockScaled && !llvm::isa<Float32Type>(inputZpEType)) {
953 return op.emitOpError(
954 "expect block scaled input to have fp32 zero point, got ")
955 << inputEType <<
" and " << inputZpEType;
959 if (!isWeightBlockScaled && weightEType != weightZpEType) {
960 return op.emitOpError(
"expect both weight and its zero point are the same "
961 "element type, got ")
962 << weightEType <<
" and " << weightZpEType;
964 if (isWeightBlockScaled && !llvm::isa<Float32Type>(weightZpEType)) {
965 return op.emitOpError(
966 "expect block scaled weight to have fp32 zero point, got ")
967 << weightEType <<
" and " << weightZpEType;
970 FailureOr<int64_t> maybeIZp = op.getInputZeroPoint();
971 if (succeeded(maybeIZp) && op.verifyInputZeroPoint(*maybeIZp).failed())
974 FailureOr<int64_t> maybeWZp = op.getWeightZeroPoint();
975 if (succeeded(maybeWZp) && op.verifyWeightZeroPoint(*maybeWZp).failed())
981LogicalResult tosa::ConstOp::verify() {
983 auto attrType = llvm::dyn_cast<TensorType>(getValuesAttr().
getType());
984 auto outputType = llvm::dyn_cast<TensorType>(getOutput().
getType());
986 if (!attrType || !outputType) {
987 emitOpError(
"expected tensors for attr/result type");
991 const Type attrElemType = attrType.getElementType();
992 const Type resultElemType = outputType.getElementType();
995 llvm::dyn_cast<mlir::quant::QuantizedType>(resultElemType)) {
1000 if (
auto attrBlockScaledType =
1001 llvm::dyn_cast<mlir::tosa::BlockScaledType>(attrElemType)) {
1002 if (!attrBlockScaledType.hasScaleValues())
1004 "attribute block scaled type must have scale values");
1006 const auto emitAttributeError = [&op]() {
1007 return op.
emitOpError(
"attribute block scaled type is invalid: ");
1013 const BlockScaledType resultBlockScaledType =
1014 llvm::dyn_cast<mlir::tosa::BlockScaledType>(resultElemType);
1015 if (!resultBlockScaledType)
1017 "result type must be block scaled type if attribute is block "
1020 if (attrBlockScaledType.getValueType() !=
1021 resultBlockScaledType.getValueType() ||
1022 attrBlockScaledType.getScaleType() !=
1023 resultBlockScaledType.getScaleType() ||
1024 attrBlockScaledType.getBlockShape() !=
1025 resultBlockScaledType.getBlockShape())
1027 "expected block scaled element type to be compatible "
1028 "between attr and result, got ")
1029 << attrBlockScaledType <<
" vs. " << resultBlockScaledType;
1034 if (attrElemType != resultElemType)
1035 return emitOpError(
"expected same attr/result element types");
1040template <
typename T>
1043 llvm::cast<ShapedType>(op.getInput().getType()).getElementType();
1045 if (
auto quantType = llvm::dyn_cast<mlir::quant::QuantizedType>(inputEType))
1049 llvm::cast<ShapedType>(op.getResult().getType()).getElementType();
1051 if (
auto quantType = llvm::dyn_cast<mlir::quant::QuantizedType>(resultEType))
1062template <
typename T>
1065 if (llvm::any_of(padding, [](
int64_t p) {
return p < 0; }))
1066 return op.emitOpError(
"expect all padding values to be >= 0, got ")
1070 if (llvm::any_of(strides, [](
int64_t s) {
return s < 1; }))
1071 return op.emitOpError(
"expect all stride values to be >= 1, got ")
1075 if (llvm::any_of(dilations, [](
int64_t d) {
return d < 1; }))
1076 return op.emitOpError(
"expect all dilation values to be >= 1, got ")
1079 const RankedTensorType outputType =
1080 llvm::dyn_cast<RankedTensorType>(op.getOutput().getType());
1085 const RankedTensorType inputType =
1086 llvm::dyn_cast<RankedTensorType>(op.getInput().getType());
1087 const RankedTensorType weightType =
1088 llvm::dyn_cast<RankedTensorType>(op.getWeight().getType());
1090 if (inputType && weightType) {
1092 if constexpr (std::is_same<T, tosa::Conv2DOp>::value) {
1094 op, inputType.getDimSize(1), weightType.getDimSize(1),
1095 outputType.getDimSize(1), padding[0], padding[1], strides[0],
1096 dilations[0],
"height",
"y",
"top",
"bottom")))
1100 op, inputType.getDimSize(2), weightType.getDimSize(2),
1101 outputType.getDimSize(2), padding[2], padding[3], strides[1],
1102 dilations[1],
"width",
"x",
"left",
"right")))
1107 if constexpr (std::is_same<T, tosa::DepthwiseConv2DOp>::value) {
1109 op, inputType.getDimSize(1), weightType.getDimSize(0),
1110 outputType.getDimSize(1), padding[0], padding[1], strides[0],
1111 dilations[0],
"height",
"y",
"top",
"bottom")))
1115 op, inputType.getDimSize(2), weightType.getDimSize(1),
1116 outputType.getDimSize(2), padding[2], padding[3], strides[1],
1117 dilations[1],
"width",
"x",
"left",
"right")))
1122 if constexpr (std::is_same<T, tosa::Conv3DOp>::value) {
1124 op, inputType.getDimSize(1), weightType.getDimSize(1),
1125 outputType.getDimSize(1), padding[0], padding[1], strides[0],
1126 dilations[0],
"depth",
"d",
"front",
"back")))
1130 op, inputType.getDimSize(2), weightType.getDimSize(2),
1131 outputType.getDimSize(2), padding[2], padding[3], strides[1],
1132 dilations[1],
"height",
"y",
"top",
"bottom")))
1136 op, inputType.getDimSize(3), weightType.getDimSize(3),
1137 outputType.getDimSize(3), padding[4], padding[5], strides[2],
1138 dilations[2],
"width",
"x",
"left",
"right")))
1143 const RankedTensorType biasType =
1144 llvm::dyn_cast<RankedTensorType>(op.getBias().getType());
1149 const int64_t biasChannels = biasType.getDimSize(0);
1150 const int64_t outputChannels =
1151 outputType.getDimSize(outputType.getRank() - 1);
1152 if (biasChannels == ShapedType::kDynamic ||
1153 outputChannels == ShapedType::kDynamic)
1157 if (biasChannels != outputChannels && biasChannels != 1)
1158 return op.emitOpError(
1159 "bias channels expected to be equal to output channels (")
1160 << outputChannels <<
") or 1, got " << biasChannels;
1167 StringRef name1,
Type type2,
1169 auto shapeType1 = dyn_cast<ShapedType>(type1);
1170 auto shapeType2 = dyn_cast<ShapedType>(type2);
1171 if (!shapeType1 || !shapeType2)
1174 auto elemType1 = shapeType1.getElementType();
1175 auto elemType2 = shapeType2.getElementType();
1176 if (elemType1 != elemType2)
1178 <<
"require same element type for " << name1 <<
" (" << elemType1
1179 <<
") and " << name2 <<
" (" << elemType2 <<
")";
1183 <<
"require same shapes for " << name1 <<
" (" << type1 <<
") and "
1184 << name2 <<
" (" << type2 <<
")";
1194 if (list1.size() != list2.size())
1196 <<
"require same number of values in " << name1 <<
" ("
1197 << list1.size() <<
") and " << name2 <<
" (" << list2.size() <<
")";
1199 for (
auto [type1, type2] :
1216template <
typename T>
1219 op->template getParentWithTrait<OpTrait::SymbolTable>();
1226 const auto varOp = symTable.
lookup<tosa::VariableOp>(op.getName());
1230 return op->emitOpError(
"'")
1231 << op.getName() <<
"' has not been declared by 'tosa.variable'";
1245 StringRef aName =
"input",
1246 StringRef bName =
"output") {
1247 auto aTType = llvm::dyn_cast<TensorType>(aType);
1248 auto bTType = llvm::dyn_cast<TensorType>(bType);
1250 op->
emitOpError(
"expect shaped tensor for") << aName <<
", got " << aType;
1254 op->
emitOpError(
"expect shaped tensor for") << bName <<
", got" << bType;
1257 auto aElementType = aTType.getElementType();
1258 auto bElementType = bTType.getElementType();
1260 llvm::dyn_cast<mlir::quant::UniformQuantizedType>(aElementType);
1262 llvm::dyn_cast<mlir::quant::UniformQuantizedType>(bElementType);
1263 if ((aElementType.isIntOrIndexOrFloat() || aQuantType) &&
1264 (bElementType.isIntOrIndexOrFloat() || bQuantType) &&
1265 aElementType != bElementType) {
1271 << aName <<
" and " << bName <<
" to have same element type, got "
1272 << aElementType <<
" and " << bElementType;
1278LogicalResult tosa::ArgMaxOp::verify() {
1279 const ShapedType resultType = llvm::cast<ShapedType>(
getType());
1282 if (
const auto resultETy = resultType.getElementType();
1283 !resultETy.isIntOrIndex())
1284 return emitOpError(
"result tensor is not of integer type");
1286 const auto inputType = llvm::cast<ShapedType>(getInput().
getType());
1287 if (!inputType.hasRank())
1291 const int64_t axis = getAxisAttr().getInt();
1292 if (((axis < 0) || axis >= inputType.getRank()))
1293 return emitOpError(
"specified axis is outside the rank of the tensor");
1295 if (!resultType.hasRank())
1301 expectedOutputShape.erase(expectedOutputShape.begin() + axis);
1303 return emitOpError(
"expected output shape '")
1304 << expectedOutputShape <<
"', got '" << outputShape <<
"'";
1317 const bool hasKernel = kernel.size() > 0;
1318 const bool hasStrides = strides.size() > 0;
1319 const bool hasPad = padding.size() > 0;
1321 if (hasKernel && llvm::any_of(kernel, [](
int64_t s) {
return s < 1; }))
1322 return op->
emitOpError(
"expect all kernel values to be >= 1, got ")
1325 if (hasStrides && llvm::any_of(strides, [](
int64_t s) {
return s < 1; }))
1326 return op->
emitOpError(
"expect all stride values to be >= 1, got ")
1329 if (hasPad && llvm::any_of(padding, [](
int64_t p) {
return p < 0; }))
1330 return op->
emitOpError(
"expect all padding values to be >= 0, got ")
1333 if (hasKernel && hasPad) {
1335 const int64_t kernelX = kernel[1];
1336 const int64_t padLeft = padding[2];
1337 const int64_t padRight = padding[3];
1338 if (padRight >= kernelX || padLeft >= kernelX)
1339 return op->
emitOpError(
"expected left/right padding to be less than the "
1340 "width of the kernel, got pad_left=")
1341 << padLeft <<
", pad_right=" << padRight
1342 <<
", kernel_x=" << kernelX;
1344 const int64_t kernelY = kernel[0];
1345 const int64_t padTop = padding[0];
1346 const int64_t padBottom = padding[1];
1347 if (padTop >= kernelY || padBottom >= kernelY)
1348 return op->
emitOpError(
"expected top/bottom padding to be less than the "
1349 "height of the kernel, got pad_top=")
1350 << padTop <<
", pad_bottom=" << padBottom
1351 <<
", kernel_y=" << kernelY;
1354 const auto inputType = llvm::dyn_cast<RankedTensorType>(input.
getType());
1355 const auto outputType = llvm::dyn_cast<RankedTensorType>(output.
getType());
1356 if (!inputType || !outputType)
1359 if (hasKernel && hasStrides && hasPad) {
1360 const auto verifyOutputSize =
1364 const llvm::StringRef dimName,
const llvm::StringRef dimAxis,
1365 const llvm::StringRef padBeforeName,
1366 const llvm::StringRef padAfterName) -> LogicalResult {
1367 if (ShapedType::isDynamic(inputSize))
1370 const std::optional<int64_t> calculatedOutSizeMinusOne =
1371 idivCheck(inputSize + padBefore + padAfter - kernelSize, strideSize);
1372 if (!calculatedOutSizeMinusOne.has_value())
1374 << dimName <<
" + pad_" << padBeforeName <<
" + pad_"
1375 << padAfterName <<
" - kernel_" << dimAxis
1376 <<
" to be wholly divisible by stride_" << dimAxis <<
", got ("
1377 << inputSize <<
" + " << padBefore <<
" + " << padAfter <<
" - "
1378 << kernelSize <<
") / " << strideSize;
1380 const int64_t calculatedOutSize = calculatedOutSizeMinusOne.value() + 1;
1381 if (ShapedType::isStatic(outputSize) && calculatedOutSize != outputSize)
1383 << dimName <<
" did not match expected: " <<
"calculated="
1384 << calculatedOutSize <<
", expected=" << outputSize;
1389 if (failed(verifyOutputSize(inputType.getDimSize(1),
1390 outputType.getDimSize(1), kernel[0], strides[0],
1391 padding[0], padding[1],
"height",
"y",
"top",
1395 if (failed(verifyOutputSize(
1396 inputType.getDimSize(2), outputType.getDimSize(2), kernel[1],
1397 strides[1], padding[2], padding[3],
"width",
"x",
"left",
"right")))
1403template <
typename T>
1406 op.getPad(), op.getInput(), op.getOutput());
1409template <
typename T>
1413 const Type inputZpETy =
1415 const Type outputZpETy =
1418 auto accType = op.getAccType();
1419 if (llvm::isa<IntegerType>(inputETy) && !accType.isInteger(32))
1420 return op.emitOpError(
"accumulator type for integer tensor is not i32");
1422 if (inputETy.
isF16() && !(accType.isF16() || accType.isF32()))
1423 return op.emitOpError(
"accumulator type for f16 tensor is not f16/f32");
1425 if (inputETy.
isBF16() && !accType.isF32())
1426 return op.emitOpError(
"accumulator type for bf16 tensor is not f32");
1428 if (inputETy.
isF32() && !accType.isF32())
1429 return op.emitOpError(
"accumulator type for f32 tensor is not f32");
1431 if (inputETy != inputZpETy)
1432 return op.emitOpError(
"expect both input and its zero point are the same "
1433 "element type, got ")
1434 << inputETy <<
" and " << inputZpETy;
1436 if (resultETy != outputZpETy)
1437 return op.emitOpError(
"expect both output and its zero point are the same "
1438 "element type, got ")
1439 << resultETy <<
" and " << outputZpETy;
1441 FailureOr<int64_t> maybeIZp = op.getInputZeroPoint();
1442 if (succeeded(maybeIZp) && op.verifyInputZeroPoint(*maybeIZp).failed())
1445 FailureOr<int64_t> maybeOZp = op.getOutputZeroPoint();
1446 if (succeeded(maybeOZp) && op.verifyOutputZeroPoint(*maybeOZp).failed())
1453struct AdaptivePoolingConstShapeValues {
1454 llvm::SmallVector<int64_t> kernel;
1455 llvm::SmallVector<int64_t> stride;
1456 llvm::SmallVector<int64_t> pad;
1460template <
typename T>
1462 std::is_same_v<T, tosa::AvgPool2dAdaptiveOp> ||
1463 std::is_same_v<T, tosa::MaxPool2dAdaptiveOp>;
1465template <
typename T,
1466 typename std::enable_if<IsSupportedAdaptivePoolConstShapeVerifyOp<T>,
1469 T op, AdaptivePoolingConstShapeValues &values) {
1475LogicalResult tosa::AvgPool2dOp::verify() {
1483LogicalResult tosa::AvgPool2dAdaptiveOp::verify() {
1484 AdaptivePoolingConstShapeValues values;
1493 values.pad, getInput(), getOutput())))
1502LogicalResult tosa::ClampOp::verify() {
1504 llvm::cast<ShapedType>(getInput().
getType()).getElementType();
1505 if (
auto quantType =
1506 llvm::dyn_cast<mlir::quant::UniformQuantizedType>(inputETy)) {
1510 llvm::cast<ShapedType>(getOutput().
getType()).getElementType();
1511 if (
auto quantType =
1512 llvm::dyn_cast<mlir::quant::UniformQuantizedType>(outputETy)) {
1515 if (inputETy != outputETy)
1516 return emitOpError(
"input/output element types are incompatible.");
1518 auto maxValAttr = getMaxValAttr();
1519 auto minValAttr = getMinValAttr();
1523 if (inputETy.
isInteger(dataTypeBitWidth)) {
1527 auto intMaxValAttr = mlir::dyn_cast<mlir::IntegerAttr>(maxValAttr);
1528 auto intMinValAttr = mlir::dyn_cast<mlir::IntegerAttr>(minValAttr);
1529 if (!intMaxValAttr || !intMinValAttr ||
1530 (intMaxValAttr.getType() != intMinValAttr.getType()) ||
1531 (intMaxValAttr.getType() != inputETy))
1532 return emitOpError(
"min/max attributes types are incompatible with "
1533 "input/output element types.");
1536 const bool isBoolean = inputETy.
isInteger(1);
1537 const APInt minVal = intMinValAttr.getValue();
1538 const APInt maxVal = intMaxValAttr.getValue();
1539 if ((isUnsigned || isBoolean) ? maxVal.ult(minVal) : maxVal.slt(minVal))
1540 return emitOpError(
"expected min_val <= max_val, got min_val=")
1541 << minValAttr <<
", max_val=" << maxValAttr;
1546 auto floatMaxValAttr = mlir::dyn_cast<mlir::FloatAttr>(maxValAttr);
1547 auto floatMinValAttr = mlir::dyn_cast<mlir::FloatAttr>(minValAttr);
1548 if (!floatMaxValAttr || !floatMinValAttr ||
1549 (floatMaxValAttr.getType() != floatMinValAttr.getType()) ||
1550 (floatMaxValAttr.getType() != inputETy))
1551 return emitOpError(
"min/max attributes types are incompatible with "
1552 "input/output element types.");
1554 const APFloat minVal = floatMinValAttr.getValue();
1555 const APFloat maxVal = floatMaxValAttr.getValue();
1556 if (minVal.isNaN() || maxVal.isNaN())
1557 return emitOpError(
"min/max attributes should not be 'NaN', got min_val=")
1558 << minValAttr <<
", max_val=" << maxValAttr;
1560 if (maxVal < minVal)
1561 return emitOpError(
"expected min_val <= max_val, got min_val=")
1562 << minValAttr <<
", max_val=" << maxValAttr;
1582 result.addOperands({input, weight, bias, zps.first, zps.second});
1583 result.addAttribute(
"pad", pad);
1584 result.addAttribute(
"stride", stride);
1585 result.addAttribute(
"dilation", dilation);
1586 result.addAttribute(
"acc_type", accType);
1587 Type finalOutputType = outputType;
1593 result.addTypes(finalOutputType);
1604 result.addOperands({input, weight, bias, zps.first, zps.second});
1605 result.addAttribute(
"out_pad", outpad);
1606 result.addAttribute(
"stride", stride);
1607 result.addAttribute(
"acc_type", accType);
1608 Type finalOutputType = outputType;
1614 result.addTypes(finalOutputType);
1621 result.addOperands({a,
b, zps.first, zps.second});
1623 Type finalOutputType{outputType};
1626 auto inputBits = eType.getIntOrFloatBitWidth();
1628 auto outputShapedType = llvm::dyn_cast<ShapedType>(outputType);
1629 assert(outputShapedType &&
"Output must be a shaped type");
1631 IntegerType accElementType;
1632 if (inputBits == 16)
1637 finalOutputType = outputShapedType.clone(accElementType);
1639 result.addTypes(finalOutputType);
1660 DenseArrayAttr kernel, DenseArrayAttr stride,
1661 DenseArrayAttr pad, TypeAttr accType) {
1666 if (
auto quantAttr =
1668 inputZp = quantAttr.getInputZp();
1669 outputZp = quantAttr.getOutputZp();
1671 const std::optional<Value> inputZpOp =
1676 "Failed to create input zero point tensor for quantized AVG_POOL2D op");
1678 const std::optional<Value> outputZpOp =
1681 (
void)
emitError(loc,
"Failed to create output zero point tensor for "
1682 "quantized AVG_POOL2D op");
1685 if (inputZpOp && outputZpOp) {
1686 result.addOperands({input, inputZpOp.value(), outputZpOp.value()});
1691 result.addOperands({input});
1693 result.addAttribute(
"kernel", kernel);
1694 result.addAttribute(
"stride", stride);
1695 result.addAttribute(
"pad", pad);
1696 result.addAttribute(
"acc_type", accType);
1697 result.types.push_back(outputType);
1710 if (
auto quantAttr =
1712 inputZp = quantAttr.getInputZp();
1713 outputZp = quantAttr.getOutputZp();
1715 const std::optional<Value> inputZpOp =
1719 "Failed to create input zero point tensor for quantized "
1720 "AVG_POOL2D_ADAPTIVE op");
1722 const std::optional<Value> outputZpOp =
1725 (
void)
emitError(loc,
"Failed to create output zero point tensor for "
1726 "quantized AVG_POOL2D_ADAPTIVE op");
1729 if (inputZpOp && outputZpOp) {
1734 result.addOperands({input, inputZpOp.value(), outputZpOp.value(),
1735 kernelShape, strideShape, padShape});
1740 result.addOperands({input});
1742 result.addAttribute(
"acc_type", accType);
1743 result.types.push_back(outputType);
1757 input1Zp = quantAttr.getInputZp();
1758 outputZp = quantAttr.getOutputZp();
1760 const std::optional<Value> input1ZpOp =
1764 loc,
"Failed to create input1 zero point for quantized NEGATE op");
1767 const std::optional<Value> outputZpOp =
1771 loc,
"Failed to create output zero point for quantized NEGATE op");
1774 if (input1ZpOp && outputZpOp) {
1775 result.addOperands({input, input1ZpOp.value(), outputZpOp.value()});
1780 result.addOperands({input});
1783 result.types.push_back(outputType);
1796 zp =
static_cast<int32_t
>(quantAttr.getInputZp());
1799 result.addOperands({input, paddings, padConstOp});
1800 result.types.push_back(outputType);
1804 StringRef name,
Type variableType,
1809 auto shapedType = dyn_cast<ShapedType>(variableType);
1811 (
void)
emitError(loc,
"variable type must be a shaped type");
1814 if (!shapedType.hasRank()) {
1815 (
void)
emitError(loc,
"variable type must be a ranked type");
1819 auto elementType = shapedType.getElementType();
1820 auto elementTypeAttr = TypeAttr::get(elementType);
1824 result.addAttribute(
"sym_name", nameAttr);
1825 result.addAttribute(
"var_shape", varShapeAttr);
1826 result.addAttribute(
"type", elementTypeAttr);
1827 result.addAttribute(
"initial_value", initialValue);
1840 if (ShapedType::isStatic(dim1) && ShapedType::isStatic(dim2) && dim1 != dim2)
1844 return ShapedType::isDynamic(dim1) ? dim2 : dim1;
1850 for (
int i = 0, e = operands.size(); i != e; ++i) {
1852 if (!
shape.hasRank()) {
1857 outRank = std::max<int64_t>(outRank,
shape.getRank());
1860 outShape.resize(outRank, 1);
1862 for (
int i = 0, e = operands.size(); i != e; ++i) {
1864 auto rankDiff = outShape.size() -
shape.getRank();
1866 for (
size_t i = 0, e =
shape.getRank(); i < e; ++i) {
1867 auto dim1 = outShape[i + rankDiff];
1868 auto dim2 =
shape.getDimSize(i);
1870 const FailureOr<int64_t> maybeResolvedDim =
1872 if (failed(maybeResolvedDim))
1874 const int64_t resolvedDim = *maybeResolvedDim;
1875 outShape[i + rankDiff] = resolvedDim;
1882LogicalResult tosa::ArgMaxOp::inferReturnTypeComponents(
1883 MLIRContext *context, ::std::optional<Location> location,
1884 ArgMaxOp::Adaptor adaptor,
1887 IntegerAttr axis = adaptor.getProperties().axis;
1888 int32_t axisVal = axis.getValue().getSExtValue();
1890 if (!inputShape.hasRank()) {
1896 outShape.reserve(inputShape.getRank() - 1);
1897 for (
int i = 0, s = inputShape.getRank(); i < s; i++) {
1900 outShape.push_back(inputShape.getDimSize(i));
1907LogicalResult tosa::RFFT2dOp::inferReturnTypeComponents(
1908 MLIRContext *context, ::std::optional<Location> location,
1909 RFFT2dOp::Adaptor adaptor,
1911 ShapeAdaptor inputShape(adaptor.getInputReal().getType());
1913 if (!inputShape.hasRank())
1917 outputShape.resize(3, ShapedType::kDynamic);
1918 outputShape[0] = inputShape.getDimSize(0);
1919 outputShape[1] = inputShape.getDimSize(1);
1920 int64_t inWidth = inputShape.getDimSize(2);
1924 if (inWidth != ShapedType::kDynamic)
1925 outputShape[2] = inWidth / 2 + 1;
1934 const llvm::StringRef dimName) {
1935 const bool isPowerOfTwo = (dimSize & (dimSize - 1)) == 0 && dimSize > 0;
1938 << dimName <<
" to be a power of two, got " << dimSize;
1943LogicalResult tosa::RFFT2dOp::verify() {
1944 const auto outputTypes = getResultTypes();
1946 return emitOpError(
"expected output shapes to match, got ") << outputTypes;
1948 const auto inputType =
1949 llvm::dyn_cast<RankedTensorType>(getInputReal().
getType());
1953 const int64_t height = inputType.getDimSize(1);
1954 if (ShapedType::isStatic(height) &&
1958 const int64_t width = inputType.getDimSize(2);
1959 if (ShapedType::isStatic(width) &&
1963 const auto outputType = llvm::dyn_cast<RankedTensorType>(outputTypes[0]);
1969 outputType.getShape().drop_back())))
1970 return emitOpError(
"expected batch and height dimensions of input/output "
1971 "to match, got input=")
1972 << inputType <<
" output=" << outputType;
1975 const int64_t outputWidth = outputType.getDimSize(2);
1976 if (ShapedType::isStatic(width) && ShapedType::isStatic(outputWidth) &&
1977 (outputWidth != (width / 2) + 1))
1979 "expected output width to be equal to input_width / 2 + 1, got ")
1985LogicalResult tosa::FFT2dOp::inferReturnTypeComponents(
1986 MLIRContext *context, ::std::optional<Location> location,
1987 FFT2dOp::Adaptor adaptor,
1989 inferredReturnShapes.push_back(
1991 inferredReturnShapes.push_back(
1996LogicalResult tosa::FFT2dOp::verify() {
1997 const auto inputRealType =
1998 llvm::dyn_cast<RankedTensorType>(getInputReal().
getType());
1999 const auto inputImagType =
2000 llvm::dyn_cast<RankedTensorType>(getInputImag().
getType());
2001 if (!inputRealType || !inputImagType)
2004 const auto trySelectStaticDim = [](
const int64_t a,
const int64_t b) {
2005 return ShapedType::isDynamic(a) ? a :
b;
2008 const int64_t height = trySelectStaticDim(inputRealType.getDimSize(1),
2009 inputImagType.getDimSize(1));
2010 if (ShapedType::isStatic(height) &&
2014 const int64_t width = trySelectStaticDim(inputRealType.getDimSize(2),
2015 inputImagType.getDimSize(2));
2016 if (ShapedType::isStatic(width) &&
2023LogicalResult tosa::ConcatOp::inferReturnTypeComponents(
2024 MLIRContext *context, ::std::optional<Location> location,
2025 ConcatOp::Adaptor adaptor,
2028 const Properties &prop = adaptor.getProperties();
2029 int32_t axis = prop.axis.getValue().getSExtValue();
2031 bool hasRankedInput =
false;
2032 for (
auto operand : adaptor.getOperands()) {
2034 if (!operandShape.hasRank())
2038 if (!hasRankedInput)
2039 outputShape.resize(operandShape.getRank(), ShapedType::kDynamic);
2042 for (
int i = 0, s = operandShape.getRank(); i < s; i++) {
2043 if (i == axis || operandShape.isDynamicDim(i))
2045 if (outputShape[i] == ShapedType::kDynamic)
2046 outputShape[i] = operandShape.getDimSize(i);
2047 if (outputShape[i] != operandShape.getDimSize(i))
2049 "Cannot concat tensors with different sizes"
2050 " on the non-axis dimension ",
2054 hasRankedInput =
true;
2057 if (adaptor.getInput1().empty())
2061 llvm::cast<TensorType>(adaptor.getInput1().getType()[0]).getElementType();
2062 if (!hasRankedInput) {
2069 for (
auto operand : adaptor.getOperands()) {
2074 if (!operandShape.hasRank() || operandShape.isDynamicDim(axis)) {
2075 concatDimSize = ShapedType::kDynamic;
2079 concatDimSize += operandShape.getDimSize(axis);
2082 outputShape[axis] = concatDimSize;
2088LogicalResult tosa::ConcatOp::verify() {
2090 auto outType = getOutput().getType();
2094 if (inputList.empty())
2095 return emitOpError(
"expect at least one input");
2097 if (!llvm::all_of(inputList, [&](
auto input) {
2099 *
this, input.getType(), outType));
2104 const int32_t axis = getAxis();
2106 for (
const auto &input : inputList) {
2107 const Type inputType = input.getType();
2109 if (currShape.hasRank()) {
2110 firstRankedInputShape = currShape;
2112 if (axis < 0 || axis >= firstRankedInputShape.
getRank())
2113 return emitOpError(
"expect axis to be within range 0 < axis < "
2114 "rank(input1[firstRankedTensorIdx]), got ")
2120 const auto allOperandsHasRank = [](
const Value input) {
2123 if (llvm::all_of(inputList, allOperandsHasRank)) {
2126 for (
const auto &[
index, input] : llvm::enumerate(inputList.drop_front())) {
2128 const int64_t inputRank = inputShape.getRank();
2129 const size_t operandNum =
index + 1;
2132 if (inputRank != firstInputRank)
2134 "expect all operands to have the same rank, but got ")
2135 << firstInputRank <<
" vs " << inputRank <<
" on operands 0 and "
2139 for (
int i = 0; i < inputRank; i++) {
2140 const int64_t inputDim = inputShape.getDimSize(i);
2142 if (i == axis || firstRankedInputShape.
isDynamicDim(i) ||
2143 inputShape.isDynamicDim(i))
2145 if (inputDim != firstInputDim)
2146 return emitOpError(
"expect all operand shapes to have the same sizes "
2147 "on non-axis dimensions, but got ")
2148 << inputDim <<
" vs " << firstInputDim <<
" at index " << i
2149 <<
" on operands 0 and " << operandNum;
2154 if (outputShape.hasRank() && outputShape.getRank() != firstInputRank)
2155 return emitOpError(
"expect output rank to match inputs rank, got ")
2156 << outputShape.getRank() <<
" vs " << firstInputRank;
2160 for (
const auto &input : inputList) {
2162 if (inputShape.isDynamicDim(axis)) {
2167 axisSum += inputShape.getDimSize(axis);
2170 if (axisSum >= 0 && outputShape.hasRank() &&
2171 !outputShape.isDynamicDim(axis) &&
2172 axisSum != outputShape.getDimSize(axis))
2173 return emitOpError(
"requires sum of axis dimensions of input1 "
2174 "equal to output axis dimension, got ")
2175 << axisSum <<
" and " << outputShape.getDimSize(axis);
2181LogicalResult tosa::EqualOp::inferReturnTypeComponents(
2182 MLIRContext *context, ::std::optional<Location> location,
2186 auto elementType = IntegerType::get(context, 1);
2199 if (l.size() != r.size() || l.size() != 1)
2204LogicalResult tosa::MatMulOp::inferReturnTypeComponents(
2205 MLIRContext *context, ::std::optional<Location> location,
2206 MatMulOp::Adaptor adaptor,
2213 outShape.resize(3, ShapedType::kDynamic);
2215 if (lhsShape.hasRank()) {
2216 outShape[0] = lhsShape.getDimSize(0);
2217 outShape[1] = lhsShape.getDimSize(1);
2220 if (rhsShape.hasRank()) {
2221 outShape[0] = outShape[0] == ShapedType::kDynamic ? rhsShape.getDimSize(0)
2223 outShape[2] = rhsShape.getDimSize(2);
2230template <
typename T>
2232 Type bElementType) {
2233 const auto aQuantizedEType =
2234 llvm::dyn_cast<quant::UniformQuantizedType>(aElementType);
2235 const auto bQuantizedEType =
2236 llvm::dyn_cast<quant::UniformQuantizedType>(bElementType);
2238 if (aQuantizedEType || bQuantizedEType) {
2239 if (!aQuantizedEType || !bQuantizedEType) {
2240 return op.emitOpError(
"expect operands to be both quantized or both not "
2242 << aElementType <<
" and " << bElementType;
2245 auto aQuantWidth = aQuantizedEType.getStorageTypeIntegralWidth();
2246 auto bQuantWidth = bQuantizedEType.getStorageTypeIntegralWidth();
2247 if (aQuantWidth != bQuantWidth) {
2248 return op.emitOpError(
"expect quantized operands to have same widths, "
2250 << aQuantWidth <<
" and " << bQuantWidth;
2257template <
typename T>
2259 StringRef inputName,
2264 Type expectedElementType = inputStorageElementType;
2266 if (isa<BlockScaledType>(inputElementType))
2267 expectedElementType = Float32Type::get(op.getContext());
2269 if (expectedElementType == zpElementType)
2273 diag << inputName <<
" and " << zpName;
2274 if (isa<BlockScaledType>(inputElementType))
2275 diag <<
" have compatible element types, got " << inputElementType
2276 <<
" and " << zpElementType;
2278 diag <<
" have the same element type, got " << inputStorageElementType
2279 <<
" and " << zpElementType;
2283LogicalResult MatMulOp::verify() {
2286 const Type aElementType = aShape.getElementType();
2287 const Type bElementType = bShape.getElementType();
2297 FailureOr<int64_t> maybeAZp = getAZeroPoint();
2298 if (succeeded(maybeAZp) && verifyAZeroPoint(*maybeAZp).failed())
2301 FailureOr<int64_t> maybeBZp = getBZeroPoint();
2302 if (succeeded(maybeBZp) && verifyBZeroPoint(*maybeBZp).failed())
2306 int64_t N = ShapedType::kDynamic;
2307 int64_t H = ShapedType::kDynamic;
2311 if (aShape.hasRank()) {
2312 N = aShape.getDimSize(0);
2313 H = aShape.getDimSize(1);
2314 C = aShape.getDimSize(2);
2317 if (bShape.hasRank()) {
2323 W = bShape.getDimSize(2);
2327 const auto outputType = cast<ShapedType>(getResult().
getType());
2328 if (outputType.hasRank() &&
2330 expectedOutputShape)))
2336LogicalResult tosa::MatMulTOp::inferReturnTypeComponents(
2337 MLIRContext *context, ::std::optional<Location> location,
2338 MatMulTOp::Adaptor adaptor,
2345 if (lhsShape.hasRank()) {
2346 outShape[0] = lhsShape.getDimSize(0);
2347 outShape[1] = lhsShape.getDimSize(1);
2350 if (rhsShape.hasRank()) {
2351 const int64_t bBatchSize = rhsShape.getDimSize(0);
2352 if (bBatchSize != 1 && ShapedType::isDynamic(outShape[0]))
2353 outShape[0] = bBatchSize;
2354 outShape[2] = rhsShape.getDimSize(1);
2361LogicalResult MatMulTOp::verify() {
2364 const Type aElementType = aShape.getElementType();
2365 const Type bElementType = bShape.getElementType();
2375 FailureOr<int64_t> maybeAZp = getAZeroPoint();
2376 if (succeeded(maybeAZp) && verifyAZeroPoint(*maybeAZp).failed())
2379 FailureOr<int64_t> maybeBZp = getBZeroPoint();
2380 if (succeeded(maybeBZp) && verifyBZeroPoint(*maybeBZp).failed())
2384 int64_t N = ShapedType::kDynamic;
2385 int64_t D = ShapedType::kDynamic;
2386 int64_t H = ShapedType::kDynamic;
2390 if (aShape.hasRank()) {
2391 N = aShape.getDimSize(0);
2392 H = aShape.getDimSize(1);
2393 C = aShape.getDimSize(2);
2396 if (bShape.hasRank()) {
2397 D = bShape.getDimSize(0);
2398 W = bShape.getDimSize(1);
2405 if (ShapedType::isStatic(N) && ShapedType::isStatic(D) && N != D && D != 1)
2406 return emitOpError(
"expect B matrix batch size to be broadcast compatible "
2408 << D <<
" vs N=" << N;
2410 if (ShapedType::isDynamic(N) && ShapedType::isStatic(D) && D != 1)
2414 const auto outputType = cast<ShapedType>(getResult().
getType());
2415 if (outputType.hasRank() &&
2417 expectedOutputShape)))
2423LogicalResult tosa::MatmulTBlockScaledOp::inferReturnTypeComponents(
2424 MLIRContext *context, ::std::optional<Location> location,
2425 MatmulTBlockScaledOp::Adaptor adaptor,
2429 const auto aDataShape = cast<ShapedType>(adaptor.getAData().getType());
2430 if (aDataShape.hasRank()) {
2431 outShape[0] = aDataShape.getDimSize(0);
2432 outShape[1] = aDataShape.getDimSize(1);
2435 const auto aScaleShape = cast<ShapedType>(adaptor.getAScale().getType());
2436 if (aScaleShape.hasRank()) {
2437 outShape[0] = ShapedType::isDynamic(outShape[0]) ? aScaleShape.getDimSize(0)
2439 outShape[1] = ShapedType::isDynamic(outShape[1]) ? aScaleShape.getDimSize(1)
2444 const auto bDataShape = cast<ShapedType>(adaptor.getBData().getType());
2445 if (bDataShape.hasRank()) {
2446 const int64_t bDataBatchSize = bDataShape.getDimSize(0);
2447 if (bDataBatchSize != 1)
2449 ShapedType::isDynamic(outShape[0]) ? bDataBatchSize : outShape[0];
2450 outShape[2] = bDataShape.getDimSize(1);
2453 const auto bScaleShape = cast<ShapedType>(adaptor.getBScale().getType());
2454 if (bScaleShape.hasRank()) {
2455 const int64_t bScaleBatchSize = bScaleShape.getDimSize(0);
2456 if (bScaleBatchSize != 1)
2458 ShapedType::isDynamic(outShape[0]) ? bScaleBatchSize : outShape[0];
2459 outShape[2] = ShapedType::isDynamic(outShape[2]) ? bScaleShape.getDimSize(1)
2467LogicalResult MatmulTBlockScaledOp::verify() {
2469 const Type aDataType = getAData().getType();
2470 const Type bDataType = getBData().getType();
2476 int64_t N = ShapedType::kDynamic;
2477 int64_t D = ShapedType::kDynamic;
2478 int64_t H = ShapedType::kDynamic;
2481 int64_t multiplesOfC = ShapedType::kDynamic;
2493 "a_scale",
"batch")) ||
2495 "a_scale",
"height")))
2503 "b_data",
"batch")) ||
2505 "b_data",
"channels")))
2513 "b_scale",
"batch")) ||
2515 "b_scale",
"width")) ||
2523 if (ShapedType::isStatic(N) && ShapedType::isStatic(D) && N != D && D != 1)
2524 return emitOpError(
"expect B matrix batch size to be broadcast compatible "
2526 << D <<
" vs N=" << N;
2529 const uint32_t blockSize = BlockSizeAttr::getBlockSizeValue(
getBlockSize());
2530 if (blockSize != BlockSizeAttr::getBlockSizeValue(BlockSize::BLOCK_SIZE_32))
2531 return emitOpError(
"expect block size to be 32, got ") << blockSize;
2532 if (ShapedType::isStatic(C) && C % blockSize != 0)
2533 return emitOpError(
"expect C to be a multiple of block size, got C=")
2534 <<
C <<
", block_size=" << blockSize;
2537 if (ShapedType::isStatic(C) && ShapedType::isStatic(multiplesOfC) &&
2538 multiplesOfC != C / blockSize)
2540 "expect scale operands dimension 2 to equal C/block_size (")
2541 <<
C <<
"/" << blockSize <<
")" <<
", got " << multiplesOfC;
2544 N = ShapedType::isDynamic(N) ? D : N;
2546 const auto outputType = cast<ShapedType>(getResult().
getType());
2547 if (outputType.hasRank() &&
2552 opError <<
" to be compatible with expected output shape ";
2560LogicalResult tosa::PadOp::inferReturnTypeComponents(
2561 MLIRContext *context, ::std::optional<Location> location,
2562 PadOp::Adaptor adaptor,
2564 ShapeAdaptor inputShape(adaptor.getInput1().getType());
2566 cast<tosa::shapeType>(adaptor.getPadding().getType()).getRank();
2571 if (!inputShape.hasRank()) {
2572 outputShape.resize(paddingRank / 2, ShapedType::kDynamic);
2581 outputShape.resize(inputShape.getRank(), ShapedType::kDynamic);
2586 outputShape.reserve(inputShape.getRank());
2587 for (
int i = 0, s = inputShape.getRank(); i < s; i++) {
2588 if (inputShape.isDynamicDim(i)) {
2589 outputShape.push_back(ShapedType::kDynamic);
2592 auto padFront = paddingValues[i * 2];
2593 auto padBack = paddingValues[i * 2 + 1];
2594 if (padFront < 0 || padBack < 0) {
2596 outputShape.push_back(ShapedType::kDynamic);
2600 outputShape.push_back(inputShape.getDimSize(i) + padFront + padBack);
2607LogicalResult tosa::PadOp::verify() {
2614 if (
auto padConst = getPadConst()) {
2622 RankedTensorType inputType =
2623 llvm::dyn_cast<RankedTensorType>(getInput1().
getType());
2624 RankedTensorType outputType =
2625 llvm::dyn_cast<RankedTensorType>(getOutput().
getType());
2626 if (!inputType || !outputType)
2633 auto inputRank = inputType.getRank();
2638 auto paddingValues = paddingAttr.getValues<APInt>();
2639 if (paddingValues.size() !=
static_cast<size_t>(inputRank * 2))
2640 return emitOpError() <<
"padding tensor must have " << inputRank
2641 <<
" * 2 = " << inputRank * 2 <<
" elements, but got "
2642 << paddingValues.size();
2644 auto inputShape = inputType.getShape();
2645 auto outputShape = outputType.getShape();
2647 for (
int64_t i = 0; i < inputRank; ++i) {
2648 int64_t padStart = paddingValues[i * 2].getSExtValue();
2649 int64_t padEnd = paddingValues[i * 2 + 1].getSExtValue();
2651 if ((padStart < 0 && padStart != -1) || (padEnd < 0 && padEnd != -1)) {
2652 return emitOpError()
2653 <<
"invalid padding values at dimension " << i
2654 <<
": values must be non-negative or -1 for dynamic padding, got ["
2655 << padStart <<
", " << padEnd <<
"]";
2659 if (inputShape[i] == ShapedType::kDynamic ||
2660 outputShape[i] == ShapedType::kDynamic)
2663 if (outputShape[i] != inputShape[i] + padStart + padEnd) {
2664 return emitOpError() <<
"mismatch in output shape at dimension " << i
2665 <<
": expected " << inputShape[i] <<
" + "
2666 << padStart <<
" + " << padEnd <<
" = "
2667 << (inputShape[i] + padStart + padEnd)
2668 <<
", but got " << outputShape[i];
2675LogicalResult tosa::SliceOp::inferReturnTypeComponents(
2676 MLIRContext *context, ::std::optional<Location> location,
2677 SliceOp::Adaptor adaptor,
2686 auto rank = cast<tosa::shapeType>(adaptor.getSize().getType()).getRank();
2694 ShapeAdaptor inputShape(adaptor.getInput1().getType());
2697 if (inputShape.hasRank()) {
2698 for (
size_t i = 0; i < size.size(); i++) {
2699 if (size[i] != 0 && size[i] >= -1 && start[i] >= 0 &&
2700 (ShapedType::isDynamic(inputShape.getDimSize(i)) ||
2701 start[i] < inputShape.getDimSize(i))) {
2703 if (ShapedType::isDynamic(inputShape.getDimSize(i))) {
2706 outputShape[i] = size[i];
2710 if (size[i] == -1) {
2711 outputShape[i] = inputShape.getDimSize(i) - start[i];
2712 }
else if (start[i] + size[i] <= inputShape.getDimSize(i)) {
2714 outputShape[i] = size[i];
2726LogicalResult tosa::SliceOp::verify() {
2727 const Value input = getInput1();
2728 const Value output = getOutput();
2734 const Value start = getStart();
2735 const Value size = getSize();
2739 if (inputShape.hasRank()) {
2740 const auto inputRank = inputShape.getRank();
2741 if (outputShape.hasRank() && inputRank != outputShape.getRank())
2743 "expect input1 and output to have the same ranks, got ")
2744 << inputRank <<
" and " << outputShape.getRank();
2746 const auto startShapeRank =
2747 llvm::cast<tosa::shapeType>(start.
getType()).getRank();
2748 if (inputRank != startShapeRank)
2749 return emitOpError(
"length of start is not equal to rank of input shape");
2751 const auto sizeShapeRank =
2752 llvm::cast<tosa::shapeType>(size.
getType()).getRank();
2753 if (inputRank != sizeShapeRank)
2754 return emitOpError(
"length of size is not equal to rank of input shape");
2759 if (startValues.size()) {
2760 if (llvm::any_of(startValues, [](
const int64_t v) {
2763 return emitOpError(
"start values must be non-negative, got [")
2764 << startValues <<
"]";
2771 if (llvm::any_of(sizeValues, [](
const int64_t v) {
2774 return emitOpError(
"size values must be > 0, got [") << sizeValues <<
"]";
2775 if (outputShape.hasRank()) {
2777 outputShape.getDims(outputDims);
2778 const bool hasNoInferableDims = llvm::all_of(
2780 if (hasNoInferableDims &&
2782 return emitOpError(
"expected output shape to match size values, got ")
2783 << output.
getType() <<
" vs [" << sizeValues <<
"]";
2786 if (inputShape.hasRank() && startValues.size()) {
2788 inputShape.getDims(inputDims);
2789 for (
const auto &[
index, vals] :
2790 llvm::enumerate(llvm::zip_equal(startValues, sizeValues, inputDims))) {
2791 const auto &[start, size, inputDim] = vals;
2793 ShapedType::isDynamic(inputDim))
2795 if (start + size > inputDim)
2796 return emitOpError(
"start + size must be less than or equal to input "
2797 "dimension size, got start=")
2798 << start <<
", size=" << size
2799 <<
" vs input dim size=" << inputDim <<
" at dimension "
2807LogicalResult tosa::MulOp::inferReturnTypeComponents(
2808 MLIRContext *context, ::std::optional<Location> location,
2823LogicalResult tosa::MulOp::verify() {
2824 const Value output = getOutput();
2829 if (
auto resIntType = dyn_cast<IntegerType>(resElemType)) {
2830 IntegerType lhsIntType =
2832 IntegerType rhsIntType =
2834 if (!lhsIntType || !rhsIntType || lhsIntType != rhsIntType)
2835 return emitOpError(
"requires the same element type for all operands");
2840 if (lhsIntType.getWidth() > resIntType.getWidth())
2841 return emitOpError(
"invalid data type size for operands or result");
2846 for (
int i = 0; i < 2; ++i) {
2849 "requires the same element type for all operands and results");
2853 ElementsAttr shiftElem;
2855 int32_t shift = shiftElem.getValues<IntegerAttr>()[0].getInt();
2857 return emitOpError() <<
"require shift to be 0 for float type";
2865 TypeRange operandTypes = getOperandTypes();
2866 ShapedType aType = cast<ShapedType>(operandTypes[0]);
2867 ShapedType bType = cast<ShapedType>(operandTypes[1]);
2869 const bool aHasRank = aType.hasRank();
2870 const bool bHasRank = bType.hasRank();
2872 bool hasExpectedOutputShape =
false;
2875 if (aHasRank && bHasRank) {
2876 const int64_t aRank = aType.getRank();
2877 const int64_t bRank = bType.getRank();
2879 return emitOpError(
"a and b operands don't have matching ranks, got ")
2880 << aRank <<
" and " << bRank;
2884 aType.getShape(), bType.getShape(), expectedOutputShape))
2885 return emitOpError(
"a and b operands don't have broadcast-compatible "
2887 << aType <<
" and " << bType;
2888 hasExpectedOutputShape =
true;
2891 ShapedType resultType = cast<ShapedType>(output.
getType());
2892 if (!resultType.hasRank())
2895 const int64_t resultRank = resultType.getRank();
2896 if (aHasRank && resultRank != aType.getRank())
2897 return emitOpError(
"result type has different rank than a, got ")
2898 << resultRank <<
" vs " << aType.getRank();
2899 if (bHasRank && resultRank != bType.getRank())
2900 return emitOpError(
"result type has different rank than b, got ")
2901 << resultRank <<
" vs " << bType.getRank();
2903 if (hasExpectedOutputShape &&
2905 expectedOutputShape)))
2911LogicalResult tosa::TableOp::inferReturnTypeComponents(
2912 MLIRContext *context, ::std::optional<Location> location,
2913 TableOp::Adaptor adaptor,
2915 ShapeAdaptor inputShape(adaptor.getInput1().getType());
2917 if (!inputShape.hasRank()) {
2922 inferredReturnShapes.resize(1);
2923 inputShape.getDims(inferredReturnShapes[0]);
2927LogicalResult tosa::TableOp::verify() {
2928 const TensorType inputType = getInput1().getType();
2929 const TensorType outputType = getOutput().getType();
2938 auto inputDims = inputType.
getShape();
2939 auto outputDims = outputType.
getShape();
2940 for (
auto it : llvm::enumerate(llvm::zip(inputDims, outputDims))) {
2942 auto [inputDim, outputDim] = it.value();
2943 if (ShapedType::isStatic(outputDim) && outputDim != inputDim) {
2944 return emitOpError() <<
"dim(result, " << dim <<
") = " << outputDim
2945 <<
" doesn't match dim(input, " << dim
2946 <<
") = " << inputDim;
2959 llvm::map_to_vector(multiplesAttr.getValues<APInt>(),
2960 [](
const APInt &val) { return val.getSExtValue(); });
2964LogicalResult tosa::TileOp::inferReturnTypeComponents(
2965 MLIRContext *context, ::std::optional<Location> location,
2966 TileOp::Adaptor adaptor,
2973 cast<tosa::shapeType>(adaptor.getMultiples().getType()).getRank();
2980 ShapeAdaptor inputShape(adaptor.getInput1().getType());
2982 if (!inputShape.hasRank()) {
2983 outputShape.resize(multiples.size(), ShapedType::kDynamic);
2984 inferredReturnShapes.push_back(
2988 if (
static_cast<size_t>(inputShape.getRank()) != multiples.size())
2992 outputShape.reserve(multiples.size());
2993 for (
int i = 0, s = inputShape.getRank(); i < s; i++) {
2994 if (multiples[i] == ShapedType::kDynamic) {
2995 outputShape.push_back(ShapedType::kDynamic);
2997 int64_t dim = inputShape.getDimSize(i);
2998 if (dim != ShapedType::kDynamic)
2999 dim *= multiples[i];
3000 outputShape.push_back(dim);
3008LogicalResult tosa::TileOp::verify() {
3014 ShapedType inputType = llvm::cast<ShapedType>(getInput1().
getType());
3015 ShapedType outputType = llvm::cast<ShapedType>(
getType());
3017 shapeType multiplesType =
3018 llvm::cast<tosa::shapeType>(getMultiples().
getType());
3020 auto multiplesRank = multiplesType.getRank();
3022 if (inputType.hasRank()) {
3023 if (inputType.getRank() != multiplesRank)
3024 return emitOpError(
"expect 'multiples' to have rank ")
3025 << inputType.getRank() <<
" but got " << multiplesRank <<
".";
3026 if (outputType.hasRank() &&
3030 }
else if (outputType.hasRank() && outputType.getRank() != multiplesRank)
3031 return emitOpError(
"expect 'multiples' array to have length ")
3032 << outputType.getRank() <<
" but got " << multiplesRank <<
".";
3035 if (getConstantMultiples(multiples).succeeded() &&
3036 llvm::any_of(multiples, [](
int64_t v) {
return v <= 0 && v != -1; }))
3038 "expect element of 'multiples' to be positive integer or -1.");
3044 if (l.size() != r.size() || l.size() != 1)
3049LogicalResult tosa::ReshapeOp::inferReturnTypeComponents(
3050 MLIRContext *context, ::std::optional<Location> location,
3051 ReshapeOp::Adaptor adaptor,
3053 ShapeAdaptor inputShape(adaptor.getInput1().getType());
3058 auto rank = cast<tosa::shapeType>(adaptor.getShape().getType()).getRank();
3067 if (!inputShape.hasRank() || !inputShape.hasStaticShape()) {
3068 inferredReturnShapes.push_back(
3076 int64_t numElements = inputShape.getNumElements();
3078 for (
auto val : newShapeValue) {
3079 if (ShapedType::isStatic(val)) {
3085 for (
auto &val : newShapeValue) {
3086 if (ShapedType::isDynamic(val))
3087 val = numElements / staticMul;
3090 inferredReturnShapes.push_back(
3095llvm::LogicalResult tosa::ReshapeOp::verify() {
3101 TensorType inputType = getInput1().getType();
3106 return mlir::success();
3110 if (missingDims > 1)
3111 return emitOpError() <<
"expected at most one target dimension to be "
3114 const auto outputType = dyn_cast<RankedTensorType>(
getType());
3118 if ((
int64_t)shapeValues.size() != outputType.getRank())
3119 return emitOpError() <<
"new shape does not match result rank";
3121 for (
auto [newShapeDim, outputShapeDim] :
3122 zip(shapeValues, outputType.getShape())) {
3124 newShapeDim != ShapedType::kDynamic &&
3125 outputShapeDim != ShapedType::kDynamic && newShapeDim != outputShapeDim)
3126 return emitOpError() <<
"new shape is inconsistent with result shape";
3129 return emitOpError() <<
"new shape has invalid tensor dimension size "
3133 if (inputType.hasStaticShape()) {
3134 int64_t inputElementsNum = inputType.getNumElements();
3135 if (outputType.hasStaticShape()) {
3136 int64_t outputElementsNum = outputType.getNumElements();
3137 if (inputElementsNum != outputElementsNum) {
3138 return emitOpError() <<
"cannot reshape " << inputElementsNum
3139 <<
" elements into " << outputElementsNum;
3145 return (dim > 0) ?
acc * dim :
acc;
3147 bool isStaticNewShape =
3148 llvm::all_of(shapeValues, [](
int64_t s) {
return s > 0; });
3149 if ((isStaticNewShape && inputElementsNum != newShapeElementsNum) ||
3150 (!isStaticNewShape && newShapeElementsNum > inputElementsNum)) {
3151 return emitOpError() <<
"cannot reshape " << inputElementsNum
3152 <<
" elements into " << newShapeElementsNum;
3156 return mlir::success();
3159bool tosa::ReshapeBlockScaledOp::isCompatibleReturnTypes(
TypeRange l,
3161 if (l.size() != r.size() || l.size() < 1 || l.size() > 2)
3169LogicalResult tosa::ReshapeBlockScaledOp::inferReturnTypeComponents(
3170 MLIRContext *context, ::std::optional<Location> location,
3171 ReshapeBlockScaledOp::Adaptor adaptor,
3174 const auto numInputs = adaptor.getInput().size();
3175 ShapeAdaptor inputShape(adaptor.getInput()[0].getType());
3178 const auto newShape = adaptor.getNewValueShape();
3180 auto rank = cast<tosa::shapeType>(newShape.getType()).getRank();
3189 const uint32_t blockSize =
3190 BlockSizeAttr::getBlockSizeValue(adaptor.getBlockSize());
3193 if (numInputs == 2) {
3194 newScaleShapeValue.assign(newShapeValue.begin(), newShapeValue.end());
3195 if (!newScaleShapeValue.empty() &&
3196 ShapedType::isStatic(newScaleShapeValue.back()))
3197 newScaleShapeValue.back() /= blockSize;
3200 inferredReturnShapes.push_back(
3202 if (numInputs == 2) {
3204 for (
size_t idx = 0; idx < newShapeValue.size(); idx++) {
3205 if (ShapedType::isDynamic(newScaleShapeValue[idx])) {
3206 newScaleShapeValue[idx] = newShapeValue[idx];
3207 if (idx + 1 == newShapeValue.size())
3208 newScaleShapeValue[idx] /= blockSize;
3219llvm::LogicalResult tosa::ReshapeBlockScaledOp::verify() {
3223 if (inputList.size() == 0)
3224 return emitOpError(
"requires at least one input");
3226 if (inputList.size() > 2)
3227 return emitOpError(
"requires at most two inputs");
3229 if (inputList.size() != outputList.size())
3230 return emitOpError(
"requires number of results to match inputs");
3238 if (inputList.size() == 2 &&
3239 cast<tosa::shapeType>(getNewValueShape().
getType()).getRank() == 0)
3240 return emitOpError(
"requires new shape to have a rank greater than 0");
3242 const auto inputType = llvm::cast<ShapedType>(inputList[0].
getType());
3243 if (!inputType.hasRank())
3245 const uint32_t blockSize = BlockSizeAttr::getBlockSizeValue(
getBlockSize());
3247 if (inputList.size() == 2) {
3248 if (blockSize != BlockSizeAttr::getBlockSizeValue(BlockSize::BLOCK_SIZE_32))
3249 return emitOpError(
"expect block size to be 32, got ") << blockSize;
3250 if (llvm::any_of(inputList, [](
Value v) {
3251 const auto input = cast<ShapedType>(v.
getType());
3252 return input.hasRank() && input.getRank() == 0;
3255 "requires all input shapes have a rank greater than 0");
3256 if (llvm::any_of(outputList, [](
Value v) {
3257 const auto output = cast<ShapedType>(v.
getType());
3258 return output.hasRank() && output.getRank() == 0;
3261 "requires all result shapes have a rank greater than 0");
3269 const auto inputScaleType = llvm::cast<ShapedType>(inputList[1].
getType());
3270 if (inputScaleType.hasRank()) {
3271 if (inputType.getRank() != inputScaleType.getRank())
3272 return emitOpError(
"input shapes do not have same rank");
3275 for (
auto dimIdx = 0; dimIdx < inputType.getRank() - 1; dimIdx++) {
3276 const int64_t inputValueDim = inputType.getDimSize(dimIdx);
3277 const int64_t inputScaleDim = inputScaleType.getShape()[dimIdx];
3278 if (ShapedType::isStatic(inputValueDim) &&
3279 ShapedType::isStatic(inputScaleDim) &&
3280 inputValueDim != inputScaleDim)
3281 return emitOpError(
"input shapes for data and scale do not match on "
3288 inputType.getDimSize(inputType.getRank() - 1);
3289 if (ShapedType::isStatic(lastValueDim)) {
3290 if (lastValueDim % blockSize != 0)
3291 return emitOpError(
"expect last dimension of input_data (")
3292 << lastValueDim <<
") to be divisible by block_size ("
3293 << blockSize <<
")";
3296 inputScaleType.getDimSize(inputScaleType.getRank() - 1);
3298 if (ShapedType::isStatic(lastScaleDim) &&
3299 lastScaleDim != lastValueDim / blockSize)
3300 return emitOpError(
"expect last dimension of scale_data (")
3301 << lastScaleDim <<
") to be " << lastValueDim <<
"/"
3306 if (blockSize != BlockSizeAttr::getBlockSizeValue(BlockSize::BLOCK_SIZE_1))
3307 return emitOpError(
"expect block size to be 1, got ") << blockSize;
3315 return mlir::success();
3318 if (inputList.size() == 2) {
3319 const int64_t lastShapeDim = shapeValues.back();
3320 if (ShapedType::isStatic(lastShapeDim) && lastShapeDim % blockSize != 0)
3321 return emitOpError(
"expect last dimension of new shape (")
3322 << lastShapeDim <<
") to be divisible by block_size (" << blockSize
3326 const auto outputType = llvm::cast<ShapedType>(outputList[0].
getType());
3327 if (!outputType.hasRank())
3330 if (
static_cast<int64_t>(shapeValues.size()) != outputType.getRank())
3331 return emitOpError() <<
"result does not match new shape rank";
3333 for (
auto [newShapeDim, outputShapeDim] :
3334 zip(shapeValues, outputType.getShape())) {
3335 if (ShapedType::isStatic(newShapeDim) &&
3336 ShapedType::isStatic(outputShapeDim) && newShapeDim != outputShapeDim)
3337 return emitOpError() <<
"result shape is inconsistent with new shape";
3340 if (outputList.size() == 2) {
3344 scaleShapeValues.back() /= blockSize;
3346 const auto outputScaleType =
3347 llvm::cast<ShapedType>(outputList[1].
getType());
3348 if (outputScaleType.hasRank()) {
3349 if ((
int64_t)scaleShapeValues.size() != outputScaleType.getRank())
3350 return emitOpError() <<
"result scale does not match new shape rank";
3352 for (
auto [newScaleShapeDim, outputScaleShapeDim] :
3353 zip(scaleShapeValues, outputScaleType.getShape())) {
3354 if (ShapedType::isStatic(newScaleShapeDim) &&
3355 ShapedType::isStatic(outputScaleShapeDim) &&
3356 newScaleShapeDim != outputScaleShapeDim)
3357 return emitOpError()
3358 <<
"result scale shape is inconsistent with new shape";
3363 if (inputType.hasStaticShape()) {
3364 int64_t inputElementsNum = inputType.getNumElements();
3365 if (outputType.hasStaticShape()) {
3366 int64_t outputElementsNum = outputType.getNumElements();
3367 if (inputElementsNum != outputElementsNum) {
3368 return emitOpError() <<
"cannot reshape " << inputElementsNum
3369 <<
" elements into " << outputElementsNum;
3375 return (dim > 0) ?
acc * dim :
acc;
3377 bool isStaticNewShape =
3378 llvm::all_of(shapeValues, [](
int64_t s) {
return s > 0; });
3379 if ((isStaticNewShape && inputElementsNum != newShapeElementsNum) ||
3380 (!isStaticNewShape && newShapeElementsNum > inputElementsNum)) {
3381 return emitOpError() <<
"cannot reshape " << inputElementsNum
3382 <<
" elements into " << newShapeElementsNum;
3386 return mlir::success();
3393 ElementsAttr zpAttr;
3398 Type zpElemType = zpAttr.getElementType();
3400 if (llvm::isa<FloatType>(zpElemType)) {
3401 if (zpAttr.getValues<APFloat>()[0].isZero()) {
3408 if (llvm::isa<IntegerType>(zpElemType)) {
3410 return zpAttr.getValues<APInt>()[0].getSExtValue();
3411 return zpAttr.getValues<APInt>()[0].getZExtValue();
3418template <
typename T>
3420 const std::string &operand) {
3423 if (!zpElemType.
isInteger(8) && zp != 0) {
3425 std::string lower = operand;
3426 llvm::transform(lower, lower.begin(), ::tolower);
3427 return op.emitOpError()
3428 << lower <<
" zero point must be zero for non-int8 integer types";
3436 const std::string &operand) {
3437 bool isInputZp = (operand ==
"Input");
3439 bool tensorUnsigned =
3440 isInputZp ? op.getInputUnsigned() : op.getOutputUnsigned();
3441 StringRef tensorName = isInputZp ?
"input" :
"output";
3447 !(zpElemType.
isInteger(16) && tensorUnsigned)) {
3448 return op.emitOpError()
3449 <<
"expect " << tensorName <<
"_zp of 0, got " << zp;
3451 if (zpElemType.
isInteger(16) && tensorUnsigned && zp != 32768) {
3452 return op.emitOpError() <<
"expect " << tensorName
3453 <<
"_zp of 0 or 32768 for unsigned int16 "
3454 << tensorName <<
", got " << zp;
3461#define ZERO_POINT_HELPER(OP, OPERAND_NAME, SIGN_EXTEND) \
3462 FailureOr<int64_t> tosa::OP::get##OPERAND_NAME##ZeroPoint() { \
3463 return getZeroPoint(get##OPERAND_NAME##Zp(), SIGN_EXTEND); \
3465 LogicalResult tosa::OP::verify##OPERAND_NAME##ZeroPoint(int64_t zp) { \
3466 return verifyZeroPoint(*this, get##OPERAND_NAME##Zp(), zp, #OPERAND_NAME); \
3489#undef ZERO_POINT_HELPER
3491LogicalResult tosa::TransposeOp::inferReturnTypeComponents(
3492 MLIRContext *context, ::std::optional<Location> location,
3493 TransposeOp::Adaptor adaptor,
3495 ShapeAdaptor inputShape(adaptor.getInput1().getType());
3504 const auto inputRank = inputShape.
getRank();
3508 if (adaptor.getPerms().size() !=
static_cast<size_t>(inputRank)) {
3514 if (inputRank == 0) {
3520 bool allTheSame =
true;
3521 for (
int i = 1, s = inputRank; i < s; i++) {
3531 outputShape.resize(inputRank, inputShape.
getDimSize(0));
3536 outputShape.resize(inputRank, ShapedType::kDynamic);
3539 if (llvm::any_of(adaptor.getPerms(),
3540 [inputRank](
const auto i) { return i >= inputRank; }))
3543 outputShape.reserve(inputRank);
3544 for (
int i = 0, s = inputRank; i < s; i++) {
3545 outputShape[i] = inputShape.
getDimSize(adaptor.getPerms()[i]);
3552LogicalResult tosa::TransposeOp::verify() {
3564 if (inputShape.hasRank() &&
3565 constantPerms.size() !=
static_cast<size_t>(inputShape.getRank()))
3566 return emitOpError() <<
"expected perms attribute to have size "
3567 << inputShape.getRank()
3568 <<
" (input rank) but got size "
3569 << constantPerms.size();
3571 if (inputShape.hasRank() && outputShape.hasRank() &&
3572 inputShape.getRank() != outputShape.getRank())
3573 return emitOpError()
3574 <<
"expected input tensor rank to equal result tensor rank";
3576 if (outputShape.hasRank() &&
3577 constantPerms.size() !=
static_cast<size_t>(outputShape.getRank()))
3578 return emitOpError() <<
"expected perms attribute to have size "
3579 << outputShape.getRank()
3580 <<
" (output rank) but got size "
3581 << constantPerms.size();
3583 if (!llvm::all_of(constantPerms,
3584 [&constantPerms](int32_t s) {
3586 static_cast<size_t>(s) < constantPerms.size();
3589 constantPerms, [](int32_t v) ->
int64_t {
return v; })))
3590 return emitOpError() <<
"expected valid permutation indices";
3593 constantPerms.back() !=
static_cast<int32_t
>(constantPerms.size()) - 1) {
3594 return emitOpError() <<
"expected no-op permutation on innermost dimension "
3595 "for block scaled input";
3599 if (inputShape.hasStaticShape() && outputShape.hasStaticShape() &&
3600 inputShape.getNumElements() != outputShape.getNumElements())
3601 return emitOpError() <<
"expected input1 and output to have same numbers "
3603 << inputShape.getNumElements() <<
" and "
3604 << outputShape.getNumElements();
3608 if (inputShape.hasRank() && outputShape.hasRank()) {
3609 for (
auto i = 0; i < outputShape.getRank(); i++) {
3610 if (inputShape.isDynamicDim(constantPerms[i]) ||
3611 outputShape.isDynamicDim(i))
3614 if (inputShape.getDimSize(constantPerms[i]) != outputShape.getDimSize(i))
3615 return emitOpError()
3616 <<
"expected output tensor dim " << i <<
" to match "
3617 <<
"input dim " << constantPerms[i] <<
" with value of "
3618 << inputShape.getDimSize(constantPerms[i]);
3625LogicalResult TransposeOp::reifyResultShapes(
3628 const llvm::ArrayRef<int32_t> transposePerms = getPerms();
3630 Value input = getInput1();
3631 auto inputType = cast<TensorType>(input.
getType());
3633 SmallVector<OpFoldResult> returnedDims(inputType.getRank());
3634 for (
auto dim : transposePerms) {
3635 int32_t dimInInput = transposePerms[dim];
3636 if (inputType.isDynamicDim(dimInInput))
3638 tensor::DimOp::create(builder, getLoc(), input, dimInInput)
3642 builder.
getIndexAttr(inputType.getDimSize(dimInInput));
3645 reifiedReturnShapes.emplace_back(std::move(returnedDims));
3649LogicalResult tosa::GatherOp::inferReturnTypeComponents(
3650 MLIRContext *context, ::std::optional<Location> location,
3651 GatherOp::Adaptor adaptor,
3652 SmallVectorImpl<ShapedTypeComponents> &inferredReturnShapes) {
3653 llvm::SmallVector<int64_t> outputShape;
3654 outputShape.resize(3, ShapedType::kDynamic);
3656 ShapeAdaptor valuesShape(adaptor.getValues().getType());
3657 if (valuesShape.hasRank()) {
3658 outputShape[0] = valuesShape.getDimSize(0);
3659 outputShape[2] = valuesShape.getDimSize(2);
3662 ShapeAdaptor indicesShape(adaptor.getIndices().getType());
3663 if (indicesShape.hasRank()) {
3664 if (outputShape[0] == ShapedType::kDynamic)
3665 outputShape[0] = indicesShape.getDimSize(0);
3666 if (outputShape[1] == ShapedType::kDynamic)
3667 outputShape[1] = indicesShape.getDimSize(1);
3670 inferredReturnShapes.push_back(ShapedTypeComponents(outputShape));
3674LogicalResult tosa::RowGatherOp::inferReturnTypeComponents(
3675 MLIRContext *context, ::std::optional<Location> location,
3676 RowGatherOp::Adaptor adaptor,
3677 SmallVectorImpl<ShapedTypeComponents> &inferredReturnShapes) {
3678 llvm::SmallVector<int64_t> outputShape;
3679 outputShape.resize(3, ShapedType::kDynamic);
3681 const ShapeAdaptor valuesShape(adaptor.getValues().getType());
3682 if (valuesShape.hasRank()) {
3683 outputShape[0] = valuesShape.getDimSize(0);
3684 outputShape[2] = valuesShape.getDimSize(2);
3687 const ShapeAdaptor indicesShape(adaptor.getIndices().getType());
3688 if (indicesShape.hasRank()) {
3689 if (outputShape[0] == ShapedType::kDynamic)
3690 outputShape[0] = indicesShape.getDimSize(0);
3692 const FailureOr<int32_t> maybeRowCount =
3694 if (succeeded(maybeRowCount)) {
3695 const int64_t indicesW = indicesShape.getDimSize(1);
3696 if (ShapedType::isStatic(indicesW))
3697 outputShape[1] = indicesW * maybeRowCount.value();
3701 inferredReturnShapes.push_back(ShapedTypeComponents(outputShape));
3705LogicalResult tosa::RowGatherBlockScaledOp::inferReturnTypeComponents(
3706 MLIRContext *context, ::std::optional<Location> location,
3707 RowGatherBlockScaledOp::Adaptor adaptor,
3708 SmallVectorImpl<ShapedTypeComponents> &inferredReturnShapes) {
3709 const auto values = adaptor.getValues();
3713 SmallVector<int64_t> dataShape(3, ShapedType::kDynamic);
3714 const ShapeAdaptor valuesShape(values.front().getType());
3715 if (valuesShape.hasRank()) {
3716 dataShape[0] = valuesShape.getDimSize(0);
3717 dataShape[2] = valuesShape.getDimSize(2);
3720 const ShapeAdaptor indicesShape(adaptor.getIndices().getType());
3721 if (indicesShape.hasRank()) {
3722 if (dataShape[0] == ShapedType::kDynamic)
3723 dataShape[0] = indicesShape.getDimSize(0);
3727 succeeded(rowCount) && rowCount.value() > 0) {
3728 const int64_t indicesW = indicesShape.getDimSize(1);
3729 if (ShapedType::isStatic(indicesW))
3730 dataShape[1] = indicesW * rowCount.value();
3734 inferredReturnShapes.push_back(ShapedTypeComponents(dataShape));
3735 if (values.size() == 1)
3738 SmallVector<int64_t> scaleShape = dataShape;
3739 const uint32_t blockSize =
3740 BlockSizeAttr::getBlockSizeValue(adaptor.getBlockSize());
3741 if (ShapedType::isStatic(dataShape[2]))
3742 scaleShape[2] = dataShape[2] / blockSize;
3744 inferredReturnShapes.push_back(ShapedTypeComponents(scaleShape));
3748LogicalResult tosa::GatherOp::verify() {
3755 const ShapeAdaptor valuesShape(getValues().
getType());
3757 const ShapeAdaptor outputShape(getOutput().
getType());
3759 int64_t n = ShapedType::kDynamic;
3760 int64_t w = ShapedType::kDynamic;
3761 int64_t c = ShapedType::kDynamic;
3763 if (valuesShape.hasRank()) {
3764 n = valuesShape.getDimSize(0);
3765 c = valuesShape.getDimSize(2);
3767 if (indicesShape.hasRank()) {
3768 const int64_t indicesN = indicesShape.getDimSize(0);
3769 w = indicesShape.getDimSize(1);
3770 if (n == ShapedType::kDynamic)
3772 else if (indicesN != ShapedType::kDynamic && n != indicesN)
3773 return emitOpError() <<
"requires indices dimension 0 to have size " << n
3774 <<
", got " << indicesN;
3776 if (outputShape.hasRank()) {
3777 const int64_t outputN = outputShape.getDimSize(0);
3778 const int64_t outputW = outputShape.getDimSize(1);
3779 const int64_t outputC = outputShape.getDimSize(2);
3780 if (n != ShapedType::kDynamic && outputN != ShapedType::kDynamic &&
3782 return emitOpError() <<
"requires output dimension 0 to have size " << n
3783 <<
", got " << outputN;
3785 if (w != ShapedType::kDynamic && outputW != ShapedType::kDynamic &&
3787 return emitOpError() <<
"requires output dimension 1 to have size " << w
3788 <<
", got " << outputW;
3789 if (c != ShapedType::kDynamic && outputC != ShapedType::kDynamic &&
3791 return emitOpError() <<
"requires output dimension 2 to have size " << c
3792 <<
", got " << outputC;
3797LogicalResult tosa::RowGatherOp::verify() {
3802 const FailureOr<int32_t> maybeRowCount =
3804 if (succeeded(maybeRowCount) && maybeRowCount.value() <= 0)
3805 return emitOpError() <<
"requires row_count to be > 0, got "
3806 << maybeRowCount.value();
3808 int64_t n = ShapedType::kDynamic;
3809 int64_t c = ShapedType::kDynamic;
3810 int64_t w = ShapedType::kDynamic;
3812 const ShapeAdaptor valuesShape(getValues().
getType());
3813 if (valuesShape.hasRank()) {
3814 n = valuesShape.getDimSize(0);
3815 c = valuesShape.getDimSize(2);
3819 if (indicesShape.hasRank()) {
3821 "indices",
"batch")))
3823 w = indicesShape.getDimSize(1);
3826 const ShapeAdaptor outputShape(getOutput().
getType());
3827 if (outputShape.hasRank()) {
3829 "output",
"batch")) ||
3831 "output",
"channels")))
3834 if (succeeded(maybeRowCount) && maybeRowCount.value() > 0 &&
3835 ShapedType::isStatic(w)) {
3836 const int64_t expectedOutputRows = w * maybeRowCount.value();
3837 if (ShapedType::isStatic(outputShape.getDimSize(1)) &&
3838 outputShape.getDimSize(1) != expectedOutputRows)
3839 return emitOpError()
3840 <<
"requires output dimension to be equal to "
3841 "indices[1]*row_count ("
3842 << expectedOutputRows <<
"), got " << outputShape.getDimSize(1);
3849LogicalResult tosa::RowGatherBlockScaledOp::verify() {
3850 const OperandRange values = getValues();
3851 const ResultRange output = getOutput();
3852 if (values.empty() || values.size() > 2)
3853 return emitOpError()
3854 <<
"expects values tensor list length to be 1 or 2, got "
3856 if (output.size() != values.size())
3857 return emitOpError()
3858 <<
"expects output tensor list length to match values tensor list "
3860 << output.size() <<
" results for " << values.size()
3861 <<
" input tensors";
3863 const uint32_t blockSize = BlockSizeAttr::getBlockSizeValue(
getBlockSize());
3864 if (values.size() == 1 && blockSize != 1)
3865 return emitOpError()
3866 <<
"requires block_size to be BLOCK_SIZE_1 when values tensor list "
3868 if (values.size() == 2 && blockSize == 1)
3869 return emitOpError()
3870 <<
"requires block_size to not be BLOCK_SIZE_1 when values tensor "
3874 output[0].
getType(),
"values[0]",
3879 "values[1]",
"output[1]")))
3883 succeeded(rowCount) && rowCount.value() <= 0)
3884 return emitOpError() <<
"requires row_count to be > 0, got "
3885 << rowCount.value();
3887 int64_t n = ShapedType::kDynamic;
3888 int64_t k = ShapedType::kDynamic;
3889 int64_t c = ShapedType::kDynamic;
3890 int64_t w = ShapedType::kDynamic;
3891 int64_t multiplesOfC = ShapedType::kDynamic;
3893 const ShapeAdaptor valuesDataShape(values[0].
getType());
3894 if (valuesDataShape.hasRank()) {
3895 n = valuesDataShape.getDimSize(0);
3896 k = valuesDataShape.getDimSize(1);
3897 c = valuesDataShape.getDimSize(2);
3900 if (ShapedType::isStatic(c) && c % blockSize != 0)
3901 return emitOpError() <<
"expects channels of values[0] (" << c
3902 <<
") to be divisible by block_size (" << blockSize
3906 if (indicesShape.hasRank()) {
3908 "indices",
"batch")))
3910 w = indicesShape.getDimSize(1);
3913 const ShapeAdaptor outputDataShape(output[0].
getType());
3914 if (outputDataShape.hasRank()) {
3916 "output[0]",
"batch")) ||
3918 "output[0]",
"channels")))
3922 succeeded(rowCount) && rowCount.value() > 0 &&
3923 ShapedType::isStatic(w)) {
3924 const int64_t expectedOutputRows = w * rowCount.value();
3925 if (ShapedType::isStatic(outputDataShape.getDimSize(1)) &&
3926 outputDataShape.getDimSize(1) != expectedOutputRows)
3927 return emitOpError() <<
"requires output[0] dimension 1 to have size "
3928 << expectedOutputRows <<
", got "
3929 << outputDataShape.getDimSize(1);
3933 if (values.size() == 2) {
3934 const ShapeAdaptor valuesScaleShape(values[1].
getType());
3935 if (valuesScaleShape.hasRank()) {
3937 "values[1]",
"batch")) ||
3939 "values[1]",
"rows")))
3941 multiplesOfC = valuesScaleShape.getDimSize(2);
3944 const ShapeAdaptor outputScaleShape(output[1].
getType());
3945 if (outputScaleShape.hasRank()) {
3947 "output[1]",
"batch")))
3951 succeeded(rowCount) && rowCount.value() > 0 &&
3952 ShapedType::isStatic(w)) {
3953 const int64_t expectedOutputRows = w * rowCount.value();
3954 if (ShapedType::isStatic(outputScaleShape.getDimSize(1)) &&
3955 outputScaleShape.getDimSize(1) != expectedOutputRows)
3956 return emitOpError() <<
"requires output[1] dimension 1 to have size "
3957 << expectedOutputRows <<
", got "
3958 << outputScaleShape.getDimSize(1);
3961 if (ShapedType::isDynamic(multiplesOfC))
3962 multiplesOfC = outputScaleShape.getDimSize(2);
3963 else if (ShapedType::isStatic(outputScaleShape.getDimSize(2)) &&
3964 multiplesOfC != outputScaleShape.getDimSize(2))
3965 return emitOpError()
3966 <<
"expected channels of output[1] to match size "
3967 << multiplesOfC <<
", got " << outputScaleShape.getDimSize(2);
3970 if (ShapedType::isStatic(c) && ShapedType::isStatic(multiplesOfC) &&
3971 multiplesOfC != c / blockSize)
3972 return emitOpError()
3973 <<
"expects channels of scale tensors to equal C/block_size (" << c
3974 <<
"/" << blockSize <<
"), got " << multiplesOfC;
3980LogicalResult tosa::ResizeOp::inferReturnTypeComponents(
3981 MLIRContext *context, ::std::optional<Location> location,
3982 ResizeOp::Adaptor adaptor,
3983 SmallVectorImpl<ShapedTypeComponents> &inferredReturnShapes) {
3984 llvm::SmallVector<int64_t, 4> outputShape;
3985 outputShape.resize(4, ShapedType::kDynamic);
3987 ShapeAdaptor inputShape(adaptor.getInput().getType());
3988 if (!inputShape.hasRank())
3991 outputShape[0] = inputShape.getDimSize(0);
3992 outputShape[3] = inputShape.getDimSize(3);
3993 int64_t inputHeight = inputShape.getDimSize(1);
3994 int64_t inputWidth = inputShape.getDimSize(2);
3996 if ((inputHeight == ShapedType::kDynamic) ||
3997 (inputWidth == ShapedType::kDynamic))
4000 SmallVector<int64_t> scaleInt, offsetInt, borderInt;
4011 const int64_t outputHeight =
4012 (((inputHeight - 1) * scaleInt[0] - offsetInt[0] + borderInt[0]) /
4016 const int64_t outputWidth =
4017 (((inputWidth - 1) * scaleInt[2] - offsetInt[1] + borderInt[1]) /
4021 if (outputHeight < 0 || outputWidth < 0) {
4024 "calculated output height and width must be non-negative, "
4026 outputHeight,
", width = ", outputWidth);
4029 outputShape[1] = outputHeight;
4030 outputShape[2] = outputWidth;
4031 inferredReturnShapes.push_back(ShapedTypeComponents(outputShape));
4035LogicalResult tosa::ResizeOp::verify() {
4036 const Value input = getInput();
4037 const Value output = getOutput();
4040 if (isa<BlockScaledType>(inputElementType) &&
4041 getMode() != ResizeMode::NEAREST_NEIGHBOR)
4042 return emitOpError(
"requires NEAREST_NEIGHBOR mode for block scaled input");
4044 const RankedTensorType inputType =
4045 llvm::dyn_cast<RankedTensorType>(input.
getType());
4046 const RankedTensorType outputType =
4047 llvm::dyn_cast<RankedTensorType>(output.
getType());
4049 SmallVector<int64_t> scaleValues;
4050 SmallVector<int64_t> offsetValues;
4051 SmallVector<int64_t> borderValues;
4059 if (llvm::any_of(scaleValues, [](int64_t s) {
return s <= 0; }))
4060 return emitOpError(
"expect all scale values to be > 0, got ")
4063 const int64_t scaleYN = scaleValues[0];
4064 const int64_t scaleYD = scaleValues[1];
4065 const int64_t scaleXN = scaleValues[2];
4066 const int64_t scaleXD = scaleValues[3];
4068 const int64_t offsetY = offsetValues[0];
4069 const int64_t offsetX = offsetValues[1];
4071 const int64_t borderY = borderValues[0];
4072 const int64_t borderX = borderValues[1];
4079 const int64_t oh = outputType.getDimSize(1);
4080 const int64_t ow = outputType.getDimSize(2);
4081 const int64_t ih = inputType.getDimSize(1);
4082 const int64_t iw = inputType.getDimSize(2);
4088 if (ih != ShapedType::kDynamic && ih != 1) {
4089 const std::optional<int64_t> calculatedOutHeightMinusOne =
4090 idivCheck((ih - 1) * scaleYN - offsetY + borderY, scaleYD);
4091 if (!calculatedOutHeightMinusOne.has_value())
4092 return emitOpError(
"expected (input_height - 1) * scale_y_n - offset_y + "
4094 <<
"to be wholly divisible by scale_y_d, got ((" << ih
4095 <<
" - 1) * " << scaleYN <<
" - " << offsetY <<
" + " << borderY
4096 <<
") / " << scaleYD;
4097 const int64_t calculatedOutHeight = calculatedOutHeightMinusOne.value() + 1;
4098 if (oh != ShapedType::kDynamic && calculatedOutHeight != oh)
4099 return emitOpError(
"calculated output height did not match expected: ")
4100 <<
"calculated=" << calculatedOutHeight <<
", expected=" << oh;
4107 if (iw != ShapedType::kDynamic && iw != 1) {
4108 const int64_t scaledInWidth = (iw - 1) * scaleXN - offsetX + borderX;
4109 const std::optional<int64_t> calculatedOutWidthMinusOne =
4111 if (!calculatedOutWidthMinusOne.has_value())
4112 return emitOpError(
"expected (input_width - 1) * scale_x_n - offset_x + "
4114 <<
"to be wholly divisible by scale_x_d, got ((" << iw
4115 <<
" - 1) * " << scaleXN <<
" - " << offsetX <<
" + " << borderX
4116 <<
") / " << scaleXD;
4117 const int64_t calculatedOutWidth = calculatedOutWidthMinusOne.value() + 1;
4118 if (ow != ShapedType::kDynamic && calculatedOutWidth != ow)
4119 return emitOpError(
"calculated output width did not match expected: ")
4120 <<
"calculated=" << calculatedOutWidth <<
", expected=" << ow;
4126LogicalResult tosa::ScatterOp::inferReturnTypeComponents(
4127 MLIRContext *context, ::std::optional<Location> location,
4128 ScatterOp::Adaptor adaptor,
4129 SmallVectorImpl<ShapedTypeComponents> &inferredReturnShapes) {
4130 llvm::SmallVector<int64_t> outputShape;
4131 outputShape.resize(3, ShapedType::kDynamic);
4133 ShapeAdaptor valuesInShape(adaptor.getValuesIn().getType());
4134 if (valuesInShape.hasRank()) {
4135 outputShape[0] = valuesInShape.getDimSize(0);
4136 outputShape[1] = valuesInShape.getDimSize(1);
4137 outputShape[2] = valuesInShape.getDimSize(2);
4140 ShapeAdaptor indicesShape(adaptor.getIndices().getType());
4141 if (indicesShape.hasRank()) {
4142 if (outputShape[0] == ShapedType::kDynamic)
4143 outputShape[0] = indicesShape.getDimSize(0);
4146 ShapeAdaptor inputShape(adaptor.getInput().getType());
4147 if (inputShape.hasRank()) {
4148 if (outputShape[0] == ShapedType::kDynamic)
4149 outputShape[0] = inputShape.getDimSize(0);
4150 if (outputShape[2] == ShapedType::kDynamic)
4151 outputShape[2] = inputShape.getDimSize(2);
4154 inferredReturnShapes.push_back(ShapedTypeComponents(outputShape));
4158LogicalResult tosa::ScatterOp::verify() {
4168 const ShapeAdaptor valuesInShape(getValuesIn().
getType());
4170 const ShapeAdaptor inputShape(getInput().
getType());
4171 const ShapeAdaptor outputShape(getValuesOut().
getType());
4173 int64_t n = ShapedType::kDynamic;
4174 int64_t k = ShapedType::kDynamic;
4175 int64_t w = ShapedType::kDynamic;
4176 int64_t c = ShapedType::kDynamic;
4177 if (valuesInShape.hasRank()) {
4178 n = valuesInShape.getDimSize(0);
4179 k = valuesInShape.getDimSize(1);
4180 c = valuesInShape.getDimSize(2);
4182 if (indicesShape.hasRank()) {
4183 const int64_t indicesN = indicesShape.getDimSize(0);
4184 w = indicesShape.getDimSize(1);
4185 if (n == ShapedType::kDynamic)
4187 else if (indicesN != ShapedType::kDynamic && n != indicesN)
4188 return emitOpError() <<
"requires indices dimension 0 to have size " << n
4189 <<
", got " << indicesN;
4191 if (inputShape.hasRank()) {
4192 const int64_t inputN = inputShape.getDimSize(0);
4193 const int64_t inputW = inputShape.getDimSize(1);
4194 const int64_t inputC = inputShape.getDimSize(2);
4195 if (n == ShapedType::kDynamic)
4197 else if (inputN != ShapedType::kDynamic && n != inputN)
4198 return emitOpError() <<
"requires input dimension 0 to have size " << n
4199 <<
", got " << inputN;
4200 if (w == ShapedType::kDynamic)
4202 else if (inputW != ShapedType::kDynamic && w != inputW)
4203 return emitOpError() <<
"requires input dimension 1 to have size " << w
4204 <<
", got " << inputW;
4206 if (c == ShapedType::kDynamic)
4208 else if (inputC != ShapedType::kDynamic && c != inputC)
4209 return emitOpError() <<
"requires input dimension 2 to have size " << c
4210 <<
", got " << inputC;
4212 if (outputShape.hasRank()) {
4213 const int64_t outputN = outputShape.getDimSize(0);
4214 const int64_t outputK = outputShape.getDimSize(1);
4215 const int64_t outputC = outputShape.getDimSize(2);
4216 if (n != ShapedType::kDynamic && outputN != ShapedType::kDynamic &&
4218 return emitOpError() <<
"requires values_out dimension 0 to have size "
4219 << n <<
", got " << outputN;
4220 if (k == ShapedType::kDynamic)
4222 else if (outputK != ShapedType::kDynamic && k != outputK)
4223 return emitOpError() <<
"requires values_out dimension 1 to have size "
4224 << k <<
", got " << outputK;
4225 if (c != ShapedType::kDynamic && outputC != ShapedType::kDynamic &&
4227 return emitOpError() <<
"requires values_out dimension 2 to have size "
4228 << c <<
", got " << outputC;
4230 if (k != ShapedType::kDynamic && w != ShapedType::kDynamic && !(k >= w))
4231 return emitOpError() <<
"requires dimensions K >= W, got K=" << k
4240 int64_t axisVal = axis.getValue().getSExtValue();
4241 if (!operandShape.
hasRank() || operandShape.
getRank() <= axisVal) {
4247 operandShape.
getDims(outputShape);
4248 outputShape[axisVal] = 1;
4253#define COMPATIBLE_RETURN_TYPES(OP) \
4254 bool OP::isCompatibleReturnTypes(TypeRange l, TypeRange r) { \
4255 if (l.size() != r.size() || l.size() != 1) \
4257 if (getElementTypeOrSelf(l[0]) != getElementTypeOrSelf(r[0])) \
4259 return succeeded(verifyCompatibleShape(l[0], r[0])); \
4262#define REDUCE_SHAPE_INFER(OP) \
4263 LogicalResult OP::inferReturnTypeComponents( \
4264 MLIRContext *context, ::std::optional<Location> location, \
4265 OP::Adaptor adaptor, \
4266 SmallVectorImpl<ShapedTypeComponents> &inferredReturnShapes) { \
4268 llvm::cast<TensorType>(adaptor.getInput().getType()).getElementType(); \
4269 ShapeAdaptor inputShape(adaptor.getInput().getType()); \
4270 const Properties &prop = adaptor.getProperties(); \
4271 return ReduceInferReturnTypes(inputShape, inputType, prop.axis, \
4272 inferredReturnShapes); \
4274 COMPATIBLE_RETURN_TYPES(OP)
4282#undef REDUCE_SHAPE_INFER
4284#undef COMPATIBLE_RETURN_TYPES
4286template <
typename T>
4289 TensorType inputType = op.getInput().getType();
4290 TensorType outputType = op.getOutput().getType();
4291 int32_t reduceAxis = op.getAxis();
4293 if (reduceAxis < 0) {
4294 op.emitOpError(
"reduce axis must not be negative");
4298 int64_t inputRank = inputType.getRank();
4301 if (reduceAxis >= inputRank && (reduceAxis != 0 || inputRank != 0)) {
4302 op.emitOpError(
"expect input tensor rank (")
4303 << inputRank <<
") to be larger than reduce axis (" << reduceAxis
4309 int64_t outputRank = outputType.getRank();
4310 if (inputType.
hasRank() && outputRank != inputType.getRank()) {
4312 "expect output tensor rank to be equal to input tensor rank");
4315 if (reduceAxis >= outputRank && (reduceAxis != 0 || outputRank != 0)) {
4316 op.emitOpError(
"expect output tensor rank (")
4317 << outputRank <<
") to be larger than reduce axis (" << reduceAxis
4323 if (outputRank != 0) {
4324 auto outputShape = outputType.
getShape();
4325 if (!outputType.isDynamicDim(reduceAxis) &&
4326 outputShape[reduceAxis] != 1) {
4327 op.emitOpError(
"expect reduced dimension size to be 1, got ")
4328 << outputShape[reduceAxis];
4336LogicalResult tosa::ReduceAllOp::verify() {
return verifyReduceOp(*
this); }
4337LogicalResult tosa::ReduceAnyOp::verify() {
return verifyReduceOp(*
this); }
4338LogicalResult tosa::ReduceMaxOp::verify() {
return verifyReduceOp(*
this); }
4339LogicalResult tosa::ReduceMinOp::verify() {
return verifyReduceOp(*
this); }
4340LogicalResult tosa::ReduceProductOp::verify() {
return verifyReduceOp(*
this); }
4341LogicalResult tosa::ReduceSumOp::verify() {
return verifyReduceOp(*
this); }
4355#define NARY_SHAPE_INFER(OP) \
4356 LogicalResult OP::inferReturnTypeComponents( \
4357 MLIRContext *context, ::std::optional<Location> location, \
4358 ValueShapeRange operands, DictionaryAttr attributes, \
4359 PropertyRef properties, RegionRange regions, \
4360 SmallVectorImpl<ShapedTypeComponents> &inferredReturnShapes) { \
4361 return NAryInferReturnTypes(operands, inferredReturnShapes); \
4401#undef PRED_SHAPE_INFER
4403LogicalResult tosa::NegateOp::inferReturnTypeComponents(
4404 MLIRContext *context, ::std::optional<Location> location,
4405 NegateOp::Adaptor adaptor,
4407 ShapeAdaptor inputShape(adaptor.getInput1().getType());
4412LogicalResult tosa::NegateOp::verify() {
4414 const Type input1Type = getInput1().getType();
4415 const Type outputType = getOutput().getType();
4420 const SmallVector<Type, 2> types = {input1Type, outputType};
4422 return emitOpError() <<
"requires the same shape for input1 and output";
4425 const Type input1ZpEType =
4427 if (input1EType != input1ZpEType) {
4428 return emitOpError(
"expect both input1 and its zero point are the same "
4429 "element type, got ")
4430 << input1EType <<
" and " << input1ZpEType;
4433 const Type outputZpEType =
4435 if (outputEType != outputZpEType) {
4436 return emitOpError(
"expect both output and its zero point are the same "
4437 "element type, got ")
4438 << outputEType <<
" and " << outputZpEType;
4441 FailureOr<int64_t> maybeIZp = getInput1ZeroPoint();
4442 if (succeeded(maybeIZp) && verifyInput1ZeroPoint(*maybeIZp).failed())
4445 FailureOr<int64_t> maybeOZp = getOutputZeroPoint();
4446 if (succeeded(maybeOZp) && verifyOutputZeroPoint(*maybeOZp).failed())
4457 outputShape.resize(4, ShapedType::kDynamic);
4472 if (ShapedType::isStatic(height)) {
4473 int64_t padded = height + pad[0] + pad[1] - kernel[0];
4474 outputShape[1] = padded / stride[0] + 1;
4477 if (ShapedType::isStatic(width)) {
4478 int64_t padded = width + pad[2] + pad[3] - kernel[1];
4479 outputShape[2] = padded / stride[1] + 1;
4486template <
typename AdaptorT>
4492 if (ShapedType::isDynamic(current))
4493 current = candidate;
4502 : adaptor(adaptor) {}
4506 const ShapeAdaptor inputShape(adaptor.getInput().getType());
4514 outputShape[0] = outputBatch;
4515 inputSpatial[0] = inputHeight;
4516 inputSpatial[1] = inputWidth;
4521 const ShapeAdaptor weightShape(adaptor.getWeight().getType());
4529 outputShape[3] = outputChannels;
4530 weightSpatial[0] = kernelHeight;
4531 weightSpatial[1] = kernelWidth;
4540 padValues.assign(adaptor.getPad().begin(), adaptor.getPad().end());
4541 strideValues.assign(adaptor.getStride().begin(), adaptor.getStride().end());
4542 dilationValues.assign(adaptor.getDilation().begin(),
4543 adaptor.getDilation().end());
4548 Conv2DOp::Adaptor adaptor;
4556 : adaptor(adaptor) {}
4560 const ShapeAdaptor inputDataShape(adaptor.getInputData().getType());
4561 if (inputDataShape.
hasRank()) {
4566 outputShape[0] = outputBatch;
4567 inputSpatial[0] = inputHeight;
4568 inputSpatial[1] = inputWidth;
4571 const ShapeAdaptor inputScaleShape(adaptor.getInputScale().getType());
4572 if (!inputScaleShape.
hasRank())
4586 const ShapeAdaptor weightDataShape(adaptor.getWeightData().getType());
4587 if (weightDataShape.
hasRank()) {
4592 outputShape[3] = outputChannels;
4593 weightSpatial[0] = kernelHeight;
4594 weightSpatial[1] = kernelWidth;
4597 const ShapeAdaptor weightScaleShape(adaptor.getWeightScale().getType());
4598 if (!weightScaleShape.
hasRank())
4627 Conv2DBlockScaledOp::Adaptor adaptor;
4635 : adaptor(adaptor) {}
4639 const ShapeAdaptor inputShape(adaptor.getInput().getType());
4648 outputShape[0] = outputBatch;
4649 inputSpatial[0] = inputDepth;
4650 inputSpatial[1] = inputHeight;
4651 inputSpatial[2] = inputWidth;
4656 const ShapeAdaptor weightShape(adaptor.getWeight().getType());
4665 outputShape[4] = outputChannels;
4666 weightSpatial[0] = kernelDepth;
4667 weightSpatial[1] = kernelHeight;
4668 weightSpatial[2] = kernelWidth;
4677 padValues.assign(adaptor.getPad().begin(), adaptor.getPad().end());
4678 strideValues.assign(adaptor.getStride().begin(), adaptor.getStride().end());
4679 dilationValues.assign(adaptor.getDilation().begin(),
4680 adaptor.getDilation().end());
4685 Conv3DOp::Adaptor adaptor;
4688template <
typename AdaptorT>
4694 ShapedType::kDynamic);
4696 ShapedType::kDynamic);
4698 ShapedType::kDynamic);
4700 convShapeAdaptor.inferInputShape(outputShape, inputSpatial);
4701 convShapeAdaptor.inferWeightShape(outputShape, weightSpatial);
4703 const ShapeAdaptor biasShape = adaptor.getBias().getType();
4706 if (biasSize != 1) {
4707 const size_t outputChannelDim = convShapeAdaptor.getOutputRank() - 1;
4708 outputShape[outputChannelDim] =
4709 ShapedType::isDynamic(outputShape[outputChannelDim])
4711 : outputShape[outputChannelDim];
4718 if (failed(convShapeAdaptor.getSpatialParameters(padValues, strideValues,
4724 for (
int64_t dim = 0; dim < convShapeAdaptor.getNumSpatialDims(); ++dim) {
4725 if (!ShapedType::isStatic(inputSpatial[dim]) ||
4726 !ShapedType::isStatic(weightSpatial[dim]))
4729 inputSpatial[dim] + padValues[2 * dim] + padValues[2 * dim + 1];
4731 (weightSpatial[dim] - 1) * dilationValues[dim] + 1;
4732 const int64_t unstridedResult = inputSize - filterSize + 1;
4733 outputShape[dim + 1] = (unstridedResult - 1) / strideValues[dim] + 1;
4740LogicalResult Conv2DOp::inferReturnTypeComponents(
4741 MLIRContext *context, ::std::optional<Location> location,
4742 Conv2DOp::Adaptor adaptor,
4743 SmallVectorImpl<ShapedTypeComponents> &inferredReturnShapes) {
4747LogicalResult Conv2DOp::verify() {
4754LogicalResult Conv2DBlockScaledOp::inferReturnTypeComponents(
4755 MLIRContext *context, ::std::optional<Location> location,
4756 Conv2DBlockScaledOp::Adaptor adaptor,
4757 SmallVectorImpl<ShapedTypeComponents> &inferredReturnShapes) {
4761LogicalResult Conv2DBlockScaledOp::verify() {
4763 getWeightData().
getType(),
"input_data",
4766 getWeightScale().
getType(),
"input_scale",
4769 getOutput().
getType(),
"bias",
"output")))
4773 int64_t N = ShapedType::kDynamic;
4774 int64_t IH = ShapedType::kDynamic;
4775 int64_t IW = ShapedType::kDynamic;
4776 int64_t IC = ShapedType::kDynamic;
4777 int64_t multiplesOfIC = ShapedType::kDynamic;
4778 int64_t OC = ShapedType::kDynamic;
4779 int64_t KH = ShapedType::kDynamic;
4780 int64_t KW = ShapedType::kDynamic;
4782 const ShapeAdaptor inputDataShape(getInputData().
getType());
4783 if (inputDataShape.hasRank()) {
4784 N = inputDataShape.getDimSize(0);
4785 IH = inputDataShape.getDimSize(1);
4786 IW = inputDataShape.getDimSize(2);
4787 IC = inputDataShape.getDimSize(3);
4790 const ShapeAdaptor inputScaleShape(getInputScale().
getType());
4791 if (inputScaleShape.hasRank()) {
4793 "input_scale",
"batch size")) ||
4795 "input_scale",
"input height")) ||
4797 "input_scale",
"input width")))
4799 multiplesOfIC = inputScaleShape.getDimSize(3);
4802 const ShapeAdaptor weightDataShape(getWeightData().
getType());
4803 if (weightDataShape.hasRank()) {
4804 OC = weightDataShape.getDimSize(0);
4805 KH = weightDataShape.getDimSize(1);
4806 KW = weightDataShape.getDimSize(2);
4808 "weight_data",
"input channels")))
4812 const ShapeAdaptor weightScaleShape(getWeightScale().
getType());
4813 if (weightScaleShape.hasRank()) {
4815 "weight_scale",
"output channels")) ||
4817 "weight_scale",
"kernel height")) ||
4819 "weight_scale",
"kernel width")) ||
4821 weightScaleShape.getDimSize(3),
4822 "weight_scale",
"input channel blocks")))
4826 const uint32_t blockSize = BlockSizeAttr::getBlockSizeValue(
getBlockSize());
4827 if (blockSize != BlockSizeAttr::getBlockSizeValue(BlockSize::BLOCK_SIZE_32))
4828 return emitOpError(
"expect block size to be 32, got ") << blockSize;
4830 if (ShapedType::isStatic(IC) && IC % blockSize != 0)
4831 return emitOpError(
"expect IC to be a multiple of block size, got IC=")
4832 << IC <<
", block_size=" << blockSize;
4835 if (ShapedType::isStatic(IC) && ShapedType::isStatic(multiplesOfIC) &&
4836 multiplesOfIC != IC / blockSize)
4838 "expect scale operands dimension 2 to equal IC/block_size (")
4839 << IC <<
"/" << blockSize <<
")"
4840 <<
", got " << multiplesOfIC;
4843 SmallVector<int64_t> padValues;
4845 if (llvm::any_of(padValues, [](int64_t p) {
return p < 0; }))
4846 return emitOpError(
"expect all padding values to be >= 0, got ")
4850 SmallVector<int64_t> strideValues;
4852 if (llvm::any_of(strideValues, [](int64_t s) {
return s < 1; }))
4853 return emitOpError(
"expect all stride values to be >= 1, got ")
4857 SmallVector<int64_t> dilationValues;
4860 if (llvm::any_of(dilationValues, [](int64_t d) {
return d < 1; }))
4861 return emitOpError(
"expect all dilation values to be >= 1, got ")
4866 const ShapeAdaptor outputShape(getOutput().
getType());
4867 if (!padValues.empty() && !strideValues.empty() && !dilationValues.empty() &&
4868 outputShape.hasRank()) {
4870 padValues[0], padValues[1], strideValues[0],
4871 dilationValues[0],
"height",
"y",
"top",
4874 padValues[2], padValues[3], strideValues[1],
4875 dilationValues[1],
"width",
"x",
"left",
4881 const ShapeAdaptor biasShape(getBias().
getType());
4882 if (biasShape.hasRank() && outputShape.hasRank()) {
4883 const int64_t biasChannels = biasShape.getDimSize(0);
4884 const int64_t outputChannels =
4885 outputShape.getDimSize(outputShape.getRank() - 1);
4886 if (biasChannels == ShapedType::kDynamic ||
4887 outputChannels == ShapedType::kDynamic)
4891 if (biasChannels != outputChannels && biasChannels != 1)
4893 "bias channels expected to be equal to output channels (")
4894 << outputChannels <<
") or 1, got " << biasChannels;
4900LogicalResult Conv3DOp::inferReturnTypeComponents(
4901 MLIRContext *context, ::std::optional<Location> location,
4902 Conv3DOp::Adaptor adaptor,
4903 SmallVectorImpl<ShapedTypeComponents> &inferredReturnShapes) {
4907LogicalResult Conv3DOp::verify() {
4914LogicalResult AvgPool2dOp::inferReturnTypeComponents(
4915 MLIRContext *context, ::std::optional<Location> location,
4916 AvgPool2dOp::Adaptor adaptor,
4917 SmallVectorImpl<ShapedTypeComponents> &inferredReturnShapes) {
4918 ShapeAdaptor inputShape(adaptor.getInput().getType());
4919 const Properties &prop = adaptor.getProperties();
4921 inferredReturnShapes);
4924LogicalResult AvgPool2dAdaptiveOp::inferReturnTypeComponents(
4925 MLIRContext *context, ::std::optional<Location> location,
4926 AvgPool2dAdaptiveOp::Adaptor adaptor,
4927 SmallVectorImpl<ShapedTypeComponents> &inferredReturnShapes) {
4928 ShapeAdaptor inputShape(adaptor.getInput().getType());
4930 llvm::SmallVector<int64_t> kernelValues;
4931 llvm::SmallVector<int64_t> strideValues;
4932 llvm::SmallVector<int64_t> padValues;
4939 padValues, inferredReturnShapes);
4942 llvm::SmallVector<int64_t> outputShape(4, ShapedType::kDynamic);
4943 if (inputShape.hasRank()) {
4945 outputShape[0] = inputShape.getDimSize(0);
4946 outputShape[3] = inputShape.getDimSize(3);
4949 inferredReturnShapes.push_back(ShapedTypeComponents(outputShape));
4953LogicalResult MaxPool2dOp::inferReturnTypeComponents(
4954 MLIRContext *context, ::std::optional<Location> location,
4955 MaxPool2dOp::Adaptor adaptor,
4956 SmallVectorImpl<ShapedTypeComponents> &inferredReturnShapes) {
4957 ShapeAdaptor inputShape(adaptor.getInput().getType());
4958 const Properties &prop = adaptor.getProperties();
4960 inferredReturnShapes);
4963LogicalResult MaxPool2dAdaptiveOp::inferReturnTypeComponents(
4964 MLIRContext *context, ::std::optional<Location> location,
4965 MaxPool2dAdaptiveOp::Adaptor adaptor,
4966 SmallVectorImpl<ShapedTypeComponents> &inferredReturnShapes) {
4967 ShapeAdaptor inputShape(adaptor.getInput().getType());
4969 llvm::SmallVector<int64_t> kernelValues;
4970 llvm::SmallVector<int64_t> strideValues;
4971 llvm::SmallVector<int64_t> padValues;
4978 padValues, inferredReturnShapes);
4981 llvm::SmallVector<int64_t> outputShape(4, ShapedType::kDynamic);
4982 if (inputShape.hasRank()) {
4983 outputShape[0] = inputShape.getDimSize(0);
4984 outputShape[3] = inputShape.getDimSize(3);
4986 inferredReturnShapes.push_back(ShapedTypeComponents(outputShape));
4990LogicalResult MaxPool2dOp::verify() {
5001LogicalResult MaxPool2dAdaptiveOp::verify() {
5006 AdaptivePoolingConstShapeValues values;
5010 values.pad, getInput(), getOutput())))
5016LogicalResult DepthwiseConv2DOp::inferReturnTypeComponents(
5017 MLIRContext *context, ::std::optional<Location> location,
5018 DepthwiseConv2DOp::Adaptor adaptor,
5019 SmallVectorImpl<ShapedTypeComponents> &inferredReturnShapes) {
5020 llvm::SmallVector<int64_t> outputShape(4, ShapedType::kDynamic);
5022 int64_t inputWidth = ShapedType::kDynamic;
5023 int64_t inputHeight = ShapedType::kDynamic;
5024 int64_t inputChannels = ShapedType::kDynamic;
5026 int64_t weightWidth = ShapedType::kDynamic;
5027 int64_t weightHeight = ShapedType::kDynamic;
5028 int64_t depthChannels = ShapedType::kDynamic;
5031 ShapeAdaptor inputShape(adaptor.getInput().getType());
5032 if (inputShape.hasRank()) {
5033 outputShape[0] = inputShape.getDimSize(0);
5034 inputHeight = inputShape.getDimSize(1);
5035 inputWidth = inputShape.getDimSize(2);
5036 inputChannels = inputShape.getDimSize(3);
5040 ShapeAdaptor weightShape(adaptor.getWeight().getType());
5041 if (weightShape.hasRank()) {
5042 weightHeight = weightShape.getDimSize(0);
5043 weightWidth = weightShape.getDimSize(1);
5044 inputChannels = ShapedType::isDynamic(inputChannels)
5045 ? weightShape.getDimSize(2)
5047 depthChannels = weightShape.getDimSize(3);
5052 if (ShapedType::isStatic(inputChannels) &&
5053 ShapedType::isStatic(depthChannels)) {
5054 outputShape[3] = inputChannels * depthChannels;
5058 ShapeAdaptor biasShape(adaptor.getBias().getType());
5059 if (biasShape.hasRank() && ShapedType::isDynamic(outputShape[3])) {
5060 int64_t bc = biasShape.getDimSize(0);
5061 if (bc != ShapedType::kDynamic && bc != 1)
5062 outputShape[3] = bc;
5065 llvm::ArrayRef<int64_t> dilation = adaptor.getDilation();
5066 llvm::ArrayRef<int64_t> padding = adaptor.getPad();
5067 llvm::ArrayRef<int64_t> stride = adaptor.getStride();
5069 if (ShapedType::isStatic(inputHeight) && ShapedType::isStatic(weightHeight)) {
5070 int64_t inputSize = inputHeight + padding[0] + padding[1];
5071 int64_t filterSize = (weightHeight - 1) * dilation[0] + 1;
5072 int64_t unstridedResult = inputSize - filterSize + 1;
5073 outputShape[1] = (unstridedResult - 1) / stride[0] + 1;
5076 if (ShapedType::isStatic(inputWidth) && ShapedType::isStatic(weightWidth)) {
5077 int64_t inputSize = inputWidth + padding[2] + padding[3];
5078 int64_t filterSize = (weightWidth - 1) * dilation[1] + 1;
5079 int64_t unstridedResult = inputSize - filterSize + 1;
5080 outputShape[2] = (unstridedResult - 1) / stride[1] + 1;
5083 inferredReturnShapes.push_back(ShapedTypeComponents(outputShape));
5087LogicalResult DepthwiseConv2DOp::verify() {
5094LogicalResult TransposeConv2DOp::inferReturnTypeComponents(
5095 MLIRContext *context, ::std::optional<Location> location,
5096 TransposeConv2DOp::Adaptor adaptor,
5097 SmallVectorImpl<ShapedTypeComponents> &inferredReturnShapes) {
5098 llvm::SmallVector<int64_t> outputShape(4, ShapedType::kDynamic);
5100 int64_t inputWidth = ShapedType::kDynamic;
5101 int64_t inputHeight = ShapedType::kDynamic;
5102 int64_t weightWidth = ShapedType::kDynamic;
5103 int64_t weightHeight = ShapedType::kDynamic;
5106 ShapeAdaptor inputShape(adaptor.getInput().getType());
5107 if (inputShape.hasRank()) {
5108 outputShape[0] = ShapedType::isDynamic(outputShape[0])
5109 ? inputShape.getDimSize(0)
5111 inputHeight = inputShape.getDimSize(1);
5112 inputWidth = inputShape.getDimSize(2);
5116 ShapeAdaptor weightShape(adaptor.getWeight().getType());
5117 if (weightShape.hasRank()) {
5118 outputShape[3] = ShapedType::isDynamic(outputShape[3])
5119 ? weightShape.getDimSize(0)
5121 weightHeight = weightShape.getDimSize(1);
5122 weightWidth = weightShape.getDimSize(2);
5126 ShapeAdaptor biasShape(adaptor.getBias().getType());
5127 if (biasShape.hasRank() && ShapedType::isDynamic(outputShape[3])) {
5128 int64_t bc = biasShape.getDimSize(0);
5129 if (bc != ShapedType::kDynamic && bc != 1)
5130 outputShape[3] = bc;
5133 llvm::ArrayRef<int64_t> padding = adaptor.getOutPad();
5134 llvm::ArrayRef<int64_t> stride = adaptor.getStride();
5136 if (ShapedType::isStatic(inputHeight) && ShapedType::isStatic(weightHeight)) {
5137 int64_t calculateSize =
5138 (inputHeight - 1) * stride[0] + padding[0] + padding[1] + weightHeight;
5140 ShapedType::isDynamic(outputShape[1]) ? calculateSize : outputShape[1];
5143 if (ShapedType::isStatic(inputWidth) && ShapedType::isStatic(weightWidth)) {
5144 int64_t calculateSize =
5145 (inputWidth - 1) * stride[1] + padding[2] + padding[3] + weightWidth;
5147 ShapedType::isDynamic(outputShape[2]) ? calculateSize : outputShape[2];
5150 inferredReturnShapes.push_back(ShapedTypeComponents(outputShape));
5154LogicalResult TransposeConv2DOp::verify() {
5158 const llvm::ArrayRef<int64_t> strides = getStride();
5159 const int64_t strideY = strides[0];
5160 const int64_t strideX = strides[1];
5162 if (strideY < 1 || strideX < 1)
5163 return emitOpError(
"expect all stride values to be >= 1, got [")
5166 const auto checkPadAgainstKernelDim =
5167 [
this](int64_t padValue, int64_t kernelDimSize, llvm::StringRef padName,
5168 llvm::StringRef kernelDimName) -> LogicalResult {
5169 if (padValue <= -kernelDimSize)
5170 return emitOpError(
"expected ")
5171 << padName <<
" > -" << kernelDimName <<
", but got: " << padName
5172 <<
"=" << padValue <<
" and " << kernelDimName <<
"="
5177 const llvm::ArrayRef<int64_t> padding = getOutPad();
5178 const int64_t outPadTop = padding[0];
5179 const int64_t outPadBottom = padding[1];
5180 const int64_t outPadLeft = padding[2];
5181 const int64_t outPadRight = padding[3];
5183 const auto weightType =
5184 llvm::dyn_cast<RankedTensorType>(getWeight().
getType());
5187 const int64_t kernelHeight = weightType.getDimSize(1);
5188 if (ShapedType::isStatic(kernelHeight)) {
5189 if (
failed(checkPadAgainstKernelDim(outPadTop, kernelHeight,
5190 "out_pad_top",
"KH")))
5193 if (
failed(checkPadAgainstKernelDim(outPadBottom, kernelHeight,
5194 "out_pad_bottom",
"KH")))
5198 const int64_t kernelWidth = weightType.getDimSize(2);
5199 if (ShapedType::isStatic(kernelWidth)) {
5200 if (
failed(checkPadAgainstKernelDim(outPadLeft, kernelWidth,
5201 "out_pad_left",
"KW")))
5204 if (
failed(checkPadAgainstKernelDim(outPadRight, kernelWidth,
5205 "out_pad_right",
"KW")))
5211 const auto outputType =
5212 llvm::dyn_cast<RankedTensorType>(getOutput().
getType());
5216 const auto inputType = llvm::dyn_cast<RankedTensorType>(getInput().
getType());
5217 if (inputType && weightType) {
5218 const int64_t inputHeight = inputType.getDimSize(1);
5219 const int64_t kernelHeight = weightType.getDimSize(1);
5220 const int64_t outputHeight = outputType.getDimSize(1);
5222 if (ShapedType::isStatic(inputHeight) &&
5223 ShapedType::isStatic(outputHeight)) {
5225 (inputHeight - 1) * strideY + outPadTop + outPadBottom + kernelHeight)
5227 "dimension mismatch: expected OH == (IH - 1) * stride_y "
5228 "+ out_pad_top + out_pad_bottom + KH, but got ")
5229 << outputHeight <<
" != (" << inputHeight <<
" - 1) * "
5230 << strideY <<
" + " << outPadTop <<
" + " << outPadBottom
5231 <<
" + " << kernelHeight;
5234 const int64_t inputWidth = inputType.getDimSize(2);
5235 const int64_t kernelWidth = weightType.getDimSize(2);
5236 const int64_t outputWidth = outputType.getDimSize(2);
5238 if (ShapedType::isStatic(inputWidth) && ShapedType::isStatic(outputWidth)) {
5240 (inputWidth - 1) * strideX + outPadLeft + outPadRight + kernelWidth)
5242 "dimension mismatch: expected OW == (IW - 1) * stride_x "
5243 "+ out_pad_left + out_pad_right + KW, but got ")
5244 << outputWidth <<
" != (" << inputWidth <<
" - 1) * " << strideX
5245 <<
" + " << outPadLeft <<
" + " << outPadRight <<
" + "
5250 const auto biasType = llvm::dyn_cast<RankedTensorType>(getBias().
getType());
5255 const int64_t biasChannels = biasType.getDimSize(0);
5258 if (biasChannels == ShapedType::kDynamic)
5261 const int64_t outputChannels = outputType.getDimSize(3);
5262 if (!ShapedType::isDynamic(outputChannels) &&
5263 biasChannels != outputChannels && biasChannels != 1)
5265 "bias channels expected to be equal to output channels (")
5266 << outputChannels <<
") or 1, got " << biasChannels;
5271LogicalResult RescaleOp::verify() {
5272 const auto inputType = llvm::cast<ShapedType>(getInput().
getType());
5273 auto inputElementType =
5275 if (!mlir::isa<IntegerType>(inputElementType)) {
5276 emitOpError(
"expect input to have integer element type, got ")
5277 << inputElementType;
5281 const auto outputType = llvm::cast<ShapedType>(getOutput().
getType());
5282 auto outputElementType =
5284 if (!mlir::isa<IntegerType>(outputElementType)) {
5285 emitOpError(
"expect output to have integer element type, got ")
5286 << outputElementType;
5298 FailureOr<int64_t> maybeIZp = getInputZeroPoint();
5299 if (succeeded(maybeIZp) && verifyInputZeroPoint(*maybeIZp).failed())
5302 FailureOr<int64_t> maybeOZp = getOutputZeroPoint();
5303 if (succeeded(maybeOZp) && verifyOutputZeroPoint(*maybeOZp).failed())
5306 const auto multiplierType = llvm::cast<ShapedType>(getMultiplier().
getType());
5308 if (getScale32() && !multiplierType.getElementType().isInteger(32)) {
5309 emitOpError(
"expect i32 element type for multiplier for scale32=true, got ")
5310 << multiplierType.getElementType();
5315 if (!getScale32() && !multiplierType.getElementType().isInteger(16)) {
5317 "expect i16 element type for multiplier for scale32=false, got ")
5318 << multiplierType.getElementType();
5322 if (!inputType.hasRank())
5328 int64_t numChannels = 1;
5329 if (getPerChannel()) {
5330 if (inputType.getRank() < 1) {
5331 emitOpError(
"requires input to be at least rank 1 when per_channel is "
5332 "true, but got rank ")
5333 << inputType.getRank();
5336 numChannels = inputType.getDimSize(inputType.getRank() - 1);
5339 if (outputType.hasRank()) {
5341 getOperation(), outputType, inputType.getShape())))
5345 if (multiplierType.hasRank()) {
5346 ArrayRef<int64_t> multiplierShape = multiplierType.getShape();
5348 if (multiplierShape[0] != ShapedType::kDynamic &&
5349 multiplierShape[0] != numChannels) {
5350 emitOpError(
"expect shape of { ")
5351 << numChannels <<
" } for multiplier input, got { "
5352 << multiplierShape[0] <<
" }";
5357 const auto shiftType = llvm::cast<ShapedType>(getShift().
getType());
5358 if (shiftType.hasRank()) {
5359 ArrayRef<int64_t> shiftShape = shiftType.getShape();
5361 if (shiftShape[0] != ShapedType::kDynamic && shiftShape[0] != numChannels) {
5362 emitOpError(
"expect shape of { ")
5363 << numChannels <<
" } for shift input, got { " << shiftShape[0]
5372LogicalResult RescaleOp::inferReturnTypeComponents(
5373 MLIRContext *context, ::std::optional<Location> location,
5374 RescaleOp::Adaptor adaptor,
5375 SmallVectorImpl<ShapedTypeComponents> &inferredReturnShapes) {
5376 ShapeAdaptor inputShape(adaptor.getInput().getType());
5377 inferredReturnShapes.push_back(ShapedTypeComponents(inputShape));
5381LogicalResult CastOp::verify() {
5382 const ShapedType inputType = llvm::cast<ShapedType>(getInput().
getType());
5383 const ShapedType outputType = llvm::cast<ShapedType>(
getType());
5384 const Type inputElementType = inputType.getElementType();
5385 const Type outputElementType = outputType.getElementType();
5387 const bool inputIsBlockScaled = llvm::isa<BlockScaledType>(inputElementType);
5388 const bool outputIsBlockScaled =
5389 llvm::isa<BlockScaledType>(outputElementType);
5391 const bool isUnsigned = this->getInputUnsigned();
5396 return emitOpError()
5397 <<
"attribute input_unsigned requires integer type inputs. Got: "
5400 if (!inputIsBlockScaled && !outputIsBlockScaled)
5403 if (inputIsBlockScaled && outputIsBlockScaled)
5404 return emitOpError()
5405 <<
"requires exactly one of input or output to have block scaled "
5408 const Type scalarElementType =
5409 inputIsBlockScaled ? outputElementType : inputElementType;
5410 if (!llvm::isa<FloatType>(scalarElementType))
5411 return emitOpError()
5412 <<
"requires non-block-scaled element type to be floating-point "
5413 "when casting to or from block scaled element type, got "
5414 << scalarElementType;
5419LogicalResult CastFromBlockScaledOp::inferReturnTypeComponents(
5420 MLIRContext *context, ::std::optional<Location> location,
5421 CastFromBlockScaledOp::Adaptor adaptor,
5422 SmallVectorImpl<ShapedTypeComponents> &inferredReturnShapes) {
5423 const ShapeAdaptor inputShape(adaptor.getInputData().getType());
5424 inferredReturnShapes.push_back(ShapedTypeComponents(inputShape));
5428LogicalResult CastFromBlockScaledOp::verify() {
5429 const Type inputDataType = getInputData().getType();
5430 const Type outputDataType = getResult().getType();
5432 return emitOpError() <<
"require compatible shapes for input_data ("
5433 << inputDataType <<
") and " <<
"output_data ("
5434 << outputDataType <<
")";
5436 const ShapeAdaptor inputDataShape = ShapeAdaptor(inputDataType);
5438 if (inputDataShape.
hasRank()) {
5439 const unsigned int blockSize =
5441 if (blockSize != BlockSizeAttr::getBlockSizeValue(BlockSize::BLOCK_SIZE_32))
5442 return emitOpError(
"expect block size to be 32, got ") << blockSize;
5443 const int64_t inputDataLastDim =
5445 if (inputDataLastDim % blockSize != 0)
5446 return emitOpError() <<
"expect last dimension of input_data ("
5448 <<
") to be divisible by block_size (" << blockSize
5451 const Type inputScaleType = getInputScale().getType();
5452 const ShapeAdaptor inputScaleShape = ShapeAdaptor(inputScaleType);
5454 if (inputScaleShape.
hasRank()) {
5455 SmallVector<int64_t> inputDataDims, inputScaleDims;
5456 inputDataShape.
getDims(inputDataDims);
5457 inputScaleShape.
getDims(inputScaleDims);
5459 if (inputDataDims.size() != inputScaleDims.size() ||
5461 ArrayRef<int64_t>(inputDataDims).drop_back(1),
5462 ArrayRef<int64_t>(inputScaleDims).drop_back(1))))
5463 return emitOpError()
5464 <<
"require compatible shapes for input_data (" << inputDataType
5465 <<
") and " <<
"input_scale (" << inputScaleType
5466 <<
") except for the last dimension";
5468 const SmallVector<int64_t, 2> dimsToCheck{inputDataLastDim / blockSize,
5469 inputScaleDims.back()};
5470 if (ShapedType::isStatic(inputDataLastDim) &&
5472 return emitOpError()
5473 <<
"expect last dimension of input_scale ("
5474 << inputScaleDims.back()
5475 <<
") to be equal to last dimension of input_data / block_size ("
5476 << inputDataDims.back() / blockSize <<
")";
5483LogicalResult CastToBlockScaledOp::inferReturnTypeComponents(
5484 MLIRContext *context, ::std::optional<Location> location,
5485 CastToBlockScaledOp::Adaptor adaptor,
5486 SmallVectorImpl<ShapedTypeComponents> &inferredReturnShapes) {
5487 const ShapeAdaptor inputShape(adaptor.getInputData().getType());
5488 inferredReturnShapes.push_back(ShapedTypeComponents(inputShape));
5489 if (!inputShape.hasRank())
5493 SmallVector<int64_t> outputScaleShape;
5494 inputShape.getDims(outputScaleShape);
5495 const int64_t lastDimLoc = inputShape.getRank() - 1;
5496 const int64_t lastDimSize = inputShape.getDimSize(lastDimLoc);
5497 if (ShapedType::isStatic(lastDimSize)) {
5498 const unsigned int blockSize =
5499 BlockSizeAttr::getBlockSizeValue(adaptor.getBlockSize());
5500 outputScaleShape[lastDimLoc] = lastDimSize / blockSize;
5502 inferredReturnShapes.push_back(ShapedTypeComponents(outputScaleShape));
5506LogicalResult CastToBlockScaledOp::verify() {
5507 const Type inputDataType = getInputData().getType();
5508 const Type outputDataType = getResult(0).getType();
5510 return emitOpError() <<
"require compatible shapes for input_data ("
5511 << inputDataType <<
") and " <<
"output_data ("
5512 << outputDataType <<
")";
5514 const unsigned int blockSize =
5516 if (blockSize != BlockSizeAttr::getBlockSizeValue(BlockSize::BLOCK_SIZE_32))
5517 return emitOpError(
"expect block size to be 32, got ") << blockSize;
5518 const ShapeAdaptor inputDataShape = ShapeAdaptor(inputDataType);
5519 if (inputDataShape.
hasRank()) {
5520 const int64_t inputDataLastDim =
5522 if (ShapedType::isStatic(inputDataLastDim) &&
5523 inputDataLastDim % blockSize != 0)
5524 return emitOpError() <<
"expect last dimension of input_data ("
5526 <<
") to be divisible by block_size (" << blockSize
5530 const ShapeAdaptor outputDataShape = ShapeAdaptor(outputDataType);
5531 const Type outputScaleType = getResult(1).getType();
5532 const ShapeAdaptor outputScaleShape = ShapeAdaptor(outputScaleType);
5534 SmallVector<int64_t> outputDataDims, outputScaleDims;
5535 outputDataShape.
getDims(outputDataDims);
5536 outputScaleShape.
getDims(outputScaleDims);
5538 if (outputDataDims.size() != outputScaleDims.size() ||
5540 ArrayRef<int64_t>(outputDataDims).drop_back(1),
5541 ArrayRef<int64_t>(outputScaleDims).drop_back(1))))
5542 return emitOpError() <<
"require compatible shapes for output_data ("
5543 << outputDataType <<
") and " <<
"output_scale ("
5545 <<
") except for the last dimension";
5547 const int64_t outputDataLastDim = outputDataDims.back();
5548 const SmallVector<int64_t, 2> dimsToCheck{outputDataLastDim / blockSize,
5549 outputScaleDims.back()};
5550 if (ShapedType::isStatic(outputDataLastDim) &&
5552 return emitOpError()
5553 <<
"expect last dimension of output_scale ("
5554 << outputScaleDims.back()
5555 <<
") to be equal to last dimension of output_data / block_size ("
5556 << outputDataDims.back() / blockSize <<
")";
5562LogicalResult IfOp::inferReturnTypeComponents(
5563 MLIRContext *context, ::std::optional<Location> location,
5564 IfOp::Adaptor adaptor,
5565 SmallVectorImpl<ShapedTypeComponents> &inferredReturnShapes) {
5566 llvm::SmallVector<tosa::YieldOp> yieldOps;
5567 for (Region *region : adaptor.getRegions()) {
5568 for (
auto &block : *region)
5569 if (
auto returnOp = dyn_cast<tosa::YieldOp>(block.getTerminator()))
5570 yieldOps.push_back(returnOp);
5573 if (yieldOps.empty())
5577 llvm::SmallVector<ValueKnowledge> resultKnowledge;
5578 resultKnowledge.reserve(yieldOps.front().getNumOperands());
5579 for (
auto operand : yieldOps.front().getOperands()) {
5580 resultKnowledge.push_back(
5584 for (
auto yieldOp : yieldOps) {
5585 if (resultKnowledge.size() != yieldOp.getNumOperands())
5588 for (
const auto &it : llvm::enumerate(yieldOp.getOperands())) {
5589 int32_t index = it.index();
5591 resultKnowledge[index],
5595 resultKnowledge[index] = meet;
5599 for (
const ValueKnowledge &
result : resultKnowledge) {
5600 inferredReturnShapes.push_back(
result.getShapedTypeComponents());
5606LogicalResult WhileOp::inferReturnTypeComponents(
5607 MLIRContext *context, ::std::optional<Location> location,
5608 WhileOp::Adaptor adaptor,
5609 SmallVectorImpl<ShapedTypeComponents> &inferredReturnShapes) {
5610 llvm::SmallVector<tosa::YieldOp> yieldOps;
5611 for (
auto &block : adaptor.getBodyGraph())
5612 if (
auto returnOp = dyn_cast<tosa::YieldOp>(block.getTerminator()))
5613 yieldOps.push_back(returnOp);
5617 if (yieldOps.empty())
5621 llvm::SmallVector<ValueKnowledge> resultKnowledge;
5622 resultKnowledge.reserve(yieldOps.front().getNumOperands());
5623 for (
auto operand : yieldOps.front().getOperands()) {
5624 resultKnowledge.push_back(
5628 for (
auto yieldOp : yieldOps) {
5629 if (resultKnowledge.size() != yieldOp.getNumOperands())
5632 for (
const auto &it : llvm::enumerate(yieldOp.getOperands())) {
5633 int32_t index = it.index();
5635 resultKnowledge[index],
5637 resultKnowledge[index] = meet;
5642 for (
const ValueKnowledge &
result : resultKnowledge) {
5643 inferredReturnShapes.push_back(
result.getShapedTypeComponents());
5649std::optional<SmallVector<int64_t, 4>> ApplyScaleOp::getShapeForUnroll() {
5650 if (
auto vt = llvm::dyn_cast<VectorType>(
getType()))
5651 return llvm::to_vector<4>(vt.getShape());
5652 return std::nullopt;
5658 StringRef prefix =
"") {
5659 assert(blocksArgs.size() == initializers.size() &&
5660 "expected same length of arguments and initializers");
5661 if (initializers.empty())
5664 parser << prefix <<
'(';
5665 llvm::interleaveComma(
5666 llvm::zip(blocksArgs, initializers), parser,
5667 [&](
auto it) { parser << std::get<0>(it) <<
" = " << std::get<1>(it); });
5672ParseResult IfOp::parse(OpAsmParser &parser, OperationState &
result) {
5674 result.regions.reserve(2);
5675 Region *thenRegion =
result.addRegion();
5676 Region *elseRegion =
result.addRegion();
5678 OpAsmParser::UnresolvedOperand cond;
5683 SmallVector<OpAsmParser::Argument, 4> regionArgs;
5684 SmallVector<OpAsmParser::UnresolvedOperand, 4> operands;
5687 OptionalParseResult listResult =
5695 "expected type for condition operand");
5701 "expected type for condition operand");
5709 FunctionType functionType;
5713 <<
"expected list of types for block arguments "
5714 <<
"followed by arrow type and list of return types";
5716 result.addTypes(functionType.getResults());
5718 if (functionType.getNumInputs() != operands.size()) {
5720 <<
"expected as many input types as operands " <<
"(expected "
5721 << operands.size() <<
" got " << functionType.getNumInputs()
5752void IfOp::print(OpAsmPrinter &p) {
5753 p <<
" " << getCondition();
5756 getInputList(),
" ");
5758 p << getCondition().getType();
5760 if (!getInputList().empty()) {
5762 llvm::interleaveComma(getInputList().getTypes(), p);
5771 auto &elseRegion = getElseGraph();
5772 if (!elseRegion.
empty()) {
5780LogicalResult IfOp::verify() {
5782 "'then_graph' arguments", getInputList(),
5788 "'else_graph' arguments", getInputList(),
5794 if (getThenGraph().front().mightHaveTerminator()) {
5796 dyn_cast<tosa::YieldOp>(getThenGraph().front().getTerminator());
5798 *
this, thenYield.getInputs(),
"'then_graph' results",
5799 getOutputList(),
"'output_list'")
5805 if (getElseGraph().front().mightHaveTerminator()) {
5807 dyn_cast<tosa::YieldOp>(getElseGraph().front().getTerminator());
5809 *
this, elseYield.getInputs(),
"'else_graph' results",
5810 getOutputList(),
"'output_list'")
5815 auto condType = getCondition().getType();
5817 return emitOpError() <<
"'condition' must be a size 1 tensor, got "
5823LogicalResult WhileOp::verify() {
5825 getOutputList(),
"'output_list'")
5830 "'cond_graph' arguments", getInputList(),
5836 "'body_graph' arguments", getInputList(),
5841 if (getBodyGraph().front().mightHaveTerminator()) {
5843 dyn_cast<tosa::YieldOp>(getBodyGraph().front().getTerminator());
5845 "'body_graph' results",
5846 getInputList(),
"'input_list'")
5853 if (!getCondGraph().front().mightHaveTerminator())
5857 dyn_cast<tosa::YieldOp>(getCondGraph().front().getTerminator());
5861 if (condYield.getInputs().size() != 1)
5862 return emitOpError() <<
"require 'cond_graph' only have one result";
5864 auto condOutType = condYield.getInputs()[0].getType();
5866 return emitOpError() <<
"'cond_graph' result must be a size 1 tensor, got "
5870 return emitOpError() <<
"'cond_graph' result must be a boolean tensor, got "
5876LogicalResult ReverseOp::verify() {
5877 TensorType inputType = getInput1().getType();
5878 int32_t reverseAxis = getAxis();
5880 if (reverseAxis < 0)
5881 return emitOpError(
"expected non-negative reverse axis");
5883 int64_t inputRank = inputType.getRank();
5886 if (reverseAxis >= inputRank && (reverseAxis != 0 || inputRank != 0))
5887 return emitOpError(
"expect input tensor rank (")
5888 << inputRank <<
") to be larger than reverse axis (" << reverseAxis
5895LogicalResult tosa::SelectOp::verify() {
5906 auto predicateType = llvm::dyn_cast<ShapedType>(getPred().
getType());
5907 if (!predicateType) {
5908 return emitOpError(
"expect shaped tensor for input1, got ")
5909 << getInput1().getType();
5911 auto predicateElementType = predicateType.getElementType();
5912 if (!predicateElementType.isInteger(1)) {
5913 return emitOpError(
"expect element type of bool for input1, got ")
5914 << predicateElementType;
5920LogicalResult tosa::VariableReadOp::verify() {
5928LogicalResult tosa::VariableWriteOp::verify() {
5937ParseResult WhileOp::parse(OpAsmParser &parser, OperationState &
result) {
5938 SmallVector<OpAsmParser::Argument, 4> regionArgs;
5939 SmallVector<OpAsmParser::UnresolvedOperand, 4> operands;
5940 Region *cond =
result.addRegion();
5941 Region *body =
result.addRegion();
5943 OptionalParseResult listResult =
5948 FunctionType functionType;
5953 result.addTypes(functionType.getResults());
5955 if (functionType.getNumInputs() != operands.size()) {
5957 <<
"expected as many input types as operands " <<
"(expected "
5958 << operands.size() <<
" got " << functionType.getNumInputs() <<
")";
5968 for (
size_t i = 0, e = regionArgs.size(); i != e; ++i)
5969 regionArgs[i].type = functionType.getInput(i);
5971 return failure(parser.
parseRegion(*cond, regionArgs) ||
5976void WhileOp::print(OpAsmPrinter &parser) {
5978 getInputList(),
" ");
5981 getResults().getTypes());
5987 (*this)->getDiscardableAttrDictionary().getValue());
5996 auto zpType = mlir::RankedTensorType::get({1}, srcElemType);
5997 if (llvm::isa<FloatType>(srcElemType)) {
5999 zpType, builder.
getFloatAttr(srcElemType,
static_cast<double>(zp)));
6000 return tosa::ConstOp::create(builder, loc, zpType, zpAttr);
6002 if (llvm::isa<IntegerType>(srcElemType)) {
6005 return tosa::ConstOp::create(builder, loc, zpType, zpAttr);
6007 llvm::errs() <<
"zero point is not allowed for unsupported data types\n";
6008 return std::nullopt;
6016 return mlir::isa<tosa::shapeType>(t);
6023 return emitError() <<
"invalid rank (must be >= 0): " << rank;
6029 if (mlir::isa<::mlir::tosa::shapeType>(v.getType())) {
6030 Operation *definingOp = v.getDefiningOp();
6032 return op->
emitOpError(
"shape operand is not compile time resolvable");
6045 auto getRank = [](
const Type type) {
6046 return mlir::cast<mlir::tosa::shapeType>(type).getRank();
6052 for (
auto type : operandTypes) {
6053 if (getRank(type) != rank) {
6054 return op->
emitOpError(
"operands don't have matching ranks");
6057 for (
auto type : resultTypes) {
6058 if (getRank(type) != rank) {
6059 return op->
emitOpError(
"result shape has different rank than operands");
6069LogicalResult tosa::ConstShapeOp::verify() {
6071 auto valuesRank = getValues().getType().getRank();
6072 if (valuesRank != 1)
6073 return emitOpError(
"expect elements in attribute values with rank 1");
6075 auto count = getValues().getNumElements();
6076 auto rank = (cast<tosa::shapeType>(getResult().
getType())).getRank();
6077 if (count != rank && (count != 1 || rank != 0)) {
6078 return emitOpError(
"expect number of elements in attribute values (")
6079 << count <<
") to be equal to the rank (" << rank
6080 <<
") for the result shape type";
6085LogicalResult tosa::DimOp::verify() {
6086 const tosa::shapeType outShapeType =
6087 cast<tosa::shapeType>(getResult().
getType());
6088 if (outShapeType.getRank() != 1)
6089 return emitOpError(
"expect output shape type to contain one element, got ")
6094 const int64_t inputRank = inputType.getRank();
6095 const int64_t axis = getAxisAttr().getInt();
6096 if (axis < 0 || axis >= inputRank)
6097 return emitOpError(
"expect axis to be in the range [0, ")
6098 << inputRank <<
"), got " << axis;
6103LogicalResult tosa::ConcatShapeOp::verify() {
6104 const tosa::shapeType outShapeType =
6105 cast<tosa::shapeType>(getResult().
getType());
6106 const int64_t outputRank = outShapeType.getRank();
6109 if (inputList.size() == 0)
6110 return emitOpError(
"requires at least one input shape");
6112 if (llvm::any_of(inputList, [](Value v) {
6113 return cast<tosa::shapeType>(v.
getType()).getRank() == 0;
6115 return emitOpError(
"requires all inputs shapes have a rank greater than 0");
6117 const int64_t inputsRank =
6118 llvm::accumulate(inputList, 0, [](int64_t acc,
const Value &input) {
6119 const tosa::shapeType inShapeType =
6120 cast<tosa::shapeType>(input.
getType());
6121 return acc + inShapeType.getRank();
6123 if (outputRank != inputsRank)
6124 return emitOpError(
"requires output shape rank to be equal to the sum of "
6125 "the input shape ranks (")
6126 << inputsRank <<
"), got " << outputRank;
6131LogicalResult tosa::SliceShapeOp::verify() {
6132 std::optional<int32_t> start;
6133 DenseIntElementsAttr startAttr;
6135 start = startAttr.getValues<int32_t>()[0];
6136 if (start && start.value() < 0)
6137 return emitOpError(
"expected non-negative start index, got ")
6140 std::optional<int32_t> size;
6141 DenseIntElementsAttr sizeAttr;
6143 size = sizeAttr.getValues<int32_t>()[0];
6144 if (size && size.value() <= 0)
6145 return emitOpError(
"expected positive size, got ") << size.value();
6150 const tosa::shapeType outShapeType =
6151 cast<tosa::shapeType>(getResult().
getType());
6152 const int64_t outputRank = outShapeType.getRank();
6153 if (outputRank != size)
6155 "expected output type size to be equal to size attribute, got ")
6156 << outputRank <<
" vs " << size.value();
6161 const tosa::shapeType inShapeType =
6162 cast<tosa::shapeType>(getInput().
getType());
6163 const int64_t inputRank = inShapeType.getRank();
6164 const int64_t sliceSize = start.value() + size.value();
6165 if (sliceSize > inputRank)
6166 return emitOpError(
"expected start + size to be less than or equal to "
6167 "input shape rank (")
6168 << inputRank <<
"), got " << sliceSize;
6177#define GET_ATTRDEF_CLASSES
6178#include "mlir/Dialect/Tosa/IR/TosaAttributes.cpp.inc"
6183#define GET_TYPEDEF_CLASSES
6184#include "mlir/Dialect/Tosa/IR/TosaOpsTypesBase.cpp.inc"
6190#define GET_OP_CLASSES
6191#include "mlir/Dialect/Tosa/IR/TosaOps.cpp.inc"
static void printInitializationList(OpAsmPrinter &p, Block::BlockArgListType blocksArgs, ValueRange initializers, StringRef prefix="")
Prints the initialization list in the form of <prefix>(inner = outer, inner2 = outer2,...
true
Given two iterators into the same block, return "true" if a is before `b.
static bool isLegalToInline(InlinerInterface &interface, Region *src, Region *insertRegion, bool shouldCloneInlinedRegion, IRMapping &valueMapping)
Utility to check that all of the operations within 'src' can be inlined.
static std::string diag(const llvm::Value &value)
static Type getValueType(Attribute attr)
static Type getElementType(Type type, ArrayRef< int32_t > indices, function_ref< InFlightDiagnostic(StringRef)> emitErrorFn)
Walks the given type hierarchy with the given indices, potentially down to component granularity,...
static void printShapeToDiagnostic(InFlightDiagnostic &diag, ArrayRef< int64_t > shape)
static void buildMatMulOpWithQuantInfo(OpBuilder &builder, OperationState &result, Type outputType, Value a, Value b)
static LogicalResult verifySameElementTypes(Operation *op, Type aType, Type bType, StringRef aName="input", StringRef bName="output")
LogicalResult inferConvReturnTypeComponents(AdaptorT adaptor, SmallVectorImpl< ShapedTypeComponents > &inferredReturnShapes)
static SmallVector< int64_t > convertToMlirShape(ArrayRef< int64_t > shape)
static LogicalResult ReduceInferReturnTypes(ShapeAdaptor operandShape, Type inputType, IntegerAttr axis, SmallVectorImpl< ShapedTypeComponents > &inferredReturnShapes)
static void printScaleValues(AsmPrinter &printer, ArrayRef< Attribute > scaleValues, Type)
static void buildAvgPool2dAdaptiveOpWithQuantInfo(OpBuilder &builder, OperationState &result, Type outputType, Value input, DenseI64ArrayAttr kernel, DenseI64ArrayAttr stride, DenseI64ArrayAttr pad, TypeAttr accType)
This builder mirrors avg_pool2d quant-info handling and materializes kernel/stride/pad as const_shape...
static LogicalResult verifyRescaleValueAndZpTypes(Operation *op, Value val, Value valZp, StringRef name)
static LogicalResult errorIfShapeNotSizeOne(Operation *op, Type type)
static LogicalResult verifyMatMulZeroPointType(T op, Value input, Value zp, StringRef inputName, StringRef zpName)
static ParseResult parseScaleValues(AsmParser &parser, SmallVector< Attribute > &scaleValues, Type scaleType)
#define REDUCE_SHAPE_INFER(OP)
static LogicalResult verifyConvOp(T op)
static LogicalResult verifyAvgPoolCommonTypeAndZpChecks(T op)
static LogicalResult verifyVariableOpErrorIf(T op, Type type, StringRef name)
static LogicalResult poolingInferReturnTypes(ShapeAdaptor inputShape, ArrayRef< int64_t > kernel, ArrayRef< int64_t > stride, ArrayRef< int64_t > pad, SmallVectorImpl< ShapedTypeComponents > &inferredReturnShapes)
static void buildPadOpWithQuantInfo(OpBuilder &builder, OperationState &result, Type outputType, Value input, Value paddings)
This builder is called on TOSA pad operator that needs to create its own OptionalAttr quantization_at...
static LogicalResult verifyPoolingOpImpl(Operation *op, ArrayRef< int64_t > kernel, ArrayRef< int64_t > strides, ArrayRef< int64_t > padding, Value input, Value output)
static std::optional< int64_t > idivCheck(const int64_t lhs, const int64_t rhs)
static void buildVariableOp(OpBuilder &builder, OperationState &result, StringRef name, Type variableType, Attribute initialValue)
static void buildMatMulLikeOpWithQuantInfo(OpBuilder &builder, OperationState &result, Type outputType, Value a, Value b)
LogicalResult verifyConvOutputSize(Operation *op, const int64_t inputSize, const int64_t kernelSize, const int64_t outputSize, const int64_t padBefore, const int64_t padAfter, const int64_t stride, const int64_t dilation, const llvm::StringRef dimName, const llvm::StringRef dimAxis, const llvm::StringRef padBeforeName, const llvm::StringRef padAfterName)
static LogicalResult verifyReduceOp(T op)
#define NARY_SHAPE_INFER(OP)
#define ZERO_POINT_HELPER(OP, OPERAND_NAME, SIGN_EXTEND)
static void buildTransConvOpWithQuantInfo(OpBuilder &builder, OperationState &result, Type outputType, Value input, Value weight, Value bias, DenseI64ArrayAttr outpad, DenseI64ArrayAttr stride, TypeAttr accType)
Handles tosa.transpose_conv2d which has outpad and output shape attributes.
static void extractAdaptivePoolingConstShapeOperands(T op, AdaptivePoolingConstShapeValues &values)
static LogicalResult verifyConvOpErrorIf(T op)
static FailureOr< int64_t > getZeroPoint(Value val, bool signExtend)
static constexpr bool IsSupportedAdaptivePoolConstShapeVerifyOp
LogicalResult tryUpdateDimOrFailure(Operation *op, int64_t &currDim, const int64_t newDim, const StringRef operandName, const StringRef dimName)
static LogicalResult verifyConvOpModes(T op)
static LogicalResult NAryInferReturnTypes(const ValueShapeRange &operands, SmallVectorImpl< ShapedTypeComponents > &inferredReturnShapes)
#define COMPATIBLE_RETURN_TYPES(OP)
static LogicalResult resolveBroadcastShape(const ValueShapeRange &operands, SmallVector< int64_t > &outShape)
static LogicalResult verifyMatMulQuantizedOperandsType(T op, Type aElementType, Type bElementType)
static LogicalResult verifyOutputShapeCompatibleWithExpected(Operation *op, ShapedType outputType, ArrayRef< int64_t > expectedShape, StringRef outputName="output")
static void buildNegateOpWithQuantInfo(OpBuilder &builder, OperationState &result, Type outputType, Value input)
This builder is called on single-parameter negate operator to construct input and output zero points ...
static void buildConvOpWithQuantInfo(OpBuilder &builder, OperationState &result, Type outputType, Value input, Value weight, Value bias, DenseI64ArrayAttr pad, DenseI64ArrayAttr stride, DenseI64ArrayAttr dilation, TypeAttr accType)
This builder is called on all convolution operators except TransposeConv, which has specialized outpu...
static void buildAvgPool2dOpWithQuantInfo(OpBuilder &builder, OperationState &result, Type outputType, Value input, DenseArrayAttr kernel, DenseArrayAttr stride, DenseArrayAttr pad, TypeAttr accType)
Both the tosa.avg_pool2d and unary ops use the same UnaryOpQuantizationAttr but avg_pool operator has...
static LogicalResult errorIfTypeOrShapeMismatch(Operation *op, Type type1, StringRef name1, Type type2, StringRef name2)
static void buildMatMulTOpWithQuantInfo(OpBuilder &builder, OperationState &result, Type outputType, Value a, Value b)
static FailureOr< int64_t > resolveBroadcastDim(const int64_t dim1, const int64_t dim2)
static LogicalResult verifyZeroPoint(T op, Value val, const int64_t &zp, const std::string &operand)
static LogicalResult verifyPoolingOp(T op)
static LogicalResult verifyDimIsPowerOfTwo(Operation *op, const int64_t dimSize, const llvm::StringRef dimName)
static ArrayRef< int64_t > getShape(Type type)
Returns the shape of the given type.
static void updateIfDynamic(int64_t ¤t, int64_t candidate)
void inferWeightShape(SmallVectorImpl< int64_t > &outputShape, SmallVectorImpl< int64_t > &weightSpatial)
LogicalResult getSpatialParameters(SmallVector< int64_t > &padValues, SmallVector< int64_t > &strideValues, SmallVector< int64_t > &dilationValues)
void inferInputShape(SmallVectorImpl< int64_t > &outputShape, SmallVectorImpl< int64_t > &inputSpatial)
ConvInferShapeAdaptor(Conv2DBlockScaledOp::Adaptor adaptor)
int64_t getOutputRank() const
int64_t getNumSpatialDims() const
void inferInputShape(SmallVectorImpl< int64_t > &outputShape, SmallVectorImpl< int64_t > &inputSpatial)
void inferWeightShape(SmallVectorImpl< int64_t > &outputShape, SmallVectorImpl< int64_t > &weightSpatial)
ConvInferShapeAdaptor(Conv2DOp::Adaptor adaptor)
int64_t getNumSpatialDims() const
int64_t getOutputRank() const
LogicalResult getSpatialParameters(SmallVector< int64_t > &padValues, SmallVector< int64_t > &strideValues, SmallVector< int64_t > &dilationValues)
int64_t getNumSpatialDims() const
void inferWeightShape(SmallVectorImpl< int64_t > &outputShape, SmallVectorImpl< int64_t > &weightSpatial)
int64_t getOutputRank() const
ConvInferShapeAdaptor(Conv3DOp::Adaptor adaptor)
void inferInputShape(SmallVectorImpl< int64_t > &outputShape, SmallVectorImpl< int64_t > &inputSpatial)
LogicalResult getSpatialParameters(SmallVector< int64_t > &padValues, SmallVector< int64_t > &strideValues, SmallVector< int64_t > &dilationValues)
This base class exposes generic asm parser hooks, usable across the various derived parsers.
virtual ParseResult parseOptionalRBrace()=0
Parse a } token if present.
virtual ParseResult parseCommaSeparatedList(Delimiter delimiter, function_ref< ParseResult()> parseElementFn, StringRef contextMessage=StringRef())=0
Parse a list of comma-separated items with an optional delimiter.
virtual ParseResult parseOptionalAttrDict(NamedAttrList &result)=0
Parse a named dictionary into 'result' if it is present.
virtual ParseResult parseOptionalEqual()=0
Parse a = token if present.
virtual ParseResult parseOptionalKeyword(StringRef keyword)=0
Parse the given keyword if present.
MLIRContext * getContext() const
virtual InFlightDiagnostic emitError(SMLoc loc, const Twine &message={})=0
Emit a diagnostic at the specified location and return failure.
virtual ParseResult parseOptionalColon()=0
Parse a : token if present.
virtual ParseResult parseRBrace()=0
Parse a } token.
virtual ParseResult parseEqual()=0
Parse a = token.
virtual ParseResult parseOptionalAttrDictWithKeyword(NamedAttrList &result)=0
Parse a named dictionary into 'result' if the attributes keyword is present.
virtual ParseResult parseColonType(Type &result)=0
Parse a colon followed by a type.
virtual SMLoc getCurrentLocation()=0
Get the location of the next token and store it into the argument.
virtual ParseResult parseOptionalComma()=0
Parse a , token if present.
virtual ParseResult parseColon()=0
Parse a : token.
virtual ParseResult parseType(Type &result)=0
Parse a type.
virtual ParseResult parseOptionalArrowTypeList(SmallVectorImpl< Type > &result)=0
Parse an optional arrow followed by a type list.
virtual ParseResult parseFloat(double &result)=0
Parse a floating point value from the stream.
ParseResult parseKeyword(StringRef keyword)
Parse a given keyword.
virtual ParseResult parseAttribute(Attribute &result, Type type={})=0
Parse an arbitrary attribute of a given type and return it in result.
virtual ParseResult parseOptionalLBrace()=0
Parse a { token if present.
This base class exposes generic asm printer hooks, usable across the various derived printers.
virtual void printAttributeWithoutType(Attribute attr)
Print the given attribute without its type.
virtual void printAttribute(Attribute attr)
void printArrowTypeList(TypeRange &&types)
Attributes are known-constant values of operations.
MutableArrayRef< BlockArgument > BlockArgListType
This class is a general helper class for creating context-global objects like types,...
IntegerAttr getIndexAttr(int64_t value)
IntegerAttr getIntegerAttr(Type type, int64_t value)
FloatAttr getFloatAttr(Type type, double value)
IntegerType getIntegerType(unsigned width)
StringAttr getStringAttr(const Twine &bytes)
DenseIntElementsAttr getIndexTensorAttr(ArrayRef< int64_t > values)
An attribute that represents a reference to a dense vector or tensor object.
auto getValues() const
Return the held element values as a range of the given type.
static DenseElementsAttr get(ShapedType type, ArrayRef< Attribute > values)
Constructs a dense elements attribute from an array of element values.
An attribute that represents a reference to a dense integer vector or tensor object.
This class contains all of the information necessary to report a diagnostic to the DiagnosticEngine.
virtual InFlightDiagnostic emitError(const Twine &msg={}) const =0
Emit an error to the reader.
ImplicitLocOpBuilder maintains a 'current location', allowing use of the create<> method without spec...
This class represents a diagnostic that is inflight and set to be reported.
This class defines the main interface for locations in MLIR and acts as a non-nullable wrapper around...
MLIRContext is the top-level object for a collection of MLIR operations.
NamedAttrList is array of NamedAttributes that tracks whether it is sorted and does some basic work t...
void push_back(NamedAttribute newAttribute)
Add an attribute with the specified name.
NamedAttribute represents a combination of a name and an Attribute value.
StringAttr getName() const
Return the name of the attribute.
Attribute getValue() const
Return the value of the attribute.
The OpAsmParser has methods for interacting with the asm parser: parsing things from it,...
virtual OptionalParseResult parseOptionalAssignmentList(SmallVectorImpl< Argument > &lhs, SmallVectorImpl< UnresolvedOperand > &rhs)=0
virtual ParseResult parseRegion(Region ®ion, ArrayRef< Argument > arguments={}, bool enableNameShadowing=false)=0
Parses a region.
virtual ParseResult resolveOperand(const UnresolvedOperand &operand, Type type, SmallVectorImpl< Value > &result)=0
Resolve an operand to an SSA value, emitting an error on failure.
ParseResult resolveOperands(Operands &&operands, Type type, SmallVectorImpl< Value > &result)
Resolve a list of operands to SSA values, emitting an error on failure, or appending the results to t...
virtual ParseResult parseOperand(UnresolvedOperand &result, bool allowResultNumber=true)=0
Parse a single SSA value operand name along with a result number if allowResultNumber is true.
This is a pure-virtual base class that exposes the asmprinter hooks necessary to implement a custom p...
void printOperands(const ContainerType &container)
Print a comma separated list of operands.
virtual void printOptionalAttrDictWithKeyword(ArrayRef< NamedAttribute > attrs, ArrayRef< StringRef > elidedAttrs={})=0
If the specified operation has attributes, print out an attribute dictionary prefixed with 'attribute...
virtual void printOptionalAttrDict(ArrayRef< NamedAttribute > attrs, ArrayRef< StringRef > elidedAttrs={})=0
If the specified operation has attributes, print out an attribute dictionary with their values.
void printFunctionalType(Operation *op)
Print the complete type of an operation in functional form.
virtual void printRegion(Region &blocks, bool printEntryBlockArgs=true, bool printBlockTerminators=true, bool printEmptyBlock=false)=0
Prints a region.
This class helps build Operations.
This class indicates that op operates on tosa shape types.
void walkInherentAttrs(Operation *op, InherentAttrVisitor visitor) const
Visit the inherent attributes stored in the properties of op.
Operation is the basic unit of execution within MLIR.
ResultRange result_range
Support result iteration.
bool hasTrait()
Returns true if the operation was registered with a particular trait, e.g.
OperandRange operand_range
OperationName getName()
The name of an operation is the key identifier for it.
DictionaryAttr getDiscardableAttrDictionary()
Return all of the discardable attributes on this operation as a DictionaryAttr.
operand_type_range getOperandTypes()
result_type_range getResultTypes()
operand_range getOperands()
Returns an iterator on the underlying Value's.
InFlightDiagnostic emitOpError(const Twine &message={})
Emit an error with the op name prefixed, like "'dim' op " which is convenient for verifiers.
ParseResult value() const
Access the internal ParseResult value.
bool has_value() const
Returns true if we contain a valid ParseResult value.
Type-safe wrapper around a void* for passing properties, including the properties structs of operatio...
This class provides an abstraction over the different types of ranges over Regions.
This diagnostic handler is a simple RAII class that registers and erases a diagnostic handler on a gi...
Adaptor class to abstract the differences between whether value is from a ShapedType or ShapedTypeCom...
bool isDynamicDim(int index) const
Returns whether the index'th dimension is dynamic.
int64_t getDimSize(int index) const
Returns the size of the index'th dimension.
int64_t getRank() const
Returns the rank of the shape.
bool hasStaticShape() const
Returns whether the shape is fully static.
int64_t getNumElements() const
Returns the number of elements in the shape.
void getDims(SmallVectorImpl< int64_t > &res) const
Populates the dimensions from shape referenced.
bool hasRank() const
Returns whether the shape has a rank.
ShapedTypeComponents that represents the components of a ShapedType.
This class allows for representing and managing the symbol table used by operations with the 'SymbolT...
Operation * lookup(StringRef name) const
Look up a symbol with the specified name, returning null if no such name exists.
Tensor types represent multi-dimensional arrays, and have two variants: RankedTensorType and Unranked...
ArrayRef< int64_t > getShape() const
Returns the shape of this tensor type.
bool hasRank() const
Returns if this type is ranked, i.e. it has a known number of dimensions.
This class provides an abstraction over the various different ranges of value types.
Instances of the Type class are uniqued, have an immutable identifier and an optional mutable compone...
MLIRContext * getContext() const
Return the MLIRContext in which this type was uniqued.
bool isSignlessInteger() const
Return true if this is a signless integer type (with the specified width).
bool isUnsignedInteger() const
Return true if this is an unsigned integer type (with the specified width).
bool isInteger() const
Return true if this is an integer type (with the specified width).
unsigned getIntOrFloatBitWidth() const
Return the bit width of an integer or a float type, assert failure on other types.
This class provides an abstraction over the different types of ranges over Values.
type_range getTypes() const
Range of values and shapes (corresponding effectively to Shapes dialect's ValueShape type concept).
ShapeAdaptor getShape(int index) const
Returns the shape of index'th operand.
This class represents an instance of an SSA value in the MLIR system, representing a computable value...
Type getType() const
Return the type of this value.
Operation * getDefiningOp() const
If this value is the result of an operation, return the operation that defines it.
ArrayRef< T > asArrayRef() const
LogicalResult verifyAtLeastNOperands(Operation *op, unsigned numOperands)
LogicalResult verifyTosaShapeOperatorWithSameRanks(Operation *op)
LogicalResult verifyTosaResolvableShapeOperands(Operation *op)
bool getBroadcastedShape(ArrayRef< int64_t > shape1, ArrayRef< int64_t > shape2, SmallVectorImpl< int64_t > &resultShape)
Returns true and sets resultShape to the broadcasted shape from the two given shapes if they are broa...
LogicalResult convertFloatTypeFromAttribute(Type type, Attribute attr, llvm::SmallVectorImpl< char > &result)
Float type implementation of DenseElementTypeInterface::convertFromAttribute.
Attribute convertFloatTypeToAttribute(Type type, llvm::ArrayRef< char > rawData)
Float type implementation of DenseElementTypeInterface::convertToAttribute.
Operation::operand_range getIndices(Operation *op)
Get the indices that the given load/store operation is operating on.
SmallVector< unsigned > getBlockSize(AffineMap dimToLvl)
Given the dimToLvl map, returns the block sizes in a vector.
ConvOpQuantizationAttr buildConvOpQuantizationAttr(OpBuilder &builder, Value input, Value weight)
Method to build ConvOpQuantizationAttr, called from ConvOpQuantInfoBuilder/TransConvOpQuantInfoBuilde...
Type getStorageElementTypeOrSelf(Type type)
RankedTensorType getVariableType(VariableOp variableOp)
Type buildConvOpResultTypeInfo(OpBuilder &builder, Type outputType, Value input, Value weight)
construct ConvOp output type with correct bitwidth based on input/weight width.
ParseResult parseVariableOpTypeOrInitialValue(OpAsmParser &parser, DenseElementsAttr &varShapeAttr, TypeAttr &typeAttr, Attribute &initialValueAttr)
PadOpQuantizationAttr buildPadOpQuantizationAttr(OpBuilder &builder, Value input)
Builds PadOpQuantizationAttr, called from PadOpQuantInfoBuilder: inputZp: input zeropoint.
constexpr int64_t kInferableDimSize
Represents a dimension in the shape of a tensor that can be inferred based on the other provided dime...
std::pair< Value, Value > createZPsAsConst(OpBuilder &builder, Value input, Value weight)
void printVariableOpTypeOrInitialValue(OpAsmPrinter &p, Operation *op, DenseElementsAttr varShapeAttr, TypeAttr typeAttr, Attribute initialValueAttr)
FailureOr< T > getConstantScalarIntValue(Value val)
Value getTosaConstShape(ImplicitLocOpBuilder &builder, llvm::ArrayRef< int64_t > shape)
MatMulOpQuantizationAttr buildMatMulOpQuantizationAttr(OpBuilder &builder, Value a, Value b)
Builds MatMulOpQuantizationAttr, called from MatMulOpQuantInfoBuilder: aZp: input a zeropoint bZp: in...
unsigned getBitWidth(Type type)
std::optional< Value > createZeroPointTensor(OpBuilder &builder, Location loc, Type srcElemType, int64_t zp=0)
bool isa_tosa_shape_type(mlir::Type t)
SmallVector< int64_t > convertFromMlirShape(ArrayRef< int64_t > shape)
UnaryOpQuantizationAttr buildUnaryOpQuantizationAttr(OpBuilder &builder, Value input, Type outputRawType)
Builds UnaryOpQuantizationAttr UnaryOpQuantInfoBuilder: inputZp: input zeropoint outputZp: output zer...
Type getStorageElementTypeFromQuantized(quant::QuantizedType quantizedType)
Value createPadConstTensor(OpBuilder &builder, Location loc, Value src, int32_t val=0)
LogicalResult verifyBlockScaledTensorType(mlir::Type type, llvm::function_ref< mlir::InFlightDiagnostic()> emitError=nullptr, bool allowScaleValues=false)
std::string getTosaTensorTypeErrorMessage(mlir::Type type)
bool getConstShapeValues(Operation *op, llvm::SmallVector< int64_t > &result_shape)
Include the generated interface declarations.
bool matchPattern(Value value, const Pattern &pattern)
Entry point for matching a pattern over a Value.
detail::DenseArrayAttrImpl< int64_t > DenseI64ArrayAttr
LogicalResult verifyCompatibleShapes(TypeRange types1, TypeRange types2)
Returns success if the given two arrays have the same number of elements and each pair wise entries h...
Type getType(OpFoldResult ofr)
Returns the int type of the integer in ofr.
LogicalResult emitOptionalError(std::optional< Location > loc, Args &&...args)
Overloads of the above emission functions that take an optionally null location.
InFlightDiagnostic emitError(Location loc)
Utility method to emit an error message using this location.
SmallVector< SmallVector< OpFoldResult > > ReifiedRankedShapedTypeDims
Type getElementTypeOrSelf(Type type)
Return the element type or return the type itself.
LogicalResult verifyCompatibleDims(ArrayRef< int64_t > dims)
Dimensions are compatible if all non-dynamic dims are equal.
LogicalResult verifyRanksMatch(Operation *op, ShapedType lhs, ShapedType rhs, StringRef lhsName, StringRef rhsName)
Verify that two shaped types have matching ranks.
LogicalResult verifyCompatibleShape(ArrayRef< int64_t > shape1, ArrayRef< int64_t > shape2)
Returns success if the given two shapes are compatible.
detail::constant_op_matcher m_Constant()
Matches a constant foldable operation.
llvm::function_ref< Fn > function_ref
bool isPermutationVector(ArrayRef< int64_t > interchange)
Method to check if an interchange vector is a permutation.
This represents an operation in an abstracted form, suitable for use with the builder APIs.
static ValueKnowledge meet(const ValueKnowledge &lhs, const ValueKnowledge &rhs)
static ValueKnowledge getKnowledgeFromType(Type type)