Skip to content

Commit f2e42d9

Browse files
authored
[mlir][IntRangeInference] Handle ceildivsi(INT_MIN, x > 1) as expected (llvm#116284)
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.
1 parent d82422f commit f2e42d9

File tree

2 files changed

+22
-0
lines changed

2 files changed

+22
-0
lines changed

mlir/lib/Interfaces/Utils/InferIntRangeCommon.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -375,6 +375,15 @@ mlir::intrange::inferCeilDivS(ArrayRef<ConstantIntRanges> argRanges) {
375375
result.sadd_ov(APInt(result.getBitWidth(), 1), overflowed);
376376
return overflowed ? std::optional<APInt>() : corrected;
377377
}
378+
// Special case where the usual implementation of ceilDiv causes
379+
// INT_MIN / [positive number] to be positive. This doesn't match the
380+
// definition of signed ceiling division mathematically, but it prevents
381+
// inconsistent constant-folding results. This arises because (-int_min) is
382+
// still negative, so -(-int_min / b) is -(int_min / b), which is
383+
// positive See #115293.
384+
if (lhs.isMinSignedValue() && rhs.sgt(1)) {
385+
return -result;
386+
}
378387
return result;
379388
};
380389
return inferDivSRange(lhs, rhs, ceilDivSIFix);

mlir/test/Dialect/Arith/int-range-interface.mlir

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,19 @@ func.func @ceil_divsi(%arg0 : index) -> i1 {
249249
func.return %10 : i1
250250
}
251251

252+
// CHECK-LABEL: func @ceil_divsi_intmin_bug_115293
253+
// CHECK: %[[ret:.*]] = arith.constant true
254+
// CHECK: return %[[ret]]
255+
func.func @ceil_divsi_intmin_bug_115293() -> i1 {
256+
%intMin_i64 = test.with_bounds { smin = -9223372036854775808 : si64, smax = -9223372036854775808 : si64, umin = 9223372036854775808 : ui64, umax = 9223372036854775808 : ui64 } : i64
257+
%denom_i64 = test.with_bounds { smin = 1189465982 : si64, smax = 1189465982 : si64, umin = 1189465982 : ui64, umax = 1189465982 : ui64 } : i64
258+
%res_i64 = test.with_bounds { smin = 7754212542 : si64, smax = 7754212542 : si64, umin = 7754212542 : ui64, umax = 7754212542 : ui64 } : i64
259+
260+
%0 = arith.ceildivsi %intMin_i64, %denom_i64 : i64
261+
%1 = arith.cmpi eq, %0, %res_i64 : i64
262+
func.return %1 : i1
263+
}
264+
252265
// CHECK-LABEL: func @floor_divsi
253266
// CHECK: %[[true:.*]] = arith.constant true
254267
// CHECK: return %[[true]]

0 commit comments

Comments
 (0)