Skip to content

Commit 1aa044a

Browse files
ziereisclaude
andauthored
[TorchToLinalg] Support aten.mm with mixed sign extensions (#4722)
This lowering emits a matmul in its generic form when mixed sign extensions are used. Alternative would be to shift the unsigned side to signed, add a zeropoint of -128 and emit a linalg.quantized_matmul. However i think generally we should not do sign adjustments at the level of torch-mlir since its very much target dependent how the signs should be handled. Additionally undoing shifting the sign is a lot more difficult then adding the shifting afterwards if desired. --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 parent cff353c commit 1aa044a

3 files changed

Lines changed: 78 additions & 11 deletions

File tree

lib/Conversion/TorchToLinalg/Linear.cpp

Lines changed: 49 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -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);

projects/pt1/e2e_testing/xfail_sets.py

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,6 @@
3434
"ElementwiseClampInt16Module_basic",
3535
# TODO: The values are extremely close to the golden values, but the test fails because of strict rtol/atol.
3636
"AtenInstanceNormModuleFp16_basic",
37-
# mixed uint8 x int8 aten.mm has no lowering yet
38-
"AtenIntMMMixedSigni8_basic",
3937
# unimplemented lowering torch -> linalg for torchvision.deform_conv2d
4038
# this is added to check the torch.onnx.export -> import_onnx -> torch path
4139
"DeformConv2D_basic",
@@ -417,8 +415,6 @@
417415
"AtenFloatScalarModule_basic",
418416
# TODO: The values are extremely close to the golden values, but the test fails because of strict rtol/atol.
419417
"AtenInstanceNormModuleFp16_basic",
420-
# mixed uint8 x int8 aten.mm has no lowering yet
421-
"AtenIntMMMixedSigni8_basic",
422418
"AtenIntBoolOpConstFalseModule_basic",
423419
"AtenIntBoolOpConstTrueModule_basic",
424420
"AtenIntBoolOpModule_basic",

test/Conversion/TorchToLinalg/basic.mlir

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,35 @@ func.func @torch.aten.mm$basic_unsigned(%arg0: !torch.vtensor<[?,?],ui32>, %arg1
201201

202202
// -----
203203

204+
// CHECK-LABEL: func.func @torch.aten.mm$mixed_signedness_lhs_unsigned(
205+
// CHECK: linalg.generic
206+
// CHECK-SAME: iterator_types = ["parallel", "parallel", "reduction"]
207+
// CHECK-SAME: ins(%{{.*}}, %{{.*}} : tensor<3x4xi8>, tensor<4x3xi8>)
208+
// CHECK: ^bb0(%[[LHS:.*]]: i8, %[[RHS:.*]]: i8, %[[ACC:.*]]: i32):
209+
// CHECK-DAG: %[[LHS_EXT:.*]] = arith.extui %[[LHS]] : i8 to i32
210+
// CHECK-DAG: %[[RHS_EXT:.*]] = arith.extsi %[[RHS]] : i8 to i32
211+
// CHECK: %[[MUL:.*]] = arith.muli %[[LHS_EXT]], %[[RHS_EXT]] : i32
212+
// CHECK: %[[SUM:.*]] = arith.addi %[[ACC]], %[[MUL]] : i32
213+
// CHECK: linalg.yield %[[SUM]] : i32
214+
func.func @torch.aten.mm$mixed_signedness_lhs_unsigned(%arg0: !torch.vtensor<[3,4],ui8>, %arg1: !torch.vtensor<[4,3],si8>) -> !torch.vtensor<[3,3],si32> {
215+
%0 = torch.aten.mm %arg0, %arg1 : !torch.vtensor<[3,4],ui8>, !torch.vtensor<[4,3],si8> -> !torch.vtensor<[3,3],si32>
216+
return %0 : !torch.vtensor<[3,3],si32>
217+
}
218+
219+
// -----
220+
221+
// CHECK-LABEL: func.func @torch.aten.mm$mixed_signedness_rhs_unsigned(
222+
// CHECK: ^bb0(%[[LHS:.*]]: i8, %[[RHS:.*]]: i8, %[[ACC:.*]]: i32):
223+
// CHECK-DAG: %[[LHS_EXT:.*]] = arith.extsi %[[LHS]] : i8 to i32
224+
// CHECK-DAG: %[[RHS_EXT:.*]] = arith.extui %[[RHS]] : i8 to i32
225+
// CHECK: arith.muli %[[LHS_EXT]], %[[RHS_EXT]] : i32
226+
func.func @torch.aten.mm$mixed_signedness_rhs_unsigned(%arg0: !torch.vtensor<[3,4],si8>, %arg1: !torch.vtensor<[4,3],ui8>) -> !torch.vtensor<[3,3],si32> {
227+
%0 = torch.aten.mm %arg0, %arg1 : !torch.vtensor<[3,4],si8>, !torch.vtensor<[4,3],ui8> -> !torch.vtensor<[3,3],si32>
228+
return %0 : !torch.vtensor<[3,3],si32>
229+
}
230+
231+
// -----
232+
204233
// If the operands are missing dtype, we cannot lower it.
205234
func.func @torch.aten.mm$no_convert$missing_dtype(%arg0: !torch.vtensor, %arg1: !torch.vtensor) -> !torch.vtensor {
206235
// expected-error@+1 {{failed to legalize}}

0 commit comments

Comments
 (0)