1b2812113SSuraj Sudhir //===- QuantUtils.cpp -----------------------------------------------------===//
2b2812113SSuraj Sudhir //
3b2812113SSuraj Sudhir // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4b2812113SSuraj Sudhir // See https://llvm.org/LICENSE.txt for license information.
5b2812113SSuraj Sudhir // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6b2812113SSuraj Sudhir //
7b2812113SSuraj Sudhir //===----------------------------------------------------------------------===//
8b2812113SSuraj Sudhir //
9b2812113SSuraj Sudhir // This file contains TOSA numerical support functions and quantization
10b2812113SSuraj Sudhir // attribute builders.
11b2812113SSuraj Sudhir //
12b2812113SSuraj Sudhir //===----------------------------------------------------------------------===//
13b2812113SSuraj Sudhir
14b2812113SSuraj Sudhir #include "mlir/Dialect/Tosa/Utils/QuantUtils.h"
15b2812113SSuraj Sudhir
16b2812113SSuraj Sudhir using namespace mlir;
17b2812113SSuraj Sudhir using namespace mlir::tosa;
18b2812113SSuraj Sudhir
19b2812113SSuraj Sudhir /// From a scale value, generates multiplier and shift values where
20b2812113SSuraj Sudhir /// mantissa is in [-1.0,-0.5] or [0.5, 1.0] such that
21b2812113SSuraj Sudhir /// multiplier = mantissa*2^shift for 16-bit scaling.
computeMultiplierAndShiftTosaScale16(double scale,int32_t & multiplier,int32_t & shift)22ac3587f2SStella Laurenzo static void computeMultiplierAndShiftTosaScale16(double scale,
23ac3587f2SStella Laurenzo int32_t &multiplier,
24b2812113SSuraj Sudhir int32_t &shift) {
25b2812113SSuraj Sudhir
26b2812113SSuraj Sudhir const double mantissa = std::frexp(scale, &shift);
27b2812113SSuraj Sudhir auto shiftedM = std::round(mantissa * (int64_t(1) << 15));
28b2812113SSuraj Sudhir
29b2812113SSuraj Sudhir // Can't be greater than 1.0.
30b2812113SSuraj Sudhir assert(shiftedM <= (int64_t(1) << 15) &&
31b2812113SSuraj Sudhir "Shifted mantissa exceeds 16 signed bits");
32b2812113SSuraj Sudhir
33b2812113SSuraj Sudhir if (shiftedM == (int64_t(1) << 15)) {
34b2812113SSuraj Sudhir shiftedM /= 2;
35b2812113SSuraj Sudhir shift++;
36b2812113SSuraj Sudhir }
37b2812113SSuraj Sudhir
38b2812113SSuraj Sudhir // TOSA expects right shift to be positive and embed (1 << 15) into right
39b2812113SSuraj Sudhir // shift bits.
40b2812113SSuraj Sudhir shift = (-shift) + 15;
41b2812113SSuraj Sudhir
42b2812113SSuraj Sudhir assert(shiftedM <= std::numeric_limits<int32_t>::max() &&
43b2812113SSuraj Sudhir "Shifted mantissa exceeds 32-bit signed output type");
44b2812113SSuraj Sudhir
45b2812113SSuraj Sudhir multiplier = static_cast<int32_t>(shiftedM);
460763f122SRob Suderman
47a383a481SPeng Sun // Shifting tops out at 62 bits. Right shift to make 62 bits the max.
48a383a481SPeng Sun // The limit of 62 on shift allows the shift to be decomposed as
49a383a481SPeng Sun // two right shifts of 31.
50a383a481SPeng Sun if (shift > 62) {
51cd2d7369SRob Suderman // Shifting the multiplier by more than 31-bits is unnecessary.
52a383a481SPeng Sun multiplier = multiplier >> std::min<int32_t>(31, shift - 62);
53a383a481SPeng Sun shift = 62;
540763f122SRob Suderman }
55b2812113SSuraj Sudhir }
56b2812113SSuraj Sudhir
57b2812113SSuraj Sudhir /// From a scale value, generates multiplier and shift values where
58b2812113SSuraj Sudhir /// mantissa is in [-1.0,-0.5] or [0.5, 1.0] such that
59b2812113SSuraj Sudhir /// multiplier = mantissa*2^shift for 32-bit scaling.
computeMultiplierAndShiftTosaScale32(double scale,int32_t & multiplier,int32_t & shift)60ac3587f2SStella Laurenzo static void computeMultiplierAndShiftTosaScale32(double scale,
61ac3587f2SStella Laurenzo int32_t &multiplier,
62b2812113SSuraj Sudhir int32_t &shift) {
63b2812113SSuraj Sudhir
64b2812113SSuraj Sudhir const double mantissa = std::frexp(scale, &shift);
65b2812113SSuraj Sudhir auto shiftedM = std::round(mantissa * (int64_t(1) << 31));
66b2812113SSuraj Sudhir
67b2812113SSuraj Sudhir // Can't be greater than 1.0.
68b2812113SSuraj Sudhir assert(shiftedM <= (int64_t(1) << 31) &&
69b2812113SSuraj Sudhir "Shifted mantissa exceeds 32 signed bits");
70b2812113SSuraj Sudhir if (shiftedM == (int64_t(1) << 31)) {
71b2812113SSuraj Sudhir shiftedM /= 2;
72b2812113SSuraj Sudhir shift++;
73b2812113SSuraj Sudhir }
74b2812113SSuraj Sudhir
75b2812113SSuraj Sudhir // TOSA expects right shift to be positive, and embed (1 << 31) into right
76b2812113SSuraj Sudhir // shift bits.
77b2812113SSuraj Sudhir shift = (-shift) + 31;
78b2812113SSuraj Sudhir
79b2812113SSuraj Sudhir assert(shiftedM <= std::numeric_limits<int32_t>::max() &&
80b2812113SSuraj Sudhir "Shifted mantissa exceeds 32-bit signed output type");
81b2812113SSuraj Sudhir
82b2812113SSuraj Sudhir multiplier = static_cast<int32_t>(shiftedM);
830763f122SRob Suderman
84a383a481SPeng Sun // Shifting tops out at 62 bits. Right shift to make 62 bits the max.
85a383a481SPeng Sun // The limit of 62 on shift allows the shift to be decomposed as
86a383a481SPeng Sun // two right shifts of 31.
87a383a481SPeng Sun if (shift > 62) {
880763f122SRob Suderman // Shifting the multiplier by more than 32-bits is unnecessary.
89a383a481SPeng Sun multiplier = multiplier >> std::min<int32_t>(31, shift - 62);
90a383a481SPeng Sun shift = 62;
910763f122SRob Suderman }
92b2812113SSuraj Sudhir }
93b2812113SSuraj Sudhir
94b2812113SSuraj Sudhir /// Generates a quantized multiplier/shift from double.
computeMultiplierAndShift(double scale,int32_t & multiplier,int32_t & shift,int32_t scaleWidth)95ac3587f2SStella Laurenzo void mlir::tosa::computeMultiplierAndShift(double scale, int32_t &multiplier,
96b2812113SSuraj Sudhir int32_t &shift, int32_t scaleWidth) {
97b2812113SSuraj Sudhir
98b2812113SSuraj Sudhir switch (scaleWidth) {
99b2812113SSuraj Sudhir case 16:
100b2812113SSuraj Sudhir computeMultiplierAndShiftTosaScale16(scale, multiplier, shift);
101b2812113SSuraj Sudhir return;
102b2812113SSuraj Sudhir case 32:
103b2812113SSuraj Sudhir computeMultiplierAndShiftTosaScale32(scale, multiplier, shift);
104b2812113SSuraj Sudhir return;
105b2812113SSuraj Sudhir default:
106b2812113SSuraj Sudhir assert(0 && "Unsupported Tosa quantized_scale regime specified!");
107b2812113SSuraj Sudhir }
108b2812113SSuraj Sudhir }
109b2812113SSuraj Sudhir
110*3745e708STai Ly #define GET_UQTYPE(inputType) \
111*3745e708STai Ly (llvm::dyn_cast<quant::UniformQuantizedType>((inputType).getElementType()))
112*3745e708STai Ly #define GET_QTYPE(inputType) \
113*3745e708STai Ly (llvm::dyn_cast<quant::QuantizedType>((inputType).getElementType()))
114b2812113SSuraj Sudhir
115b2812113SSuraj Sudhir /// Method to build ConvOpQuantizationAttr, called from
116b2812113SSuraj Sudhir /// ConvOpQuantInfoBuilder/TransConvOpQuantInfoBuilder:
117b2812113SSuraj Sudhir /// input_zp: input zeropoint
118b2812113SSuraj Sudhir /// weight_zp: weight zeropoint.
119ac3587f2SStella Laurenzo ConvOpQuantizationAttr
buildConvOpQuantizationAttr(OpBuilder & builder,Value input,Value weight)120ac3587f2SStella Laurenzo mlir::tosa::buildConvOpQuantizationAttr(OpBuilder &builder, Value input,
121ac3587f2SStella Laurenzo Value weight) {
122b2812113SSuraj Sudhir
1235550c821STres Popp auto inputType = dyn_cast<ShapedType>(input.getType());
1245550c821STres Popp auto weightType = dyn_cast<ShapedType>(weight.getType());
125b2812113SSuraj Sudhir
126b2812113SSuraj Sudhir if (!inputType || !weightType)
127b2812113SSuraj Sudhir return nullptr;
128b2812113SSuraj Sudhir
129b2812113SSuraj Sudhir auto inputQType = GET_UQTYPE(inputType);
130b2812113SSuraj Sudhir auto weightPerTensorQType = GET_UQTYPE(weightType);
1315550c821STres Popp auto weightPerAxisQType =
1325550c821STres Popp dyn_cast<quant::UniformQuantizedPerAxisType>(weightType.getElementType());
133b2812113SSuraj Sudhir
134b2812113SSuraj Sudhir // Weights must be either per-tensor quantized or per-axis quantized.
135b2812113SSuraj Sudhir assert(!((bool)weightPerTensorQType && (bool)weightPerAxisQType) &&
136b2812113SSuraj Sudhir "Weights must be either per-tensor or per-axis quantized");
137b2812113SSuraj Sudhir
138b2812113SSuraj Sudhir // Either all quantized or all not quantized.
139b2812113SSuraj Sudhir assert(!((bool)inputQType ^
140b2812113SSuraj Sudhir ((bool)weightPerTensorQType || (bool)weightPerAxisQType)) &&
141b2812113SSuraj Sudhir "Inputs and weights must be all quantized or all not quantized");
142b2812113SSuraj Sudhir
143b2812113SSuraj Sudhir if (inputQType) {
144b2812113SSuraj Sudhir int64_t inputZp = inputQType.getZeroPoint();
145b2812113SSuraj Sudhir int64_t weightZp = 0;
146b2812113SSuraj Sudhir
147b2812113SSuraj Sudhir if (weightPerTensorQType) {
148b2812113SSuraj Sudhir weightZp = weightPerTensorQType.getZeroPoint();
149b2812113SSuraj Sudhir } else if (weightPerAxisQType) {
150b2812113SSuraj Sudhir weightZp = weightPerAxisQType.getZeroPoints().front();
151b2812113SSuraj Sudhir }
152b2812113SSuraj Sudhir
153f1182bd6SMogball return builder.getAttr<tosa::ConvOpQuantizationAttr>(inputZp, weightZp);
154b2812113SSuraj Sudhir }
155b2812113SSuraj Sudhir
156b2812113SSuraj Sudhir return nullptr;
157b2812113SSuraj Sudhir }
158b2812113SSuraj Sudhir
159b2812113SSuraj Sudhir /// Builds MatMulOpQuantizationAttr, called from
160b2812113SSuraj Sudhir /// MatMulOpQuantInfoBuilder:
161b2812113SSuraj Sudhir /// aZp: input a zeropoint
162b2812113SSuraj Sudhir /// bZp: input b zeropoint.
163ac3587f2SStella Laurenzo MatMulOpQuantizationAttr
buildMatMulOpQuantizationAttr(OpBuilder & builder,Value a,Value b)164ac3587f2SStella Laurenzo mlir::tosa::buildMatMulOpQuantizationAttr(OpBuilder &builder, Value a,
165ac3587f2SStella Laurenzo Value b) {
166b2812113SSuraj Sudhir
1675550c821STres Popp auto aType = dyn_cast<ShapedType>(a.getType());
1685550c821STres Popp auto bType = dyn_cast<ShapedType>(b.getType());
169b2812113SSuraj Sudhir
170b2812113SSuraj Sudhir if (!aType || !bType)
171b2812113SSuraj Sudhir return nullptr;
172b2812113SSuraj Sudhir
173b2812113SSuraj Sudhir auto aQType = GET_UQTYPE(aType);
174b2812113SSuraj Sudhir auto bQType = GET_UQTYPE(bType);
175b2812113SSuraj Sudhir
176b2812113SSuraj Sudhir // A and B are either all quantized or all not quantized.
177b2812113SSuraj Sudhir assert(!((bool)aQType ^ (bool)bQType) &&
178b2812113SSuraj Sudhir "Matmul operands must be all quantized or all not quantized");
179b2812113SSuraj Sudhir
180b2812113SSuraj Sudhir if (aQType) {
181f1182bd6SMogball return builder.getAttr<tosa::MatMulOpQuantizationAttr>(
182f1182bd6SMogball aQType.getZeroPoint(), bQType.getZeroPoint());
183b2812113SSuraj Sudhir }
184b2812113SSuraj Sudhir
185b2812113SSuraj Sudhir return nullptr;
186b2812113SSuraj Sudhir }
187b2812113SSuraj Sudhir
188b2812113SSuraj Sudhir /// Builds UnaryOpQuantizationAttr
189b2812113SSuraj Sudhir /// UnaryOpQuantInfoBuilder:
190b2812113SSuraj Sudhir /// inputZp: input zeropoint
191b2812113SSuraj Sudhir /// outputZp: output zeropoint.
192ac3587f2SStella Laurenzo UnaryOpQuantizationAttr
buildUnaryOpQuantizationAttr(OpBuilder & builder,Value input,Type outputRawType)193ac3587f2SStella Laurenzo mlir::tosa::buildUnaryOpQuantizationAttr(OpBuilder &builder, Value input,
194b2812113SSuraj Sudhir Type outputRawType) {
195b2812113SSuraj Sudhir
1965550c821STres Popp auto inputType = dyn_cast<ShapedType>(input.getType());
1975550c821STres Popp auto outputType = dyn_cast<ShapedType>(outputRawType);
198b2812113SSuraj Sudhir
199b2812113SSuraj Sudhir if (!inputType || !outputType)
200b2812113SSuraj Sudhir return nullptr;
201b2812113SSuraj Sudhir
202b2812113SSuraj Sudhir auto inputQType = GET_UQTYPE(inputType);
203b2812113SSuraj Sudhir auto outputQType = GET_UQTYPE(outputType);
204b2812113SSuraj Sudhir
205b2812113SSuraj Sudhir // Either all quantized or all not quantized.
206b2812113SSuraj Sudhir assert(!((bool)inputQType ^ (bool)outputQType) &&
207b2812113SSuraj Sudhir "Unary inputs/outputs must be all quantized or all not quantized");
208b2812113SSuraj Sudhir
209b2812113SSuraj Sudhir if (inputQType) {
210f1182bd6SMogball return builder.getAttr<UnaryOpQuantizationAttr>(inputQType.getZeroPoint(),
211f1182bd6SMogball outputQType.getZeroPoint());
212b2812113SSuraj Sudhir }
213b2812113SSuraj Sudhir
214b2812113SSuraj Sudhir return nullptr;
215b2812113SSuraj Sudhir }
216b2812113SSuraj Sudhir
217b2812113SSuraj Sudhir /// Builds PadOpQuantizationAttr, called from PadOpQuantInfoBuilder:
218b2812113SSuraj Sudhir /// inputZp: input zeropoint.
buildPadOpQuantizationAttr(OpBuilder & builder,Value input)219ac3587f2SStella Laurenzo PadOpQuantizationAttr mlir::tosa::buildPadOpQuantizationAttr(OpBuilder &builder,
220b2812113SSuraj Sudhir Value input) {
221b2812113SSuraj Sudhir
2225550c821STres Popp auto inputType = dyn_cast<ShapedType>(input.getType());
223b2812113SSuraj Sudhir
224b2812113SSuraj Sudhir if (!inputType)
225b2812113SSuraj Sudhir return nullptr;
226b2812113SSuraj Sudhir
227b2812113SSuraj Sudhir auto inputQType = GET_UQTYPE(inputType);
228b2812113SSuraj Sudhir
229b2812113SSuraj Sudhir if (inputQType) {
230f1182bd6SMogball return builder.getAttr<tosa::PadOpQuantizationAttr>(
231f1182bd6SMogball inputQType.getZeroPoint());
232b2812113SSuraj Sudhir }
233b2812113SSuraj Sudhir
234b2812113SSuraj Sudhir return nullptr;
235b2812113SSuraj Sudhir }
236b2812113SSuraj Sudhir
237b2812113SSuraj Sudhir /// Builds output type for a quantized ConvOp with the right bitwidth.
238b2812113SSuraj Sudhir /// This is called by the builder when dealing with quantized content.
buildConvOpResultTypeInfo(OpBuilder & builder,Type outputType,Value input,Value weight)239ac3587f2SStella Laurenzo Type mlir::tosa::buildConvOpResultTypeInfo(OpBuilder &builder, Type outputType,
240ac3587f2SStella Laurenzo Value input, Value weight) {
241b2812113SSuraj Sudhir
2425550c821STres Popp auto inputType = dyn_cast<ShapedType>(input.getType());
2435550c821STres Popp auto weightType = dyn_cast<ShapedType>(weight.getType());
244b2812113SSuraj Sudhir
245b2812113SSuraj Sudhir assert(inputType && weightType &&
246b2812113SSuraj Sudhir "Could not extract input or weight tensors from Conv op");
247b2812113SSuraj Sudhir
248b2812113SSuraj Sudhir auto inputQType = GET_QTYPE(inputType);
249b2812113SSuraj Sudhir auto weightQType = GET_QTYPE(weightType);
250b2812113SSuraj Sudhir
251b2812113SSuraj Sudhir assert(inputQType && weightQType &&
252b2812113SSuraj Sudhir "Could not extract input or weight tensor types from Conv op");
253b2812113SSuraj Sudhir
254b2812113SSuraj Sudhir unsigned inputBits = inputQType.getStorageTypeIntegralWidth();
255b2812113SSuraj Sudhir unsigned weightBits = weightQType.getStorageTypeIntegralWidth();
256b2812113SSuraj Sudhir
2575550c821STres Popp auto outputShapedType = dyn_cast<ShapedType>(outputType);
258b2812113SSuraj Sudhir assert(outputShapedType &&
259b2812113SSuraj Sudhir "Could not extract output shape type from Conv op");
260b2812113SSuraj Sudhir
261b2812113SSuraj Sudhir IntegerType accElementType;
262b2812113SSuraj Sudhir if (inputBits == 16 && weightBits == 8)
263b2812113SSuraj Sudhir accElementType = builder.getIntegerType(48);
264b2812113SSuraj Sudhir else
265b2812113SSuraj Sudhir accElementType = builder.getI32Type();
2668662a2f2SRob Suderman auto accType = outputShapedType.clone(accElementType);
267b2812113SSuraj Sudhir return accType;
268b2812113SSuraj Sudhir }
269b2812113SSuraj Sudhir
270b2812113SSuraj Sudhir /// Builds Tosa quantization attributes from min/max values.
buildQTypeFromMinMax(OpBuilder builder,Type inputDType,Attribute minAttr,Attribute maxAttr,IntegerAttr quantBits,int filterQuantDim,bool isSigned,BoolAttr narrowRange)271ac3587f2SStella Laurenzo Type mlir::tosa::buildQTypeFromMinMax(OpBuilder builder, Type inputDType,
272ac3587f2SStella Laurenzo Attribute minAttr, Attribute maxAttr,
273ac3587f2SStella Laurenzo IntegerAttr quantBits, int filterQuantDim,
274ac3587f2SStella Laurenzo bool isSigned, BoolAttr narrowRange) {
275b2812113SSuraj Sudhir
276b2812113SSuraj Sudhir quant::QuantizedType retType;
277b2812113SSuraj Sudhir
278b2812113SSuraj Sudhir auto convfunc =
279b2812113SSuraj Sudhir quant::ExpressedToQuantizedConverter::forInputType(inputDType);
280b2812113SSuraj Sudhir
2815550c821STres Popp auto minElems = dyn_cast<DenseFPElementsAttr>(minAttr);
2825550c821STres Popp auto maxElems = dyn_cast<DenseFPElementsAttr>(maxAttr);
283b2812113SSuraj Sudhir
284b2812113SSuraj Sudhir SmallVector<double, 2> min, max;
285b2812113SSuraj Sudhir
286b2812113SSuraj Sudhir // At least one is per-axis quantized elementsattr.
287b2812113SSuraj Sudhir if (minElems || maxElems) {
288b2812113SSuraj Sudhir // Must have the same number of elements.
289b2812113SSuraj Sudhir if (minElems.getNumElements() != maxElems.getNumElements())
290b2812113SSuraj Sudhir return {};
291b2812113SSuraj Sudhir min.reserve(minElems.getNumElements());
292b2812113SSuraj Sudhir max.reserve(maxElems.getNumElements());
293b2812113SSuraj Sudhir for (auto i : minElems)
294b2812113SSuraj Sudhir min.push_back(FloatAttr::getValueAsDouble(i));
295b2812113SSuraj Sudhir for (auto i : maxElems)
296b2812113SSuraj Sudhir max.push_back(FloatAttr::getValueAsDouble(i));
297b2812113SSuraj Sudhir } else { // Just a single FP value.
2985550c821STres Popp auto minVal = dyn_cast<FloatAttr>(minAttr);
299b2812113SSuraj Sudhir if (minVal)
300b2812113SSuraj Sudhir min.push_back(minVal.getValueAsDouble());
301b2812113SSuraj Sudhir else
302b2812113SSuraj Sudhir return {};
3035550c821STres Popp auto maxVal = dyn_cast<FloatAttr>(maxAttr);
304b2812113SSuraj Sudhir if (maxVal)
305b2812113SSuraj Sudhir max.push_back(maxVal.getValueAsDouble());
306b2812113SSuraj Sudhir else
307b2812113SSuraj Sudhir return {};
308b2812113SSuraj Sudhir }
309b2812113SSuraj Sudhir
310b2812113SSuraj Sudhir if (min.size() == max.size()) {
311b2812113SSuraj Sudhir if (min.size() == 1) { // Per-tensor quantization with one min/max pair.
312b2812113SSuraj Sudhir retType = quant::fakeQuantAttrsToType(
313b2812113SSuraj Sudhir builder.getUnknownLoc(), quantBits.getInt(), min[0], max[0],
314b2812113SSuraj Sudhir narrowRange.getValue(), convfunc.expressedType, isSigned);
315b2812113SSuraj Sudhir } else if (min.size() > 1) { // Per-axis quant on filterQuantDim.
3165550c821STres Popp auto shape = dyn_cast<ShapedType>(inputDType);
317b2812113SSuraj Sudhir if (!shape)
318b2812113SSuraj Sudhir return {};
319b2812113SSuraj Sudhir if ((filterQuantDim) >= 0 && (shape.getRank() > filterQuantDim)) {
320b2812113SSuraj Sudhir retType = quant::fakeQuantAttrsToType(
321b2812113SSuraj Sudhir builder.getUnknownLoc(), quantBits.getInt(), filterQuantDim, min[0],
322b2812113SSuraj Sudhir max[0], narrowRange.getValue(), convfunc.expressedType, isSigned);
323b2812113SSuraj Sudhir }
324b2812113SSuraj Sudhir } else {
325b2812113SSuraj Sudhir return {};
326b2812113SSuraj Sudhir }
327b2812113SSuraj Sudhir } else {
328b2812113SSuraj Sudhir return {};
329b2812113SSuraj Sudhir }
330b2812113SSuraj Sudhir
331b2812113SSuraj Sudhir if (!retType)
332b2812113SSuraj Sudhir return {};
333b2812113SSuraj Sudhir
334b2812113SSuraj Sudhir return convfunc.convert(retType);
335b2812113SSuraj Sudhir }
336b2812113SSuraj Sudhir
337b2812113SSuraj Sudhir /// Builds Tosa quantization attributes from min/max values.
338ac3587f2SStella Laurenzo TypeAttr
buildQTypeAttrFromMinMax(OpBuilder builder,Type inputDtype,Attribute minAttr,Attribute maxAttr,IntegerAttr quantBits,int filterQuantDim,bool isSigned,BoolAttr narrowRange)339ac3587f2SStella Laurenzo mlir::tosa::buildQTypeAttrFromMinMax(OpBuilder builder, Type inputDtype,
340b2812113SSuraj Sudhir Attribute minAttr, Attribute maxAttr,
341b2812113SSuraj Sudhir IntegerAttr quantBits, int filterQuantDim,
342b2812113SSuraj Sudhir bool isSigned, BoolAttr narrowRange) {
343b2812113SSuraj Sudhir
344b2812113SSuraj Sudhir return TypeAttr::get(buildQTypeFromMinMax(builder, inputDtype, minAttr,
345b2812113SSuraj Sudhir maxAttr, quantBits, filterQuantDim,
346b2812113SSuraj Sudhir isSigned, narrowRange));
347b2812113SSuraj Sudhir }
348