[mlir][arith] Fold trivial integer division and remainder (#212074)
Add value-preserving folds mirroring LLVM's InstructionSimplify for the integer division and remainder ops: ``` divui/divsi/ceildivui/ceildivsi/floordivsi(0, x) -> 0 divui/divsi/ceildivui/ceildivsi/floordivsi(x, x) -> 1 remui/remsi(0, x) -> 0 remui/remsi(x, x) -> 0 ``` The self and zero-dividend cases are valid refinements because division or remainder by zero is undefined behaviour; no overflow flags are required. The folds return a scalar or splat constant and bail out on shaped types with a dynamic shape. Folding `x / 0` and `x % 0` to poison is left as a TODO: it would make the arith dialect depend on the ub dialect to materialize `ub.poison`. LLVM performs the same simplifications: https://godbolt.org/z/dzTjYjs4e Verified with Alive2: | Fold | udiv | sdiv | urem | srem | | :--- | :---: | :---: | :---: | :---: | | `x / x -> 1`, `x % x -> 0` | [✓](https://alive2.llvm.org/ce/z/YjZCEA) | [✓](https://alive2.llvm.org/ce/z/BSWQrm) | [✓](https://alive2.llvm.org/ce/z/gx5sRH) | [✓](https://alive2.llvm.org/ce/z/VUYkr4) | | `0 / x -> 0`, `0 % x -> 0` | [✓](https://alive2.llvm.org/ce/z/keqeCZ) | [✓](https://alive2.llvm.org/ce/z/jyf5wm) | [✓](https://alive2.llvm.org/ce/z/fZa6ND) | [✓](https://alive2.llvm.org/ce/z/fo6ss8) | ---- Code generated by Claude Code. Signed-off-by: Víctor Pérez Carrasco <victor.pc.upm@gmail.com>
This commit is contained in:
parent
286fcce7e7
commit
bdd8c64a2a
5 changed files with 237 additions and 27 deletions
|
|
@ -178,6 +178,19 @@ static Attribute getBoolAttribute(Type type, bool value) {
|
|||
return DenseElementsAttr::get(shapedType, boolAttr);
|
||||
}
|
||||
|
||||
/// Return a scalar or splat integer attribute of `type` (an integer/index type
|
||||
/// or a shaped type thereof) holding `value`. Returns a null attribute for
|
||||
/// shaped types with a dynamic shape, so callers can bail out of folding.
|
||||
static Attribute getIntegerAttrOfType(Type type, int64_t value) {
|
||||
auto scalarAttr = IntegerAttr::get(getElementTypeOrSelf(type), value);
|
||||
ShapedType shapedType = dyn_cast<ShapedType>(type);
|
||||
if (!shapedType)
|
||||
return scalarAttr;
|
||||
if (!shapedType.hasStaticShape())
|
||||
return {};
|
||||
return DenseElementsAttr::get(shapedType, scalarAttr);
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// TableGen'd canonicalization patterns
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
|
@ -791,10 +804,22 @@ static Value foldDivMul(Value lhs, Value rhs,
|
|||
}
|
||||
|
||||
OpFoldResult arith::DivUIOp::fold(FoldAdaptor adaptor) {
|
||||
// TODO: divui (x, 0) -> poison. Division by zero is undefined behaviour and
|
||||
// could fold to poison, but that would make the arith dialect depend on the
|
||||
// ub dialect to materialize ub.poison; left out for now.
|
||||
|
||||
// divui (x, 1) -> x.
|
||||
if (matchPattern(adaptor.getRhs(), m_One()))
|
||||
return getLhs();
|
||||
|
||||
// divui (0, x) -> 0. Division by zero is UB, so refining to 0 is valid.
|
||||
if (matchPattern(adaptor.getLhs(), m_Zero()))
|
||||
return getLhs();
|
||||
|
||||
// divui (x, x) -> 1.
|
||||
if (getLhs() == getRhs())
|
||||
return getIntegerAttrOfType(getType(), 1);
|
||||
|
||||
// (a * b) / b -> a
|
||||
if (Value val = foldDivMul(getLhs(), getRhs(), IntegerOverflowFlags::nuw))
|
||||
return val;
|
||||
|
|
@ -831,10 +856,22 @@ Speculation::Speculatability arith::DivUIOp::getSpeculatability() {
|
|||
//===----------------------------------------------------------------------===//
|
||||
|
||||
OpFoldResult arith::DivSIOp::fold(FoldAdaptor adaptor) {
|
||||
// TODO: divsi (x, 0) -> poison. Division by zero is undefined behaviour and
|
||||
// could fold to poison, but that would make the arith dialect depend on the
|
||||
// ub dialect to materialize ub.poison; left out for now.
|
||||
|
||||
// divsi (x, 1) -> x.
|
||||
if (matchPattern(adaptor.getRhs(), m_One()))
|
||||
return getLhs();
|
||||
|
||||
// divsi (0, x) -> 0. Division by zero is UB, so refining to 0 is valid.
|
||||
if (matchPattern(adaptor.getLhs(), m_Zero()))
|
||||
return getLhs();
|
||||
|
||||
// divsi (x, x) -> 1.
|
||||
if (getLhs() == getRhs())
|
||||
return getIntegerAttrOfType(getType(), 1);
|
||||
|
||||
// (a * b) / b -> a
|
||||
if (Value val = foldDivMul(getLhs(), getRhs(), IntegerOverflowFlags::nsw))
|
||||
return val;
|
||||
|
|
@ -887,10 +924,22 @@ static APInt signedCeilNonnegInputs(const APInt &a, const APInt &b,
|
|||
//===----------------------------------------------------------------------===//
|
||||
|
||||
OpFoldResult arith::CeilDivUIOp::fold(FoldAdaptor adaptor) {
|
||||
// TODO: ceildivui (x, 0) -> poison. Division by zero is undefined behaviour
|
||||
// and could fold to poison, but that would make the arith dialect depend on
|
||||
// the ub dialect to materialize ub.poison; left out for now.
|
||||
|
||||
// ceildivui (x, 1) -> x.
|
||||
if (matchPattern(adaptor.getRhs(), m_One()))
|
||||
return getLhs();
|
||||
|
||||
// ceildivui (0, x) -> 0. Division by zero is UB, so refining to 0 is valid.
|
||||
if (matchPattern(adaptor.getLhs(), m_Zero()))
|
||||
return getLhs();
|
||||
|
||||
// ceildivui (x, x) -> 1.
|
||||
if (getLhs() == getRhs())
|
||||
return getIntegerAttrOfType(getType(), 1);
|
||||
|
||||
bool overflowOrDiv0 = false;
|
||||
auto result = constFoldBinaryOp<IntegerAttr>(
|
||||
adaptor.getOperands(), [&](APInt a, const APInt &b) {
|
||||
|
|
@ -917,10 +966,22 @@ Speculation::Speculatability arith::CeilDivUIOp::getSpeculatability() {
|
|||
//===----------------------------------------------------------------------===//
|
||||
|
||||
OpFoldResult arith::CeilDivSIOp::fold(FoldAdaptor adaptor) {
|
||||
// TODO: ceildivsi (x, 0) -> poison. Division by zero is undefined behaviour
|
||||
// and could fold to poison, but that would make the arith dialect depend on
|
||||
// the ub dialect to materialize ub.poison; left out for now.
|
||||
|
||||
// ceildivsi (x, 1) -> x.
|
||||
if (matchPattern(adaptor.getRhs(), m_One()))
|
||||
return getLhs();
|
||||
|
||||
// ceildivsi (0, x) -> 0. Division by zero is UB, so refining to 0 is valid.
|
||||
if (matchPattern(adaptor.getLhs(), m_Zero()))
|
||||
return getLhs();
|
||||
|
||||
// ceildivsi (x, x) -> 1.
|
||||
if (getLhs() == getRhs())
|
||||
return getIntegerAttrOfType(getType(), 1);
|
||||
|
||||
// Don't fold if it would overflow or if it requires a division by zero.
|
||||
// TODO: This hook won't fold operations where a = MININT, because
|
||||
// negating MININT overflows. This can be improved.
|
||||
|
|
@ -986,10 +1047,22 @@ Speculation::Speculatability arith::CeilDivSIOp::getSpeculatability() {
|
|||
//===----------------------------------------------------------------------===//
|
||||
|
||||
OpFoldResult arith::FloorDivSIOp::fold(FoldAdaptor adaptor) {
|
||||
// TODO: floordivsi (x, 0) -> poison. Division by zero is undefined behaviour
|
||||
// and could fold to poison, but that would make the arith dialect depend on
|
||||
// the ub dialect to materialize ub.poison; left out for now.
|
||||
|
||||
// floordivsi (x, 1) -> x.
|
||||
if (matchPattern(adaptor.getRhs(), m_One()))
|
||||
return getLhs();
|
||||
|
||||
// floordivsi (0, x) -> 0. Division by zero is UB, so refining to 0 is valid.
|
||||
if (matchPattern(adaptor.getLhs(), m_Zero()))
|
||||
return getLhs();
|
||||
|
||||
// floordivsi (x, x) -> 1.
|
||||
if (getLhs() == getRhs())
|
||||
return getIntegerAttrOfType(getType(), 1);
|
||||
|
||||
// Don't fold if it would overflow or if it requires a division by zero.
|
||||
bool overflowOrDiv = false;
|
||||
auto result = constFoldBinaryOp<IntegerAttr>(
|
||||
|
|
@ -1009,9 +1082,18 @@ OpFoldResult arith::FloorDivSIOp::fold(FoldAdaptor adaptor) {
|
|||
//===----------------------------------------------------------------------===//
|
||||
|
||||
OpFoldResult arith::RemUIOp::fold(FoldAdaptor adaptor) {
|
||||
// TODO: remui (x, 0) -> poison. Remainder by zero is undefined behaviour and
|
||||
// could fold to poison, but that would make the arith dialect depend on the
|
||||
// ub dialect to materialize ub.poison; left out for now.
|
||||
|
||||
// remui (x, 1) -> 0.
|
||||
if (matchPattern(adaptor.getRhs(), m_One()))
|
||||
return Builder(getContext()).getZeroAttr(getType());
|
||||
return getIntegerAttrOfType(getType(), 0);
|
||||
|
||||
// remui (0, x) -> 0 and remui (x, x) -> 0. Division by zero is UB, so
|
||||
// refining to 0 is valid.
|
||||
if (matchPattern(adaptor.getLhs(), m_Zero()) || getLhs() == getRhs())
|
||||
return getIntegerAttrOfType(getType(), 0);
|
||||
|
||||
// Don't fold if it would require a division by zero.
|
||||
bool div0 = false;
|
||||
|
|
@ -1036,9 +1118,18 @@ Speculation::Speculatability arith::RemUIOp::getSpeculatability() {
|
|||
//===----------------------------------------------------------------------===//
|
||||
|
||||
OpFoldResult arith::RemSIOp::fold(FoldAdaptor adaptor) {
|
||||
// TODO: remsi (x, 0) -> poison. Remainder by zero is undefined behaviour and
|
||||
// could fold to poison, but that would make the arith dialect depend on the
|
||||
// ub dialect to materialize ub.poison; left out for now.
|
||||
|
||||
// remsi (x, 1) -> 0.
|
||||
if (matchPattern(adaptor.getRhs(), m_One()))
|
||||
return Builder(getContext()).getZeroAttr(getType());
|
||||
return getIntegerAttrOfType(getType(), 0);
|
||||
|
||||
// remsi (0, x) -> 0 and remsi (x, x) -> 0. Division by zero is UB, so
|
||||
// refining to 0 is valid.
|
||||
if (matchPattern(adaptor.getLhs(), m_Zero()) || getLhs() == getRhs())
|
||||
return getIntegerAttrOfType(getType(), 0);
|
||||
|
||||
// Don't fold if it would require a division by zero.
|
||||
bool div0 = false;
|
||||
|
|
|
|||
|
|
@ -11,14 +11,14 @@ func.func @vector_ops(%arg0: vector<4xf32>, %arg1: vector<4xi1>, %arg2: vector<4
|
|||
%0 = arith.constant dense<42.> : vector<4xf32>
|
||||
// CHECK-NEXT: %1 = llvm.fadd %arg0, %0 : vector<4xf32>
|
||||
%1 = arith.addf %arg0, %0 : vector<4xf32>
|
||||
// CHECK-NEXT: %2 = llvm.sdiv %arg2, %arg2 : vector<4xi64>
|
||||
%3 = arith.divsi %arg2, %arg2 : vector<4xi64>
|
||||
// CHECK-NEXT: %3 = llvm.udiv %arg2, %arg2 : vector<4xi64>
|
||||
%4 = arith.divui %arg2, %arg2 : vector<4xi64>
|
||||
// CHECK-NEXT: %4 = llvm.srem %arg2, %arg2 : vector<4xi64>
|
||||
%5 = arith.remsi %arg2, %arg2 : vector<4xi64>
|
||||
// CHECK-NEXT: %5 = llvm.urem %arg2, %arg2 : vector<4xi64>
|
||||
%6 = arith.remui %arg2, %arg2 : vector<4xi64>
|
||||
// CHECK-NEXT: %2 = llvm.sdiv %arg2, %arg3 : vector<4xi64>
|
||||
%3 = arith.divsi %arg2, %arg3 : vector<4xi64>
|
||||
// CHECK-NEXT: %3 = llvm.udiv %arg2, %arg3 : vector<4xi64>
|
||||
%4 = arith.divui %arg2, %arg3 : vector<4xi64>
|
||||
// CHECK-NEXT: %4 = llvm.srem %arg2, %arg3 : vector<4xi64>
|
||||
%5 = arith.remsi %arg2, %arg3 : vector<4xi64>
|
||||
// CHECK-NEXT: %5 = llvm.urem %arg2, %arg3 : vector<4xi64>
|
||||
%6 = arith.remui %arg2, %arg3 : vector<4xi64>
|
||||
// CHECK-NEXT: %6 = llvm.fdiv %arg0, %0 : vector<4xf32>
|
||||
%7 = arith.divf %arg0, %0 : vector<4xf32>
|
||||
// CHECK-NEXT: %7 = llvm.frem %arg0, %0 : vector<4xf32>
|
||||
|
|
@ -780,16 +780,16 @@ func.func @ceildivsi(%arg0 : i64, %arg1 : i64) -> i64 {
|
|||
}
|
||||
|
||||
// CHECK-LABEL: @ceildivui
|
||||
// CHECK-SAME: %[[ARG0:.*]]: i32) -> i32
|
||||
func.func @ceildivui(%arg0 : i32) -> i32 {
|
||||
// CHECK-SAME: %[[ARG0:.*]]: i32, %[[ARG1:.*]]: i32) -> i32
|
||||
func.func @ceildivui(%arg0 : i32, %arg1 : i32) -> i32 {
|
||||
// CHECK: %[[CST0:.*]] = llvm.mlir.constant(0 : i32) : i32
|
||||
// CHECK: %[[CMP0:.*]] = llvm.icmp "eq" %[[ARG0]], %[[CST0]] : i32
|
||||
// CHECK: %[[CST1:.*]] = llvm.mlir.constant(1 : i32) : i32
|
||||
// CHECK: %[[SUB0:.*]] = llvm.sub %[[ARG0]], %[[CST1]] : i32
|
||||
// CHECK: %[[DIV0:.*]] = llvm.udiv %[[SUB0]], %[[ARG0]] : i32
|
||||
// CHECK: %[[DIV0:.*]] = llvm.udiv %[[SUB0]], %[[ARG1]] : i32
|
||||
// CHECK: %[[ADD0:.*]] = llvm.add %[[DIV0]], %[[CST1]] : i32
|
||||
// CHECK: %[[SEL0:.*]] = llvm.select %[[CMP0]], %[[CST0]], %[[ADD0]] : i1, i32
|
||||
%0 = arith.ceildivui %arg0, %arg0 : i32
|
||||
%0 = arith.ceildivui %arg0, %arg1 : i32
|
||||
return %0: i32
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -53,10 +53,10 @@ func.func @unsupported_2x2elem_vector(%arg0: vector<2x2xi32>) {
|
|||
|
||||
// -----
|
||||
|
||||
func.func @int_vector4_invalid(%arg0: vector<2xi16>) {
|
||||
func.func @int_vector4_invalid(%arg0: vector<2xi16>, %arg1: vector<2xi16>) {
|
||||
// expected-error @+2 {{failed to legalize operation 'arith.divui'}}
|
||||
// expected-error @+1 {{bitwidth emulation is not implemented yet on unsigned op}}
|
||||
%0 = arith.divui %arg0, %arg0: vector<2xi16>
|
||||
%0 = arith.divui %arg0, %arg1: vector<2xi16>
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -198,18 +198,18 @@ func.func @float32_binary_scalar(%lhs: f32, %rhs: f32) {
|
|||
|
||||
// Check int vector types.
|
||||
// CHECK-LABEL: @int_vector234
|
||||
func.func @int_vector234(%arg0: vector<2xi8>, %arg1: vector<4xi64>) {
|
||||
func.func @int_vector234(%arg0: vector<2xi8>, %arg1: vector<2xi8>, %arg2: vector<4xi64>, %arg3: vector<4xi64>) {
|
||||
// CHECK: spirv.SDiv %{{.*}}, %{{.*}}: vector<2xi8>
|
||||
%0 = arith.divsi %arg0, %arg0: vector<2xi8>
|
||||
%0 = arith.divsi %arg0, %arg1: vector<2xi8>
|
||||
// CHECK: spirv.UDiv %{{.*}}, %{{.*}}: vector<4xi64>
|
||||
%1 = arith.divui %arg1, %arg1: vector<4xi64>
|
||||
%1 = arith.divui %arg2, %arg3: vector<4xi64>
|
||||
return
|
||||
}
|
||||
|
||||
// CHECK-LABEL: @index_vector
|
||||
func.func @index_vector(%arg0: vector<4xindex>) {
|
||||
func.func @index_vector(%arg0: vector<4xindex>, %arg1: vector<4xindex>) {
|
||||
// CHECK: spirv.UMod %{{.*}}, %{{.*}}: vector<4xi32>
|
||||
%0 = arith.remui %arg0, %arg0: vector<4xindex>
|
||||
%0 = arith.remui %arg0, %arg1: vector<4xindex>
|
||||
return
|
||||
}
|
||||
|
||||
|
|
@ -1522,11 +1522,11 @@ func.func @float32_maxnumf_scalar(%arg0 : vector<2xf32>, %arg1 : vector<2xf32>)
|
|||
|
||||
// Check int vector types.
|
||||
// CHECK-LABEL: @int_vector234
|
||||
func.func @int_vector234(%arg0: vector<2xi8>, %arg1: vector<4xi64>) {
|
||||
func.func @int_vector234(%arg0: vector<2xi8>, %arg1: vector<2xi8>, %arg2: vector<4xi64>, %arg3: vector<4xi64>) {
|
||||
// CHECK: spirv.SDiv %{{.*}}, %{{.*}}: vector<2xi8>
|
||||
%0 = arith.divsi %arg0, %arg0: vector<2xi8>
|
||||
%0 = arith.divsi %arg0, %arg1: vector<2xi8>
|
||||
// CHECK: spirv.UDiv %{{.*}}, %{{.*}}: vector<4xi64>
|
||||
%1 = arith.divui %arg1, %arg1: vector<4xi64>
|
||||
%1 = arith.divui %arg2, %arg3: vector<4xi64>
|
||||
return
|
||||
}
|
||||
|
||||
|
|
@ -1570,11 +1570,11 @@ module attributes {
|
|||
} {
|
||||
|
||||
// CHECK-LABEL: @int_vector23
|
||||
func.func @int_vector23(%arg0: vector<2xi8>, %arg1: vector<3xi16>) {
|
||||
func.func @int_vector23(%arg0: vector<2xi8>, %arg1: vector<2xi8>, %arg2: vector<3xi16>, %arg3: vector<3xi16>) {
|
||||
// CHECK: spirv.SDiv %{{.*}}, %{{.*}}: vector<2xi32>
|
||||
%0 = arith.divsi %arg0, %arg0: vector<2xi8>
|
||||
%0 = arith.divsi %arg0, %arg1: vector<2xi8>
|
||||
// CHECK: spirv.SDiv %{{.*}}, %{{.*}}: vector<3xi32>
|
||||
%1 = arith.divsi %arg1, %arg1: vector<3xi16>
|
||||
%1 = arith.divsi %arg2, %arg3: vector<3xi16>
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3187,6 +3187,125 @@ func.func @no_fold_divsi_of_muli(%arg0 : index, %arg1 : index) -> index {
|
|||
|
||||
// -----
|
||||
|
||||
// CHECK-LABEL: func @divui_zero_dividend
|
||||
// CHECK: %[[C0:.+]] = arith.constant 0 : i32
|
||||
// CHECK: return %[[C0]]
|
||||
func.func @divui_zero_dividend(%arg0 : i32) -> i32 {
|
||||
%c0 = arith.constant 0 : i32
|
||||
%0 = arith.divui %c0, %arg0 : i32
|
||||
return %0 : i32
|
||||
}
|
||||
|
||||
// CHECK-LABEL: func @divsi_zero_dividend
|
||||
// CHECK: %[[C0:.+]] = arith.constant 0 : i32
|
||||
// CHECK: return %[[C0]]
|
||||
func.func @divsi_zero_dividend(%arg0 : i32) -> i32 {
|
||||
%c0 = arith.constant 0 : i32
|
||||
%0 = arith.divsi %c0, %arg0 : i32
|
||||
return %0 : i32
|
||||
}
|
||||
|
||||
// CHECK-LABEL: func @divui_self
|
||||
// CHECK: %[[C1:.+]] = arith.constant 1 : i32
|
||||
// CHECK: return %[[C1]]
|
||||
func.func @divui_self(%arg0 : i32) -> i32 {
|
||||
%0 = arith.divui %arg0, %arg0 : i32
|
||||
return %0 : i32
|
||||
}
|
||||
|
||||
// CHECK-LABEL: func @divsi_self
|
||||
// CHECK: %[[C1:.+]] = arith.constant 1 : i32
|
||||
// CHECK: return %[[C1]]
|
||||
func.func @divsi_self(%arg0 : i32) -> i32 {
|
||||
%0 = arith.divsi %arg0, %arg0 : i32
|
||||
return %0 : i32
|
||||
}
|
||||
|
||||
// CHECK-LABEL: func @ceildivui_self
|
||||
// CHECK: %[[C1:.+]] = arith.constant 1 : i32
|
||||
// CHECK: return %[[C1]]
|
||||
func.func @ceildivui_self(%arg0 : i32) -> i32 {
|
||||
%0 = arith.ceildivui %arg0, %arg0 : i32
|
||||
return %0 : i32
|
||||
}
|
||||
|
||||
// CHECK-LABEL: func @ceildivsi_self
|
||||
// CHECK: %[[C1:.+]] = arith.constant 1 : i32
|
||||
// CHECK: return %[[C1]]
|
||||
func.func @ceildivsi_self(%arg0 : i32) -> i32 {
|
||||
%0 = arith.ceildivsi %arg0, %arg0 : i32
|
||||
return %0 : i32
|
||||
}
|
||||
|
||||
// CHECK-LABEL: func @floordivsi_self
|
||||
// CHECK: %[[C1:.+]] = arith.constant 1 : i32
|
||||
// CHECK: return %[[C1]]
|
||||
func.func @floordivsi_self(%arg0 : i32) -> i32 {
|
||||
%0 = arith.floordivsi %arg0, %arg0 : i32
|
||||
return %0 : i32
|
||||
}
|
||||
|
||||
// CHECK-LABEL: func @remui_self_and_zero
|
||||
// CHECK: %[[C0:.+]] = arith.constant 0 : i32
|
||||
// CHECK: return %[[C0]], %[[C0]]
|
||||
func.func @remui_self_and_zero(%arg0 : i32) -> (i32, i32) {
|
||||
%c0 = arith.constant 0 : i32
|
||||
%0 = arith.remui %arg0, %arg0 : i32
|
||||
%1 = arith.remui %c0, %arg0 : i32
|
||||
return %0, %1 : i32, i32
|
||||
}
|
||||
|
||||
// CHECK-LABEL: func @remsi_self_and_zero
|
||||
// CHECK: %[[C0:.+]] = arith.constant 0 : i32
|
||||
// CHECK: return %[[C0]], %[[C0]]
|
||||
func.func @remsi_self_and_zero(%arg0 : i32) -> (i32, i32) {
|
||||
%c0 = arith.constant 0 : i32
|
||||
%0 = arith.remsi %arg0, %arg0 : i32
|
||||
%1 = arith.remsi %c0, %arg0 : i32
|
||||
return %0, %1 : i32, i32
|
||||
}
|
||||
|
||||
// CHECK-LABEL: func @divui_self_vector
|
||||
// CHECK: %[[C1:.+]] = arith.constant dense<1> : vector<4xi32>
|
||||
// CHECK: return %[[C1]]
|
||||
func.func @divui_self_vector(%arg0 : vector<4xi32>) -> vector<4xi32> {
|
||||
%0 = arith.divui %arg0, %arg0 : vector<4xi32>
|
||||
return %0 : vector<4xi32>
|
||||
}
|
||||
|
||||
// CHECK-LABEL: func @ceildivui_ceildivsi_floordivsi_zero_dividend
|
||||
// CHECK: %[[C0:.+]] = arith.constant 0 : i32
|
||||
// CHECK: return %[[C0]], %[[C0]], %[[C0]]
|
||||
func.func @ceildivui_ceildivsi_floordivsi_zero_dividend(%arg0 : i32)
|
||||
-> (i32, i32, i32) {
|
||||
%c0 = arith.constant 0 : i32
|
||||
%0 = arith.ceildivui %c0, %arg0 : i32
|
||||
%1 = arith.ceildivsi %c0, %arg0 : i32
|
||||
%2 = arith.floordivsi %c0, %arg0 : i32
|
||||
return %0, %1, %2 : i32, i32, i32
|
||||
}
|
||||
|
||||
// Distinct operands must not fold to the self identity.
|
||||
// CHECK-LABEL: func @divsi_distinct_no_fold
|
||||
// CHECK: arith.divsi
|
||||
func.func @divsi_distinct_no_fold(%arg0 : i32, %arg1 : i32) -> i32 {
|
||||
%0 = arith.divsi %arg0, %arg1 : i32
|
||||
return %0 : i32
|
||||
}
|
||||
|
||||
// A dynamic shape must bail out of the self/zero-dividend constant folds.
|
||||
// CHECK-LABEL: func @div_rem_self_dynamic_no_fold
|
||||
// CHECK: arith.divui
|
||||
// CHECK: arith.remui
|
||||
func.func @div_rem_self_dynamic_no_fold(%arg0 : tensor<?xi32>)
|
||||
-> (tensor<?xi32>, tensor<?xi32>) {
|
||||
%0 = arith.divui %arg0, %arg0 : tensor<?xi32>
|
||||
%1 = arith.remui %arg0, %arg0 : tensor<?xi32>
|
||||
return %0, %1 : tensor<?xi32>, tensor<?xi32>
|
||||
}
|
||||
|
||||
// -----
|
||||
|
||||
// CHECK-LABEL: @test_cmpf(
|
||||
func.func @test_cmpf(%arg0 : f32) -> (i1, i1, i1, i1) {
|
||||
// CHECK-DAG: %[[T:.*]] = arith.constant true
|
||||
|
|
|
|||
Loading…
Reference in a new issue