xref: /llvm-project/mlir/include/mlir/Dialect/SPIRV/IR/SPIRVArithmeticOps.td (revision 88151dd4285cdd9feeb24ebb1be9cf5252ab0883)
1//===-- SPIRVArithmeticOps.td - MLIR SPIR-V Arithmetic Ops -*- tablegen -*-===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// This file contains arithmetic ops for the SPIR-V dialect. It corresponds
10// to "3.32.13. Arithmetic Instructions" of the SPIR-V specification.
11//
12//===----------------------------------------------------------------------===//
13
14#ifndef MLIR_DIALECT_SPIRV_IR_ARITHMETIC_OPS
15#define MLIR_DIALECT_SPIRV_IR_ARITHMETIC_OPS
16
17include "mlir/Dialect/SPIRV/IR/SPIRVBase.td"
18include "mlir/Interfaces/InferTypeOpInterface.td"
19include "mlir/Interfaces/SideEffectInterfaces.td"
20
21class SPIRV_ArithmeticBinaryOp<string mnemonic, Type type,
22                               list<Trait> traits = []> :
23      // Operands type same as result type.
24      SPIRV_BinaryOp<mnemonic, type, type,
25                   !listconcat(traits,
26                               [Pure, SameOperandsAndResultType])> {
27  // In addition to normal types arithmetic instructions can support cooperative
28  // matrix.
29  let arguments = (ins
30    SPIRV_ScalarOrVectorOrCoopMatrixOf<type>:$operand1,
31    SPIRV_ScalarOrVectorOrCoopMatrixOf<type>:$operand2
32  );
33
34  let results = (outs
35    SPIRV_ScalarOrVectorOrCoopMatrixOf<type>:$result
36  );
37  let assemblyFormat = "operands attr-dict `:` type($result)";
38}
39
40class SPIRV_ArithmeticUnaryOp<string mnemonic, Type type,
41                              list<Trait> traits = []> :
42      // Operand type same as result type.
43      SPIRV_UnaryOp<mnemonic, type, type,
44                   !listconcat(traits,
45                               [Pure, SameOperandsAndResultType])> {
46  // In addition to normal types arithmetic instructions can support cooperative
47  // matrix.
48  let arguments = (ins
49    SPIRV_ScalarOrVectorOrCoopMatrixOf<type>:$operand
50  );
51
52  let results = (outs
53    SPIRV_ScalarOrVectorOrCoopMatrixOf<type>:$result
54  );
55  let assemblyFormat = "operands attr-dict `:` type($result)";
56}
57
58class SPIRV_ArithmeticExtendedBinaryOp<string mnemonic,
59                                       list<Trait> traits = []> :
60      // Result type is a struct with two operand-typed elements.
61      SPIRV_BinaryOp<mnemonic, SPIRV_AnyStruct, SPIRV_Integer, traits> {
62  let arguments = (ins
63    SPIRV_ScalarOrVectorOf<SPIRV_Integer>:$operand1,
64    SPIRV_ScalarOrVectorOf<SPIRV_Integer>:$operand2
65  );
66
67  let results = (outs
68    SPIRV_AnyStruct:$result
69  );
70
71  let builders = [
72    OpBuilder<(ins "Value":$operand1, "Value":$operand2), [{
73      build($_builder, $_state,
74            ::mlir::spirv::StructType::get({operand1.getType(), operand1.getType()}),
75            operand1, operand2);
76    }]>
77  ];
78
79  // These ops require a custom verifier.
80  let hasVerifier = 1;
81}
82
83// -----
84
85def SPIRV_FAddOp : SPIRV_ArithmeticBinaryOp<"FAdd", SPIRV_Float, [Commutative]> {
86  let summary = "Floating-point addition of Operand 1 and Operand 2.";
87
88  let description = [{
89    Result Type must be a scalar or vector of floating-point type.
90
91    The types of Operand 1 and Operand 2 both must be the same as Result
92    Type.
93
94    Results are computed per component.
95
96    #### Example:
97
98    ```mlir
99    %4 = spirv.FAdd %0, %1 : f32
100    %5 = spirv.FAdd %2, %3 : vector<4xf32>
101    ```
102  }];
103}
104
105// -----
106
107def SPIRV_FDivOp : SPIRV_ArithmeticBinaryOp<"FDiv", SPIRV_Float, []> {
108  let summary = "Floating-point division of Operand 1 divided by Operand 2.";
109
110  let description = [{
111    Result Type must be a scalar or vector of floating-point type.
112
113    The types of Operand 1 and Operand 2 both must be the same as Result
114    Type.
115
116    Results are computed per component.  The resulting value is undefined
117    if Operand 2 is 0.
118
119    #### Example:
120
121    ```mlir
122    %4 = spirv.FDiv %0, %1 : f32
123    %5 = spirv.FDiv %2, %3 : vector<4xf32>
124    ```
125  }];
126}
127
128// -----
129
130def SPIRV_FModOp : SPIRV_ArithmeticBinaryOp<"FMod", SPIRV_Float, []> {
131  let summary = [{
132    The floating-point remainder whose sign matches the sign of Operand 2.
133  }];
134
135  let description = [{
136    Result Type must be a scalar or vector of floating-point type.
137
138    The types of Operand 1 and Operand 2 both must be the same as Result
139    Type.
140
141    Results are computed per component.  The resulting value is undefined
142    if Operand 2 is 0.  Otherwise, the result is the remainder r of Operand
143    1 divided by Operand 2 where if r ≠ 0, the sign of r is the same as the
144    sign of Operand 2.
145
146    #### Example:
147
148    ```mlir
149    %4 = spirv.FMod %0, %1 : f32
150    %5 = spirv.FMod %2, %3 : vector<4xf32>
151    ```
152  }];
153}
154
155// -----
156
157def SPIRV_FMulOp : SPIRV_ArithmeticBinaryOp<"FMul", SPIRV_Float, [Commutative]> {
158  let summary = "Floating-point multiplication of Operand 1 and Operand 2.";
159
160  let description = [{
161    Result Type must be a scalar or vector of floating-point type.
162
163    The types of Operand 1 and Operand 2 both must be the same as Result
164    Type.
165
166    Results are computed per component.
167
168    #### Example:
169
170    ```mlir
171    %4 = spirv.FMul %0, %1 : f32
172    %5 = spirv.FMul %2, %3 : vector<4xf32>
173    ```
174  }];
175}
176
177// -----
178
179def SPIRV_FNegateOp : SPIRV_ArithmeticUnaryOp<"FNegate", SPIRV_Float, []> {
180  let summary = [{
181    Inverts the sign bit of Operand. (Note, however, that OpFNegate is still
182    considered a floating-point instruction, and so is subject to the
183    general floating-point rules regarding, for example, subnormals and NaN
184    propagation).
185  }];
186
187  let description = [{
188    Result Type must be a scalar or vector of floating-point type.
189
190    The type of Operand must be the same as Result Type.
191
192    Results are computed per component.
193
194    #### Example:
195
196    ```mlir
197    %1 = spirv.FNegate %0 : f32
198    %3 = spirv.FNegate %2 : vector<4xf32>
199    ```
200  }];
201}
202
203// -----
204
205def SPIRV_FRemOp : SPIRV_ArithmeticBinaryOp<"FRem", SPIRV_Float, []> {
206  let summary = [{
207    The floating-point remainder whose sign matches the sign of Operand 1.
208  }];
209
210  let description = [{
211    Result Type must be a scalar or vector of floating-point type.
212
213    The types of Operand 1 and Operand 2 both must be the same as Result
214    Type.
215
216    Results are computed per component.  The resulting value is undefined
217    if Operand 2 is 0.  Otherwise, the result is the remainder r of Operand
218    1 divided by Operand 2 where if r ≠ 0, the sign of r is the same as the
219    sign of Operand 1.
220
221    #### Example:
222
223    ```mlir
224    %4 = spirv.FRemOp %0, %1 : f32
225    %5 = spirv.FRemOp %2, %3 : vector<4xf32>
226    ```
227  }];
228}
229
230// -----
231
232def SPIRV_FSubOp : SPIRV_ArithmeticBinaryOp<"FSub", SPIRV_Float, []> {
233  let summary = "Floating-point subtraction of Operand 2 from Operand 1.";
234
235  let description = [{
236    Result Type must be a scalar or vector of floating-point type.
237
238    The types of Operand 1 and Operand 2 both must be the same as Result
239    Type.
240
241    Results are computed per component.
242
243    #### Example:
244
245    ```mlir
246    %4 = spirv.FRemOp %0, %1 : f32
247    %5 = spirv.FRemOp %2, %3 : vector<4xf32>
248    ```
249  }];
250}
251
252// -----
253
254def SPIRV_IAddOp : SPIRV_ArithmeticBinaryOp<"IAdd",
255                                        SPIRV_Integer,
256                                        [Commutative, UsableInSpecConstantOp]> {
257  let summary = "Integer addition of Operand 1 and Operand 2.";
258
259  let description = [{
260    Result Type must be a scalar or vector of integer type.
261
262    The type of Operand 1 and Operand 2  must be a scalar or vector of
263    integer type.  They must have the same number of components as Result
264    Type. They must have the same component width as Result Type.
265
266    The resulting value will equal the low-order N bits of the correct
267    result R, where N is the component width and R is computed with enough
268    precision to avoid overflow and underflow.
269
270    Results are computed per component.
271
272    #### Example:
273
274    ```mlir
275    %4 = spirv.IAdd %0, %1 : i32
276    %5 = spirv.IAdd %2, %3 : vector<4xi32>
277
278    ```
279  }];
280
281  let hasFolder = 1;
282}
283
284// -----
285
286def SPIRV_IAddCarryOp : SPIRV_ArithmeticExtendedBinaryOp<"IAddCarry",
287                                                         [Commutative, Pure]> {
288  let summary = [{
289    Integer addition of Operand 1 and Operand 2, including the carry.
290  }];
291
292  let description = [{
293    Result Type must be from OpTypeStruct.  The struct must have two
294    members, and the two members must be the same type.  The member type
295    must be a scalar or vector of integer type, whose Signedness operand is
296    0.
297
298    Operand 1 and Operand 2 must have the same type as the members of Result
299    Type. These are consumed as unsigned integers.
300
301     Results are computed per component.
302
303    Member 0 of the result gets the low-order bits (full component width) of
304    the addition.
305
306    Member 1 of the result gets the high-order (carry) bit of the result of
307    the addition. That is, it gets the value 1 if the addition overflowed
308    the component width, and 0 otherwise.
309
310    <!-- End of AutoGen section -->
311
312    #### Example:
313
314    ```mlir
315    %2 = spirv.IAddCarry %0, %1 : !spirv.struct<(i32, i32)>
316    %2 = spirv.IAddCarry %0, %1 : !spirv.struct<(vector<2xi32>, vector<2xi32>)>
317    ```
318  }];
319
320  let hasCanonicalizer = 1;
321}
322
323// -----
324
325def SPIRV_IMulOp : SPIRV_ArithmeticBinaryOp<"IMul",
326                                        SPIRV_Integer,
327                                        [Commutative, UsableInSpecConstantOp]> {
328  let summary = "Integer multiplication of Operand 1 and Operand 2.";
329
330  let description = [{
331    Result Type must be a scalar or vector of integer type.
332
333    The type of Operand 1 and Operand 2  must be a scalar or vector of
334    integer type.  They must have the same number of components as Result
335    Type. They must have the same component width as Result Type.
336
337    The resulting value will equal the low-order N bits of the correct
338    result R, where N is the component width and R is computed with enough
339    precision to avoid overflow and underflow.
340
341    Results are computed per component.
342
343    #### Example:
344
345    ```mlir
346    %4 = spirv.IMul %0, %1 : i32
347    %5 = spirv.IMul %2, %3 : vector<4xi32>
348
349    ```
350  }];
351
352  let hasFolder = 1;
353}
354
355// -----
356
357def SPIRV_ISubOp : SPIRV_ArithmeticBinaryOp<"ISub",
358                                        SPIRV_Integer,
359                                        [UsableInSpecConstantOp]> {
360  let summary = "Integer subtraction of Operand 2 from Operand 1.";
361
362  let description = [{
363    Result Type must be a scalar or vector of integer type.
364
365    The type of Operand 1 and Operand 2  must be a scalar or vector of
366    integer type.  They must have the same number of components as Result
367    Type. They must have the same component width as Result Type.
368
369    The resulting value will equal the low-order N bits of the correct
370    result R, where N is the component width and R is computed with enough
371    precision to avoid overflow and underflow.
372
373    Results are computed per component.
374
375    #### Example:
376
377    ```mlir
378    %4 = spirv.ISub %0, %1 : i32
379    %5 = spirv.ISub %2, %3 : vector<4xi32>
380
381    ```
382  }];
383
384  let hasFolder = 1;
385}
386
387// -----
388
389def SPIRV_ISubBorrowOp : SPIRV_ArithmeticExtendedBinaryOp<"ISubBorrow",
390                                                          [Pure]> {
391  let summary = [{
392    Result is the unsigned integer subtraction of Operand 2 from Operand 1,
393    and what it needed to borrow.
394  }];
395
396  let description = [{
397    Result Type must be from OpTypeStruct.  The struct must have two
398    members, and the two members must be the same type.  The member type
399    must be a scalar or vector of integer type, whose Signedness operand is
400    0.
401
402    Operand 1 and Operand 2 must have the same type as the members of Result
403    Type. These are consumed as unsigned integers.
404
405     Results are computed per component.
406
407    Member 0 of the result gets the low-order bits (full component width) of
408    the subtraction. That is, if Operand 1 is larger than Operand 2, member
409    0 gets the full value of the subtraction;  if Operand 2 is larger than
410    Operand 1, member 0 gets 2w + Operand 1 - Operand 2, where w is the
411    component width.
412
413    Member 1 of the result gets 0 if Operand 1 ≥ Operand 2, and gets 1
414    otherwise.
415
416    <!-- End of AutoGen section -->
417
418    #### Example:
419
420    ```mlir
421    %2 = spirv.ISubBorrow %0, %1 : !spirv.struct<(i32, i32)>
422    %2 = spirv.ISubBorrow %0, %1 : !spirv.struct<(vector<2xi32>, vector<2xi32>)>
423    ```
424  }];
425}
426
427// -----
428
429def SPIRV_DotOp : SPIRV_Op<"Dot",
430                    [Pure, AllTypesMatch<["vector1", "vector2"]>,
431                     AllElementTypesMatch<["vector1", "result"]>]> {
432  let summary = "Dot product of Vector 1 and Vector 2";
433
434  let description = [{
435    Result Type must be a floating point scalar.
436
437    Vector 1 and Vector 2 must be vectors of the same type, and their component
438    type must be Result Type.
439
440    #### Example:
441
442    ```mlir
443    %0 = spirv.Dot %v1, %v2 : vector<4xf32> -> f32
444    ```
445  }];
446
447  let arguments = (ins
448    SPIRV_VectorOf<SPIRV_Float>:$vector1,
449    SPIRV_VectorOf<SPIRV_Float>:$vector2
450  );
451
452  let results = (outs
453    SPIRV_Float:$result
454  );
455
456  let assemblyFormat = "operands attr-dict `:` type($vector1) `->` type($result)";
457
458  let hasVerifier = 0;
459}
460
461// -----
462
463def SPIRV_SDivOp : SPIRV_ArithmeticBinaryOp<"SDiv",
464                                        SPIRV_Integer,
465                                        [UsableInSpecConstantOp]> {
466  let summary = "Signed-integer division of Operand 1 divided by Operand 2.";
467
468  let description = [{
469    Result Type must be a scalar or vector of integer type.
470
471    The type of Operand 1 and Operand 2  must be a scalar or vector of
472    integer type.  They must have the same number of components as Result
473    Type. They must have the same component width as Result Type.
474
475    Results are computed per component.  The resulting value is undefined
476    if Operand 2 is 0.
477
478    #### Example:
479
480    ```mlir
481    %4 = spirv.SDiv %0, %1 : i32
482    %5 = spirv.SDiv %2, %3 : vector<4xi32>
483
484    ```
485  }];
486
487  let hasFolder = 1;
488}
489
490// -----
491
492def SPIRV_SModOp : SPIRV_ArithmeticBinaryOp<"SMod",
493                                        SPIRV_Integer,
494                                        [UsableInSpecConstantOp]> {
495  let summary = [{
496    Signed remainder operation for the remainder whose sign matches the sign
497    of Operand 2.
498  }];
499
500  let description = [{
501    Result Type must be a scalar or vector of integer type.
502
503    The type of Operand 1 and Operand 2  must be a scalar or vector of
504    integer type.  They must have the same number of components as Result
505    Type. They must have the same component width as Result Type.
506
507    Results are computed per component.  The resulting value is undefined
508    if Operand 2 is 0.  Otherwise, the result is the remainder r of Operand
509    1 divided by Operand 2 where if r ≠ 0, the sign of r is the same as the
510    sign of Operand 2.
511
512    #### Example:
513
514    ```mlir
515    %4 = spirv.SMod %0, %1 : i32
516    %5 = spirv.SMod %2, %3 : vector<4xi32>
517
518    ```
519  }];
520
521  let hasFolder = 1;
522}
523
524// -----
525
526def SPIRV_SMulExtendedOp : SPIRV_ArithmeticExtendedBinaryOp<"SMulExtended",
527                                                            [Pure, Commutative]> {
528  let summary = [{
529    Result is the full value of the signed integer multiplication of Operand
530    1 and Operand 2.
531  }];
532
533  let description = [{
534    Result Type must be from OpTypeStruct.  The struct must have two
535    members, and the two members must be the same type.  The member type
536    must be a scalar or vector of integer type.
537
538    Operand 1 and Operand 2 must have the same type as the members of Result
539    Type. These are consumed as signed integers.
540
541    Results are computed per component.
542
543    Member 0 of the result gets the low-order bits of the multiplication.
544
545    Member 1 of the result gets the high-order bits of the multiplication.
546
547    <!-- End of AutoGen section -->
548
549    #### Example:
550
551    ```mlir
552    %2 = spirv.SMulExtended %0, %1 : !spirv.struct<(i32, i32)>
553    %2 = spirv.SMulExtended %0, %1 : !spirv.struct<(vector<2xi32>, vector<2xi32>)>
554    ```
555  }];
556
557  let hasCanonicalizer = 1;
558}
559
560// -----
561
562def SPIRV_SNegateOp : SPIRV_ArithmeticUnaryOp<"SNegate",
563                                          SPIRV_Integer,
564                                          [UsableInSpecConstantOp]> {
565  let summary = "Signed-integer subtract of Operand from zero.";
566
567  let description = [{
568    Result Type must be a scalar or vector of integer type.
569
570    Operand's type  must be a scalar or vector of integer type.  It must
571    have the same number of components as Result Type.  The component width
572    must equal the component width in Result Type.
573
574     Results are computed per component.
575
576    <!-- End of AutoGen section -->
577
578    #### Example:
579
580    ```mlir
581    %1 = spirv.SNegate %0 : i32
582    %3 = spirv.SNegate %2 : vector<4xi32>
583    ```
584  }];
585
586  let hasFolder = 1;
587}
588
589// -----
590
591def SPIRV_SRemOp : SPIRV_ArithmeticBinaryOp<"SRem",
592                                        SPIRV_Integer,
593                                        [UsableInSpecConstantOp]> {
594  let summary = [{
595    Signed remainder operation for the remainder whose sign matches the sign
596    of Operand 1.
597  }];
598
599  let description = [{
600    Result Type must be a scalar or vector of integer type.
601
602    The type of Operand 1 and Operand 2  must be a scalar or vector of
603    integer type.  They must have the same number of components as Result
604    Type. They must have the same component width as Result Type.
605
606    Results are computed per component.  The resulting value is undefined
607    if Operand 2 is 0.  Otherwise, the result is the remainder r of Operand
608    1 divided by Operand 2 where if r ≠ 0, the sign of r is the same as the
609    sign of Operand 1.
610
611    #### Example:
612
613    ```mlir
614    %4 = spirv.SRem %0, %1 : i32
615    %5 = spirv.SRem %2, %3 : vector<4xi32>
616
617    ```
618  }];
619
620  let hasFolder = 1;
621}
622
623// -----
624
625def SPIRV_UDivOp : SPIRV_ArithmeticBinaryOp<"UDiv",
626                                        SPIRV_Integer,
627                                        [UnsignedOp, UsableInSpecConstantOp]> {
628  let summary = "Unsigned-integer division of Operand 1 divided by Operand 2.";
629
630  let description = [{
631    Result Type must be a scalar or vector of integer type, whose Signedness
632    operand is 0.
633
634    The types of Operand 1 and Operand 2 both must be the same as Result
635    Type.
636
637    Results are computed per component.  The resulting value is undefined
638    if Operand 2 is 0.
639
640    #### Example:
641
642    ```mlir
643    %4 = spirv.UDiv %0, %1 : i32
644    %5 = spirv.UDiv %2, %3 : vector<4xi32>
645    ```
646  }];
647
648  let hasFolder = 1;
649}
650
651// -----
652
653def SPIRV_UMulExtendedOp : SPIRV_ArithmeticExtendedBinaryOp<"UMulExtended",
654                                                            [Pure, Commutative]> {
655  let summary = [{
656    Result is the full value of the unsigned integer multiplication of
657    Operand 1 and Operand 2.
658  }];
659
660  let description = [{
661    Result Type must be from OpTypeStruct.  The struct must have two
662    members, and the two members must be the same type.  The member type
663    must be a scalar or vector of integer type, whose Signedness operand is
664    0.
665
666    Operand 1 and Operand 2 must have the same type as the members of Result
667    Type. These are consumed as unsigned integers.
668
669    Results are computed per component.
670
671    Member 0 of the result gets the low-order bits of the multiplication.
672
673    Member 1 of the result gets the high-order bits of the multiplication.
674
675    <!-- End of AutoGen section -->
676
677    #### Example:
678
679    ```mlir
680    %2 = spirv.UMulExtended %0, %1 : !spirv.struct<(i32, i32)>
681    %2 = spirv.UMulExtended %0, %1 : !spirv.struct<(vector<2xi32>, vector<2xi32>)>
682    ```
683  }];
684
685  let hasCanonicalizer = 1;
686}
687
688// -----
689
690def SPIRV_VectorTimesScalarOp : SPIRV_Op<"VectorTimesScalar", [Pure]> {
691  let summary = "Scale a floating-point vector.";
692
693  let description = [{
694    Result Type must be a vector of floating-point type.
695
696     The type of Vector must be the same as Result Type. Each component of
697    Vector is multiplied by Scalar.
698
699    Scalar must have the same type as the Component Type in Result Type.
700
701    <!-- End of AutoGen section -->
702
703    #### Example:
704
705    ```mlir
706    %0 = spirv.VectorTimesScalar %vector, %scalar : vector<4xf32>
707    ```
708  }];
709
710  let arguments = (ins
711    VectorOfLengthAndType<[2, 3, 4], [SPIRV_Float]>:$vector,
712    SPIRV_Float:$scalar
713  );
714
715  let results = (outs
716    VectorOfLengthAndType<[2, 3, 4], [SPIRV_Float]>:$result
717  );
718
719  let assemblyFormat = "operands attr-dict `:` `(` type(operands) `)` `->` type($result)";
720}
721
722// -----
723
724def SPIRV_UModOp : SPIRV_ArithmeticBinaryOp<"UMod",
725                                        SPIRV_Integer,
726                                        [UnsignedOp, UsableInSpecConstantOp]> {
727  let summary = "Unsigned modulo operation of Operand 1 modulo Operand 2.";
728
729  let description = [{
730    Result Type must be a scalar or vector of integer type, whose Signedness
731    operand is 0.
732
733    The types of Operand 1 and Operand 2 both must be the same as Result
734    Type.
735
736    Results are computed per component.  The resulting value is undefined
737    if Operand 2 is 0.
738
739    #### Example:
740
741    ```mlir
742    %4 = spirv.UMod %0, %1 : i32
743    %5 = spirv.UMod %2, %3 : vector<4xi32>
744    ```
745  }];
746
747  let hasFolder = 1;
748  let hasCanonicalizer = 1;
749}
750
751#endif // MLIR_DIALECT_SPIRV_IR_ARITHMETIC_OPS
752