@@ -158,6 +158,19 @@ class Arith_IntBinaryOpWithOverflowFlags<string mnemonic, list<Trait> traits = [
158158 attr-dict `:` type($result) }];
159159}
160160
161+ class Arith_IntBinaryOpWithExactFlag<string mnemonic, list<Trait> traits = []> :
162+ Arith_BinaryOp<mnemonic, traits #
163+ [DeclareOpInterfaceMethods<InferIntRangeInterface, ["inferResultRanges"]>,
164+ DeclareOpInterfaceMethods<ArithExactFlagInterface>]>,
165+ Arguments<(ins SignlessIntegerOrIndexLike:$lhs,
166+ SignlessIntegerOrIndexLike:$rhs,
167+ UnitAttr:$isExact)>,
168+ Results<(outs SignlessIntegerOrIndexLike:$result)> {
169+
170+ let assemblyFormat = [{ $lhs `,` $rhs (`exact` $isExact^)?
171+ attr-dict `:` type($result) }];
172+ }
173+
161174//===----------------------------------------------------------------------===//
162175// ConstantOp
163176//===----------------------------------------------------------------------===//
@@ -482,7 +495,8 @@ def Arith_MulUIExtendedOp : Arith_Op<"mului_extended", [Pure, Commutative,
482495// DivUIOp
483496//===----------------------------------------------------------------------===//
484497
485- def Arith_DivUIOp : Arith_IntBinaryOp<"divui", [ConditionallySpeculatable]> {
498+ def Arith_DivUIOp : Arith_IntBinaryOpWithExactFlag<"divui",
499+ [ConditionallySpeculatable]> {
486500 let summary = "unsigned integer division operation";
487501 let description = [{
488502 Unsigned integer division. Rounds towards zero. Treats the leading bit as
@@ -493,12 +507,18 @@ def Arith_DivUIOp : Arith_IntBinaryOp<"divui", [ConditionallySpeculatable]> {
493507 `tensor` values, the behavior is undefined if _any_ elements are divided by
494508 zero.
495509
510+ If the `exact` attribute is present, the result value is poison if `lhs` is
511+ not a multiple of `rhs`.
512+
496513 Example:
497514
498515 ```mlir
499516 // Scalar unsigned integer division.
500517 %a = arith.divui %b, %c : i64
501518
519+ // Scalar unsigned integer division where %b is known to be a multiple of %c.
520+ %a = arith.divui %b, %c exact : i64
521+
502522 // SIMD vector element-wise division.
503523 %f = arith.divui %g, %h : vector<4xi32>
504524
@@ -519,7 +539,8 @@ def Arith_DivUIOp : Arith_IntBinaryOp<"divui", [ConditionallySpeculatable]> {
519539// DivSIOp
520540//===----------------------------------------------------------------------===//
521541
522- def Arith_DivSIOp : Arith_IntBinaryOp<"divsi", [ConditionallySpeculatable]> {
542+ def Arith_DivSIOp : Arith_IntBinaryOpWithExactFlag<"divsi",
543+ [ConditionallySpeculatable]> {
523544 let summary = "signed integer division operation";
524545 let description = [{
525546 Signed integer division. Rounds towards zero. Treats the leading bit as
@@ -530,12 +551,18 @@ def Arith_DivSIOp : Arith_IntBinaryOp<"divsi", [ConditionallySpeculatable]> {
530551 behavior is undefined if _any_ of its elements are divided by zero or has a
531552 signed division overflow.
532553
554+ If the `exact` attribute is present, the result value is poison if `lhs` is
555+ not a multiple of `rhs`.
556+
533557 Example:
534558
535559 ```mlir
536560 // Scalar signed integer division.
537561 %a = arith.divsi %b, %c : i64
538562
563+ // Scalar signed integer division where %b is known to be a multiple of %c.
564+ %a = arith.divsi %b, %c exact : i64
565+
539566 // SIMD vector element-wise division.
540567 %f = arith.divsi %g, %h : vector<4xi32>
541568
@@ -821,7 +848,7 @@ def Arith_ShLIOp : Arith_IntBinaryOpWithOverflowFlags<"shli"> {
821848// ShRUIOp
822849//===----------------------------------------------------------------------===//
823850
824- def Arith_ShRUIOp : Arith_TotalIntBinaryOp <"shrui"> {
851+ def Arith_ShRUIOp : Arith_IntBinaryOpWithExactFlag <"shrui", [Pure] > {
825852 let summary = "unsigned integer right-shift";
826853 let description = [{
827854 The `shrui` operation shifts an integer value of the first operand to the right
@@ -830,12 +857,17 @@ def Arith_ShRUIOp : Arith_TotalIntBinaryOp<"shrui"> {
830857 filled with zeros. If the value of the second operand is greater or equal than the
831858 bitwidth of the first operand, then the operation returns poison.
832859
860+ If the `exact` keyword is present, the result value of shrui is a poison
861+ value if any of the bits shifted out are non-zero.
862+
833863 Example:
834864
835865 ```mlir
836- %1 = arith.constant 160 : i8 // %1 is 0b10100000
866+ %1 = arith.constant 160 : i8 // %1 is 0b10100000
837867 %2 = arith.constant 3 : i8
838- %3 = arith.shrui %1, %2 : (i8, i8) -> i8 // %3 is 0b00010100
868+ %3 = arith.constant 6 : i8
869+ %4 = arith.shrui %1, %2 exact : i8 // %4 is 0b00010100
870+ %5 = arith.shrui %1, %3 : i8 // %3 is 0b00000010
839871 ```
840872 }];
841873 let hasFolder = 1;
@@ -845,7 +877,7 @@ def Arith_ShRUIOp : Arith_TotalIntBinaryOp<"shrui"> {
845877// ShRSIOp
846878//===----------------------------------------------------------------------===//
847879
848- def Arith_ShRSIOp : Arith_TotalIntBinaryOp <"shrsi"> {
880+ def Arith_ShRSIOp : Arith_IntBinaryOpWithExactFlag <"shrsi", [Pure] > {
849881 let summary = "signed integer right-shift";
850882 let description = [{
851883 The `shrsi` operation shifts an integer value of the first operand to the right
@@ -856,14 +888,17 @@ def Arith_ShRSIOp : Arith_TotalIntBinaryOp<"shrsi"> {
856888 operand is greater or equal than bitwidth of the first operand, then the operation
857889 returns poison.
858890
891+ If the `exact` keyword is present, the result value of shrsi is a poison
892+ value if any of the bits shifted out are non-zero.
893+
859894 Example:
860895
861896 ```mlir
862- %1 = arith.constant 160 : i8 // %1 is 0b10100000
897+ %1 = arith.constant 160 : i8 // %1 is 0b10100000
863898 %2 = arith.constant 3 : i8
864- %3 = arith.shrsi %1, %2 : (i8, i8) -> i8 // %3 is 0b11110100
865- %4 = arith.constant 96 : i8 // %4 is 0b01100000
866- %5 = arith.shrsi %4, %2 : (i8, i8) -> i8 // %5 is 0b00001100
899+ %3 = arith.shrsi %1, %2 exact : i8 // %3 is 0b11110100
900+ %4 = arith.constant 98 : i8 // %4 is 0b01100010
901+ %5 = arith.shrsi %4, %2 : i8 // %5 is 0b00001100
867902 ```
868903 }];
869904 let hasFolder = 1;
0 commit comments