Skip to content

Commit 6c4f5a5

Browse files
authored
[NFC] Add elementwise trait on MIGraphX dialect ops (#2339)
* Add Elementwise trait to MIGraphX dialect elementwise ops Mark all MIGraphX elementwise operations (unary, binary, clip, where, convert) with the MLIR Elementwise trait. This enables programmatic querying of elementwise ops via op.hasTrait<OpTrait::Elementwise>() instead of exhaustive isa<> lists. Also add AllShapesMatch<["inA", "output"]> to unary ops and convert to enforce that elementwise unary operations preserve shape.
1 parent 321803c commit 6c4f5a5

5 files changed

Lines changed: 342 additions & 87 deletions

File tree

mlir/include/mlir/Conversion/LinalgToRock/LinalgToRock.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ void populateLinalgToRockConversionPattern(RewritePatternSet &pattern,
2828

2929
/// A tensor.insert_slice is said to be a rock.expand_strides
3030
bool isRockExpandStride(tensor::InsertSliceOp op);
31-
}
31+
} // namespace rock
3232
} // namespace mlir
3333

3434
#endif

mlir/include/mlir/Dialect/MIGraphX/IR/MIGraphX.td

Lines changed: 22 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,8 @@ def MIGraphX_LiteralOp : MIGraphX_Op<"literal",
5858
class MIGraphX_ElementwiseBinaryOp<string name, list<Trait> traits = []>
5959
: MIGraphX_Op<
6060
name, !listconcat(
61-
traits, [AllElementTypesMatch<["inA", "inB", "output"]>])>,
61+
traits, [Elementwise,
62+
AllElementTypesMatch<["inA", "inB", "output"]>])>,
6263
Arguments<(ins AnyMIXRShaped:$inA, AnyMIXRShaped:$inB)>,
6364
Results<(outs AnyMIXRShaped:$output)> {
6465
let summary = "Elementwise " # name # " of two shaped values with broadcast";
@@ -94,12 +95,14 @@ def MIGraphX_Equal : MIGraphX_ElementwiseBinaryOp<"equal"> {
9495
}];
9596
}
9697

