Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[mlir][IntRangeInference] Handle ceildivsi(INT_MIN, x > 1) as expected #116284

Open
wants to merge 3 commits into
base: main
Choose a base branch
from

Conversation

krzysz00
Copy link
Contributor

Fixes #115293

While the definition of ceildivsi is integer division, rounding up, most implementations will use -(-a / b) for dividing a ceildiv b with a negative and b positive.

Mathematically, and for most integers, these two definitions are equivalent. However, with a == INT_MIN, the initial negation is a noop, which means that, while divinding and rounding up would give a negative result, -((- INT_MIN) / b) is -(INT_MIN / b), which is positive.

This commit adds a special case to ceilDivSI inference to handle this case and bring it in line with the operational instead of the mathematical semantics of ceiling division.

Fixes llvm#115293

While the definition of ceildivsi is integer division, rounding up,
most implementations will use `-(-a / b)` for dividing `a ceildiv b`
with `a` negative and `b` positive.

Mathematically, and for most integers, these two definitions are
equivalent. However, with `a == INT_MIN`, the initial negation is a
noop, which means that, while divinding and rounding up would give a
negative result, `-((- INT_MIN) / b)` is `-(INT_MIN / b)`, which is
positive.

This commit adds a special case to ceilDivSI inference to handle this
case and bring it in line with the operational instead of the
mathematical semantics of ceiling division.
@llvmbot
Copy link

llvmbot commented Nov 14, 2024

@llvm/pr-subscribers-mlir-arith

@llvm/pr-subscribers-mlir

Author: Krzysztof Drewniak (krzysz00)

Changes

Fixes #115293

While the definition of ceildivsi is integer division, rounding up, most implementations will use -(-a / b) for dividing a ceildiv b with a negative and b positive.

Mathematically, and for most integers, these two definitions are equivalent. However, with a == INT_MIN, the initial negation is a noop, which means that, while divinding and rounding up would give a negative result, -((- INT_MIN) / b) is -(INT_MIN / b), which is positive.

This commit adds a special case to ceilDivSI inference to handle this case and bring it in line with the operational instead of the mathematical semantics of ceiling division.


Full diff: https://github.com/llvm/llvm-project/pull/116284.diff

2 Files Affected:

  • (modified) mlir/lib/Interfaces/Utils/InferIntRangeCommon.cpp (+9)
  • (modified) mlir/test/Dialect/Arith/int-range-interface.mlir (+13)
diff --git a/mlir/lib/Interfaces/Utils/InferIntRangeCommon.cpp b/mlir/lib/Interfaces/Utils/InferIntRangeCommon.cpp
index c5610ba5d3c0bc..0b085b10b2b337 100644
--- a/mlir/lib/Interfaces/Utils/InferIntRangeCommon.cpp
+++ b/mlir/lib/Interfaces/Utils/InferIntRangeCommon.cpp
@@ -375,6 +375,15 @@ mlir::intrange::inferCeilDivS(ArrayRef<ConstantIntRanges> argRanges) {
           result.sadd_ov(APInt(result.getBitWidth(), 1), overflowed);
       return overflowed ? std::optional<APInt>() : corrected;
     }
+    // Special case where the usual implementation of ceilDiv causes
+    // INT_MIN / [positive number] to be positive. This doesn't match the
+    // definition of signed ceiling division mathematically, but it prevents
+    // inconsistent constant-folding results. This arises because (-int_min) is
+    // still negative, so -(-int_min / b) is -(int_min / b), which is
+    // positive See #115293.
+    if (lhs.isMinSignedValue() && rhs.sgt(1)) {
+      return -result;
+    }
     return result;
   };
   return inferDivSRange(lhs, rhs, ceilDivSIFix);
diff --git a/mlir/test/Dialect/Arith/int-range-interface.mlir b/mlir/test/Dialect/Arith/int-range-interface.mlir
index 6d66da2fc1eb35..235d61f2f042ac 100644
--- a/mlir/test/Dialect/Arith/int-range-interface.mlir
+++ b/mlir/test/Dialect/Arith/int-range-interface.mlir
@@ -249,6 +249,19 @@ func.func @ceil_divsi(%arg0 : index) -> i1 {
     func.return %10 : i1
 }
 
+// CHECK-LABEL: func @ceil_divsi_intmin_bug_115293
+// CHECK: %[[ret:.*]] = arith.constant true
+// CHECK: return %[[ret]]
+func.func @ceil_divsi_intmin_bug_115293() -> i1 {
+    %cIntMin_i64 = arith.constant -9223372036854775808 : i64
+    %cDenom_i64 = arith.constant 1189465982 : i64
+    %cRes_i64 = arith.constant 7754212542 : i64
+
+    %0 = arith.ceildivsi %cIntMin_i64, %cDenom_i64 : i64
+    %1 = arith.cmpi eq, %0, %cRes_i64 : i64
+    func.return %1 : i1
+}
+
 // CHECK-LABEL: func @floor_divsi
 // CHECK: %[[true:.*]] = arith.constant true
 // CHECK: return %[[true]]

func.func @ceil_divsi_intmin_bug_115293() -> i1 {
%cIntMin_i64 = arith.constant -9223372036854775808 : i64
%cDenom_i64 = arith.constant 1189465982 : i64
%cRes_i64 = arith.constant 7754212542 : i64
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

General comment: this check doesn't look very reliable, as this folding can be result from both int-range-optimizations and just usual folding. We probably should use with_bounds everywhere.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Point, I can change it (though I do know this one doesn't constant-fold)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wait, intMin_i64 is not used?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

... hold on, wait what? How'd my local run pass? Checking again.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[mlir] Inconsistent results for arith.ceildivsi
3 participants