@@ -116,18 +116,32 @@ class ConvertAtenMmOp : public OpConversionPattern<AtenMmOp> {
116116 op, " unsupported: aten.mm with mixed quantization" );
117117 }
118118
119+ bool isUnsignedLhs = torch_to_linalg::isUnsignedTorchType (lhsTorchType);
120+ bool isUnsignedRhs = torch_to_linalg::isUnsignedTorchType (rhsTorchType);
121+
122+ auto lhsIntType = dyn_cast<mlir::IntegerType>(lhsType.getElementType ());
123+ auto rhsIntType = dyn_cast<mlir::IntegerType>(rhsType.getElementType ());
124+
125+ bool bothInt = lhsIntType && rhsIntType;
126+
127+ // Mixing integer widths would require extending the narrower operand to the
128+ // wider one before the contraction, which none of the paths below do.
129+ if (bothInt && lhsIntType.getWidth () != rhsIntType.getWidth ()) {
130+ return rewriter.notifyMatchFailure (
131+ op, " unsupported: aten.mm with mixed integer widths" );
132+ }
133+
134+ bool isMixedSignedness = bothInt && isUnsignedLhs != isUnsignedRhs;
135+
119136 if (lhsTorchType.getDtype () != rhsTorchType.getDtype ()) {
120- if (!lhsZeroPoint) {
137+ if (!lhsZeroPoint && !isMixedSignedness ) {
121138 return rewriter.notifyMatchFailure (
122139 op, " unsupported: aten.mm with different input element types" );
123140 }
124141 // Allows quantized types to mismatch since they will be cast to the same
125142 // type.
126143 }
127144
128- bool isUnsigned = torch_to_linalg::isUnsignedTorchType (lhsTorchType);
129- bool isUnsignedR = torch_to_linalg::isUnsignedTorchType (rhsTorchType);
130-
131145 Value lhsDim0 = tensor::DimOp::create (rewriter, loc, lhs, 0 );
132146 Value rhsDim1 = tensor::DimOp::create (rewriter, loc, rhs, 1 );
133147
@@ -171,15 +185,43 @@ class ConvertAtenMmOp : public OpConversionPattern<AtenMmOp> {
171185 // change uint8 quantization -> int8 quantization
172186 int64_t numBits =
173187 cast<mlir::IntegerType>(lhsType.getElementType ()).getWidth ();
174- signShift (rewriter, loc, lhs, lhsZeroPoint, isUnsigned , numBits);
188+ signShift (rewriter, loc, lhs, lhsZeroPoint, isUnsignedLhs , numBits);
175189 numBits = cast<mlir::IntegerType>(rhsType.getElementType ()).getWidth ();
176- signShift (rewriter, loc, rhs, rhsZeroPoint, isUnsignedR , numBits);
190+ signShift (rewriter, loc, rhs, rhsZeroPoint, isUnsignedRhs , numBits);
177191
178192 matmul = linalg::QuantizedMatmulOp::create (
179193 rewriter, loc, zeroFill.getType (),
180194 ValueRange{lhs, rhs, lhsZeroPoint, rhsZeroPoint}, zeroFill)
181195 .getResult (0 );
182- } else if (isUnsigned) {
196+ } else if (isMixedSignedness) {
197+ // `linalg.matmul` extends both operands with the same type function, so
198+ // the contraction is written as a generic that extends each operand
199+ // according to its own signedness instead.
200+ MLIRContext *context = op.getContext ();
201+ AffineExpr m, n, k;
202+ bindDims (context, m, n, k);
203+ SmallVector<AffineMap> indexingMaps = {
204+ AffineMap::get (3 , 0 , {m, k}, context),
205+ AffineMap::get (3 , 0 , {k, n}, context),
206+ AffineMap::get (3 , 0 , {m, n}, context)};
207+ SmallVector<utils::IteratorType> iteratorTypes = {
208+ utils::IteratorType::parallel, utils::IteratorType::parallel,
209+ utils::IteratorType::reduction};
210+ matmul =
211+ linalg::GenericOp::create (
212+ rewriter, loc, zeroFill.getType (), ValueRange{lhs, rhs}, zeroFill,
213+ indexingMaps, iteratorTypes,
214+ [&](OpBuilder &b, Location loc, ValueRange args) {
215+ Value lhsElem = convertScalarToDtype (
216+ b, loc, args[0 ], elementType, lhsTorchType.getDtype ());
217+ Value rhsElem = convertScalarToDtype (
218+ b, loc, args[1 ], elementType, rhsTorchType.getDtype ());
219+ Value product = arith::MulIOp::create (b, loc, lhsElem, rhsElem);
220+ Value sum = arith::AddIOp::create (b, loc, args[2 ], product);
221+ linalg::YieldOp::create (b, loc, sum);
222+ })
223+ .getResult (0 );
224+ } else if (isUnsignedLhs && isUnsignedRhs) {
183225 auto matmulOp = linalg::MatmulOp::create (
184226 rewriter, loc, zeroFill.getType (), ValueRange{lhs, rhs}, zeroFill);
185227 matmulOp.setCast (linalg::TypeFn::cast_unsigned);
0 commit comments