97-
def MIGraphX_ClipOp :
98-
MIGraphX_Op<"clip">,
99-
Arguments<(ins AnyMIXRShaped:$x,
100-
AnyMIXRShaped:$minVals,
101-
AnyMIXRShaped:$maxVals)>,
102-
Results<(outs AnyMIXRShaped:$output)> {
98+
def MIGraphX_ClipOp
99+
: MIGraphX_Op<
100+
"clip", [Elementwise,
101+
AllElementTypesMatch<["x", "minVals", "maxVals", "output"]>,
102+
AllShapesMatch<["x", "minVals", "maxVals", "output"]>]>,
103+
Arguments<(ins AnyMIXRShaped:$x, AnyMIXRShaped:$minVals,
104+
AnyMIXRShaped:$maxVals)>,
105+
Results<(outs AnyMIXRShaped:$output)> {
103106
let summary = "Elementwise clip";
104107
let description = [{
105108
Elementwise clip: output = min(max(x, minVals), maxVals)
@@ -113,7 +116,8 @@ def MIGraphX_ClipOp :
113116
// Note: when lowering to kernel calls, MIGraphX represents booleans as i8.
114117
// Keep that logic here.
115118
def MIGraphX_WhereOp
116-
: MIGraphX_Op<"where", [AllElementTypesMatch<["inA", "inB", "output"]>,
119+
: MIGraphX_Op<"where", [Elementwise,
120+
AllElementTypesMatch<["inA", "inB", "output"]>,
117121
AllShapesMatch<["inA", "inB", "output", "cond"]>]>,
118122
Arguments<(ins MIXRShapedOf<[I8, SI8, UI8]>:$cond, AnyMIXRShaped:$inA,
119123
AnyMIXRShaped:$inB)>,
@@ -130,10 +134,10 @@ def MIGraphX_WhereOp
130134

131135
// Elementwise unary operations
132136

133-
def MIGraphX_ConvertOp :
134-
MIGraphX_Op<"convert">,
135-
Arguments<(ins AnyMIXRShaped:$inA)>,
136-
Results<(outs AnyMIXRShaped:$output)> {
137+
def MIGraphX_ConvertOp
138+
: MIGraphX_Op<"convert", [Elementwise, AllShapesMatch<["inA", "output"]>]>,
139+
Arguments<(ins AnyMIXRShaped:$inA)>,
140+
Results<(outs AnyMIXRShaped:$output)> {
137141
let summary = "Elementwise type conversion";
138142
let description = [{
139143
Type conversion. Due to impedance mismatches between MIGraphX and Tosa,
@@ -142,10 +146,12 @@ def MIGraphX_ConvertOp :
142146
let assemblyFormat = "$inA attr-dict `:` type($inA) `to` type($output)";
143147
}
144148

145-
class MIGraphX_ElementwiseUnaryOp<string name, list<Trait> traits=[]> :
146-
MIGraphX_Op<name, traits>,
147-
Arguments<(ins AnyMIXRShaped:$inA)>,
148-
Results<(outs AnyMIXRShaped:$output)> {
149+
class MIGraphX_ElementwiseUnaryOp<string name, list<Trait> traits = []>
150+
: MIGraphX_Op<name,
151+
!listconcat(traits, [Elementwise,
152+
AllShapesMatch<["inA", "output"]>])>,
153+
Arguments<(ins AnyMIXRShaped:$inA)>,
154+
Results<(outs AnyMIXRShaped:$output)> {
149155
let summary = "Elementwise " # name;
150156
let assemblyFormat = [{
151157
$inA attr-dict `:` type($inA) `->` type($output)

mlir/lib/Dialect/MIGraphX/IR/MIGraphX.cpp

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -334,25 +334,24 @@ LogicalResult ReshapeOp::verify() {
334334
<< outType.getRank() << ")";
335335

336336
// Check that there is only a single -1 value
337-
int missingDims = llvm::count_if(
338-
dimsAttr.getAsRange<IntegerAttr>(),
339-
[](IntegerAttr a) { return a.getInt() == -1; });
337+
int missingDims =
338+
llvm::count_if(dimsAttr.getAsRange<IntegerAttr>(),
339+
[](IntegerAttr a) { return a.getInt() == -1; });
340340
if (missingDims > 1)
341341
return emitOpError("expected at most one target dimension to be -1");
342342

343343
// Check how many zero dimensions there are
344-
int numZeros = llvm::count_if(
345-
dimsAttr.getAsRange<IntegerAttr>(),
346-
[](IntegerAttr a) { return a.getInt() == 0; });
344+
int numZeros = llvm::count_if(dimsAttr.getAsRange<IntegerAttr>(),
345+
[](IntegerAttr a) { return a.getInt() == 0; });
347346

348347
if (missingDims > 0 && numZeros > 0)
349348
return emitOpError("Cannot mix missing dimensions with zero dimension");
350349

351350
// Compare dimension values to output shape
352351
for (auto [dimVal, outDim] : llvm::zip(dimsAttr, outType.getShape())) {
353352
int64_t dimValue = cast<IntegerAttr>(dimVal).getInt();
354-
// We cannot handle negative dims values that aren't -1
355-
if (dimValue < -1 ) {
353+
// We cannot handle negative dims values that aren't -1
354+
if (dimValue < -1) {
356355
return emitOpError("Non -1 negative values are not supported");
357356
}
358357

mlir/test/Dialect/MIGraphX/invalid.mlir

Lines changed: 123 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,23 @@
11
// RUN: rocmlir-opt %s -split-input-file -verify-diagnostics
22

3+
// ---- migraphx.shaped type ----
4+
5+
// expected-error @+1 {{migraphx.shaped type has 1 elements in its shape but 2 strides defined}}
6+
func.func @invalid_more_strides_than_shapes(%arg: !migraphx.shaped<1xf32, 1x1>) {
7+
func.return
8+
}
9+
10+
// -----
11+
12+
// expected-error @+1 {{migraphx.shaped type has 2 elements in its shape but 1 strides defined}}
13+
func.func @invalid_more_shapes_than_strides(%arg: !migraphx.shaped<1x1xf32, 1>) {
14+
func.return
15+
}
16+
17+
// -----
18+
19+
// ---- migraphx.reshape ----
20+
321
func.func @mlir_reshape_inconsistent_dims(%arg0: !migraphx.shaped<4096x4096xf16, 0x1>) {
422
// expected-error@+1 {{'migraphx.reshape' op dimValue: 64 inconsistent with result dimension 4096}}
523
%0 = migraphx.reshape %arg0 {dims = [64, 128]} : <4096x4096xf16, 0x1> -> <4096x4096xf16, 16536x2>
@@ -42,6 +60,10 @@ func.func @mlir_neg_one_with_zero(%arg0: !migraphx.shaped<2x4xf16, 0x1>) {
4260
return
4361
}
4462

63+
// -----
64+
65+
// ---- migraphx.equal ----
66+
4567
func.func @func_equal(%arg0: !migraphx.shaped<1x36x384x64xi32, 884736x24576x64x1>) -> !migraphx.shaped<1x36x384x64xi32, 884736x24576x64x1> attributes{rock.kernel, rock.arch = ""} {
4668
%cst = migraphx.literal (dense<1> : tensor<1x36x384x64xi32>) : <1x36x384x64xi32, 884736x24576x64x1>
4769
%0 = migraphx.add %arg0, %cst : <1x36x384x64xi32, 884736x24576x64x1>, <1x36x384x64xi32, 884736x24576x64x1> -> <1x36x384x64xi32, 884736x24576x64x1>
@@ -50,6 +72,104 @@ func.func @func_equal(%arg0: !migraphx.shaped<1x36x384x64xi32, 884736x24576x64x1
5072
return %1 : !migraphx.shaped<1x36x384x64xi16, 884736x24576x64x1>
5173
}
5274

75+
// -----
76+
77+
// ---- migraphx.clip ----
78+
79+
func.func @clip_mismatched_element_types(%arg0: !migraphx.shaped<4x8xf32, 8x1>, %arg1: !migraphx.shaped<4x8xf16, 8x1>, %arg2: !migraphx.shaped<4x8xf32, 8x1>) -> !migraphx.shaped<4x8xf32, 8x1> {
80+
// expected-error @+1 {{op failed to verify that all of {x, minVals, maxVals, output} have same element type}}
81+
%0 = migraphx.clip %arg0, %arg1, %arg2 : <4x8xf32, 8x1>, <4x8xf16, 8x1>, <4x8xf32, 8x1> -> <4x8xf32, 8x1>
82+
return %0 : !migraphx.shaped<4x8xf32, 8x1>
83+
}
84+
85+
// -----
86+
87+
func.func @clip_mismatched_shapes(%arg0: !migraphx.shaped<4x8xf32, 8x1>, %arg1: !migraphx.shaped<4x4xf32, 4x1>, %arg2: !migraphx.shaped<4x8xf32, 8x1>) -> !migraphx.shaped<4x8xf32, 8x1> {
88+
// expected-error @+1 {{op failed to verify that all of {x, minVals, maxVals, output} have same shape}}
89+
%0 = migraphx.clip %arg0, %arg1, %arg2 : <4x8xf32, 8x1>, <4x4xf32, 4x1>, <4x8xf32, 8x1> -> <4x8xf32, 8x1>
90+
return %0 : !migraphx.shaped<4x8xf32, 8x1>
91+
}
92+
93+
// -----
94+
95+
// ---- migraphx.where ----
96+
97+
func.func @where_cond_not_bool(%arg0: !migraphx.shaped<4x4xf32, 4x1>, %arg1: !migraphx.shaped<4x4xf32, 4x1>, %arg2: !migraphx.shaped<4x4xf32, 4x1>) -> !migraphx.shaped<4x4xf32, 4x1> {
98+
// expected-error @+1 {{'migraphx.where' op operand #0 must be !migraphx.shaped of 8-bit signless integer or 8-bit signed integer or 8-bit unsigned integer values, but got '!migraphx.shaped<4x4xf32, 4x1>'}}
99+
%0 = migraphx.where %arg0, %arg1, %arg2 : <4x4xf32, 4x1>, <4x4xf32, 4x1>, <4x4xf32, 4x1> -> <4x4xf32, 4x1>
100+
return %0 : !migraphx.shaped<4x4xf32, 4x1>
101+
}
102+
103+
// -----
104+
105+
func.func @where_mismatched_types(%arg0: !migraphx.shaped<4x4xi8, 4x1>, %arg1: !migraphx.shaped<4x4xf32, 4x1>, %arg2: !migraphx.shaped<4x4xf16, 4x1>) -> !migraphx.shaped<4x4xf32, 4x1> {
106+
// expected-error @+1 {{op failed to verify that all of {inA, inB, output} have same element type}}
107+
%0 = migraphx.where %arg0, %arg1, %arg2 : <4x4xi8, 4x1>, <4x4xf32, 4x1>, <4x4xf16, 4x1> -> <4x4xf32, 4x1>
108+
return %0 : !migraphx.shaped<4x4xf32, 4x1>
109+
}
110+
111+
// -----
112+
113+
func.func @where_mismatched_shapes(%arg0: !migraphx.shaped<4x4xi8, 4x1>, %arg1: !migraphx.shaped<4x8xf32, 8x1>, %arg2: !migraphx.shaped<4x8xf32, 8x1>) -> !migraphx.shaped<4x8xf32, 8x1> {
114+
// expected-error @+1 {{op failed to verify that all of {inA, inB, output, cond} have same shape}}
115+
%0 = migraphx.where %arg0, %arg1, %arg2 : <4x4xi8, 4x1>, <4x8xf32, 8x1>, <4x8xf32, 8x1> -> <4x8xf32, 8x1>
116+
return %0 : !migraphx.shaped<4x8xf32, 8x1>
117+
}
118+
119+
// -----
120+
121+
// ---- migraphx.convert ----
122+
123+
func.func @convert_shape_mismatch(%arg0: !migraphx.shaped<4x8xf32, 8x1>) -> !migraphx.shaped<4x4xf16, 4x1> {
124+
// expected-error @+1 {{op failed to verify that all of {inA, output} have same shape}}
125+
%0 = migraphx.convert %arg0 : <4x8xf32, 8x1> to <4x4xf16, 4x1>
126+
return %0 : !migraphx.shaped<4x4xf16, 4x1>
127+
}
128+
129+
// -----
130+
131+
// ---- migraphx.abs ----
132+
133+
func.func @abs_shape_mismatch(%arg0: !migraphx.shaped<4x8xf32, 8x1>) -> !migraphx.shaped<4x4xf32, 4x1> {
134+
// expected-error @+1 {{op failed to verify that all of {inA, output} have same shape}}
135+
%0 = migraphx.abs %arg0 : <4x8xf32, 8x1> -> <4x4xf32, 4x1>
136+
return %0 : !migraphx.shaped<4x4xf32, 4x1>
137+
}
138+
139+
// -----
140+
141+
// ---- migraphx.exp ----
142+
143+
func.func @exp_rank_mismatch(%arg0: !migraphx.shaped<4x8xf32, 8x1>) -> !migraphx.shaped<32xf32, 1> {
144+
// expected-error @+1 {{op failed to verify that all of {inA, output} have same shape}}
145+
%0 = migraphx.exp %arg0 : <4x8xf32, 8x1> -> <32xf32, 1>
146+
return %0 : !migraphx.shaped<32xf32, 1>
147+
}
148+
149+
// -----
150+
151+
// ---- migraphx.relu ----
152+
153+
func.func @relu_shape_mismatch(%arg0: !migraphx.shaped<4x8xf32, 8x1>) -> !migraphx.shaped<2x8xf32, 8x1> {
154+
// expected-error @+1 {{op failed to verify that all of {inA, output} have same shape}}
155+
%0 = migraphx.relu %arg0 : <4x8xf32, 8x1> -> <2x8xf32, 8x1>
156+
return %0 : !migraphx.shaped<2x8xf32, 8x1>
157+
}
158+
159+
// -----
160+
161+
// ---- migraphx.sigmoid ----
162+
163+
func.func @func_sigmoid_2d_i32(%arg0: !migraphx.shaped<4x8xi32, 8x1>) -> !migraphx.shaped<4x8xi32, 8x1> {
164+
// expected-error @+1 {{only support floating point}}
165+
%0 = migraphx.sigmoid %arg0 : <4x8xi32, 8x1> -> <4x8xi32, 8x1>
166+
return %0 : !migraphx.shaped<4x8xi32, 8x1>
167+
}
168+
169+
// -----
170+
171+
// ---- migraphx.quant_dot ----
172+
53173
// Test: Only scaleA provided (should fail - both scales required)
54174
func.func @quant_dot_only_scale_a(
55175
%arg0: !migraphx.shaped<1x16x512xf4E2M1FN, 8192x512x1>,
@@ -228,6 +348,8 @@ func.func @migraphx_quant_dot_f4_n_scales(%arg0: !migraphx.shaped<1x16x512xf4E2M
228348

229349
// -----
230350

351+
// ---- migraphx.dot ----
352+
231353
// CHECK-LABEL: func.func @dot_rank_less_than_2
232354
func.func @dot_rank_less_than_2(%arg0: !migraphx.shaped<320xf16, 1>, %arg1: !migraphx.shaped<320x64xf16, 64x1>) -> !migraphx.shaped<64xf16, 1> {
233355
// expected-error @+1 {{expect operand to have rank greater or equal to 2}}
@@ -273,51 +395,7 @@ func.func @dot_result_shape_mismatch(%arg0: !migraphx.shaped<2x3x4xf16, 12x4x1>,
273395

274396
// -----
275397

276-
// expected-error @+1 {{migraphx.shaped type has 1 elements in its shape but 2 strides defined}}
277-
func.func @invalid_more_strides_than_shapes(%arg: !migraphx.shaped<1xf32, 1x1>) {
278-
func.return
279-
}
280-
281-
// -----
282-
283-
// expected-error @+1 {{migraphx.shaped type has 2 elements in its shape but 1 strides defined}}
284-
func.func @invalid_more_shapes_than_strides(%arg: !migraphx.shaped<1x1xf32, 1>) {
285-
func.return
286-
}
287-
288-
// -----
289-
290-
func.func @func_sigmoid_2d_i32(%arg0: !migraphx.shaped<4x8xi32, 8x1>) -> !migraphx.shaped<4x8xi32, 8x1> {
291-
// expected-error @+1 {{only support floating point}}
292-
%0 = migraphx.sigmoid %arg0 : <4x8xi32, 8x1> -> <4x8xi32, 8x1>
293-
return %0 : !migraphx.shaped<4x8xi32, 8x1>
294-
}
295-
296-
// -----
297-
298-
func.func @where_cond_not_bool(%arg0: !migraphx.shaped<4x4xf32, 4x1>, %arg1: !migraphx.shaped<4x4xf32, 4x1>, %arg2: !migraphx.shaped<4x4xf32, 4x1>) -> !migraphx.shaped<4x4xf32, 4x1> {
299-
// expected-error @+1 {{'migraphx.where' op operand #0 must be !migraphx.shaped of 8-bit signless integer or 8-bit signed integer or 8-bit unsigned integer values, but got '!migraphx.shaped<4x4xf32, 4x1>'}}
300-
%0 = migraphx.where %arg0, %arg1, %arg2 : <4x4xf32, 4x1>, <4x4xf32, 4x1>, <4x4xf32, 4x1> -> <4x4xf32, 4x1>
301-
return %0 : !migraphx.shaped<4x4xf32, 4x1>
302-
}
303-
304-
// -----
305-
306-
func.func @where_mismatched_types(%arg0: !migraphx.shaped<4x4xi8, 4x1>, %arg1: !migraphx.shaped<4x4xf32, 4x1>, %arg2: !migraphx.shaped<4x4xf16, 4x1>) -> !migraphx.shaped<4x4xf32, 4x1> {
307-
// expected-error @+1 {{op failed to verify that all of {inA, inB, output} have same element type}}
308-
%0 = migraphx.where %arg0, %arg1, %arg2 : <4x4xi8, 4x1>, <4x4xf32, 4x1>, <4x4xf16, 4x1> -> <4x4xf32, 4x1>
309-
return %0 : !migraphx.shaped<4x4xf32, 4x1>
310-
}
311-
312-
// -----
313-
314-
func.func @where_mismatched_shapes(%arg0: !migraphx.shaped<4x4xi8, 4x1>, %arg1: !migraphx.shaped<4x8xf32, 8x1>, %arg2: !migraphx.shaped<4x8xf32, 8x1>) -> !migraphx.shaped<4x8xf32, 8x1> {
315-
// expected-error @+1 {{op failed to verify that all of {inA, inB, output, cond} have same shape}}
316-
%0 = migraphx.where %arg0, %arg1, %arg2 : <4x4xi8, 4x1>, <4x8xf32, 8x1>, <4x8xf32, 8x1> -> <4x8xf32, 8x1>
317-
return %0 : !migraphx.shaped<4x8xf32, 8x1>
318-
}
319-
320-
// -----
398+
// ---- migraphx.slice ----
321399

322400
func.func @invalid_attr_size_mismatch(%input: !migraphx.shaped<10x10xf32, 10x1>) {
323401
// expected-error @+1 {{op axes, starts, and ends must have the same size}}

0 commit comments

Comments
 (0)