[llvm] [InstCombine] Infer exact division from a known-zero remainder (PR #221509)
via llvm-commits
llvm-commits at lists.llvm.org
Sat Sep 5 21:23:42 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-llvm-transforms
Author: ZERICO2005
<details>
<summary>Changes</summary>
Fixes https://github.com/llvm/llvm-project/issues/108449
Related https://github.com/llvm/llvm-project/pull/126438
As others have stated before, there is no direct way to have C or C++ emit `udiv exact` or `sdiv exact`.
My goal with this PR is to allow LLVM to infer `udiv exact` or `sdiv exact` for user defined functions for performing exact division:
```c++
unsigned udiv_3_exact(unsigned x) {
if (x % 3 != 0) {
__builtin_unreachable();
}
return x / 3;
}
unsigned udiv_exact(unsigned x, unsigned y) {
if (y == 0 || x % y != 0) {
__builtin_unreachable();
}
return x / y;
}
```
`InstCombine` checks whether a matching remainder is known to be zero at the division, using information from assumptions and dominating conditions.
The tests also cover interaction with `DivRemPairs`. The inferred exact flag survives in the X86 cases, but is dropped in the PowerPC cases where the remainder is reconstructed from the division.
The `DivRemPairs` tests are adapted from https://github.com/llvm/llvm-project/pull/126438, with updated expectations for the current behavior.
Tests marked `TODO:` cover missed optimizations that I think are worth exploring and fixing. The other tests are intended as coverage and edge-case tests.
AI Disclosure:
I used ChatGPT Codex (Sol 5.6 and GPT-6 Astra) to help implement the code changes and tests.
---
Patch is 52.82 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/221509.diff
6 Files Affected:
- (modified) llvm/lib/Transforms/InstCombine/InstCombineMulDivRem.cpp (+43)
- (added) llvm/test/Transforms/DivRemPairs/PowerPC/div-rem-pairs-infer-exact.ll (+159)
- (added) llvm/test/Transforms/DivRemPairs/X86/div-rem-pairs-infer-exact.ll (+127)
- (added) llvm/test/Transforms/InstCombine/div-exact-from-rem-constant-lhs.ll (+104)
- (added) llvm/test/Transforms/InstCombine/div-exact-from-rem-constant-rhs.ll (+309)
- (added) llvm/test/Transforms/InstCombine/div-exact-from-rem.ll (+695)
``````````diff
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineMulDivRem.cpp b/llvm/lib/Transforms/InstCombine/InstCombineMulDivRem.cpp
index 128e3e3dcdfe8..5e5c081b72f7d 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineMulDivRem.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineMulDivRem.cpp
@@ -1318,6 +1318,39 @@ static Value *foldIDivShl(BinaryOperator &I, InstCombiner::BuilderTy &Builder) {
return nullptr;
}
+/// Return true if a matching remainder is known to be zero at the division.
+static bool isKnownExactFromRem(const BinaryOperator &Div,
+ const SimplifyQuery &Q) {
+ assert((Div.getOpcode() == Instruction::SDiv ||
+ Div.getOpcode() == Instruction::UDiv) &&
+ "Expected integer divide");
+
+ unsigned RemOpcode = Div.getOpcode() == Instruction::SDiv ? Instruction::SRem
+ : Instruction::URem;
+
+ Value *Op0 = Div.getOperand(0);
+ Value *Op1 = Div.getOperand(1);
+
+ // A matching remainder uses both operands. Avoid scanning constants, whose
+ // users may be shared across functions or whose use-lists may be unavailable.
+ Value *V = isa<Constant>(Op0) ? Op1 : Op0;
+ if (isa<Constant>(V))
+ return false;
+
+ for (User *U : V->users()) {
+ auto *Rem = dyn_cast<BinaryOperator>(U);
+ if (!Rem || Rem->getOpcode() != RemOpcode || Rem->getOperand(0) != Op0 ||
+ Rem->getOperand(1) != Op1 || (Q.DT && !Q.DT->dominates(Rem, &Div)))
+ continue;
+
+ KnownBits Known(Rem->getType()->getScalarSizeInBits());
+ computeKnownBitsFromContext(Rem, Known, Q.getWithInstruction(&Div));
+ if (Known.isZero())
+ return true;
+ }
+ return false;
+}
+
/// Common integer divide/remainder transforms
Instruction *InstCombinerImpl::commonIDivRemTransforms(BinaryOperator &I) {
assert(I.isIntDivRem() && "Unexpected instruction");
@@ -1801,6 +1834,11 @@ Instruction *InstCombinerImpl::visitUDiv(BinaryOperator &I) {
SQ.getWithInstruction(&I)))
return replaceInstUsesWith(I, V);
+ if (!I.isExact() && isKnownExactFromRem(I, SQ)) {
+ I.setIsExact();
+ return &I;
+ }
+
if (Instruction *X = foldVectorBinop(I))
return X;
@@ -1896,6 +1934,11 @@ Instruction *InstCombinerImpl::visitSDiv(BinaryOperator &I) {
SQ.getWithInstruction(&I)))
return replaceInstUsesWith(I, V);
+ if (!I.isExact() && isKnownExactFromRem(I, SQ)) {
+ I.setIsExact();
+ return &I;
+ }
+
if (Instruction *X = foldVectorBinop(I))
return X;
diff --git a/llvm/test/Transforms/DivRemPairs/PowerPC/div-rem-pairs-infer-exact.ll b/llvm/test/Transforms/DivRemPairs/PowerPC/div-rem-pairs-infer-exact.ll
new file mode 100644
index 0000000000000..fcc1590517b4b
--- /dev/null
+++ b/llvm/test/Transforms/DivRemPairs/PowerPC/div-rem-pairs-infer-exact.ll
@@ -0,0 +1,159 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
+; RUN: opt < %s -passes=instcombine -S -mtriple=powerpc64-unknown-unknown | FileCheck %s --check-prefix=INSTCOMBINE
+; RUN: opt < %s -passes="instcombine,div-rem-pairs" -S -mtriple=powerpc64-unknown-unknown | FileCheck %s
+
+; InstCombine infers exact, but DivRemPairs drops it when reconstructing the
+; remainder from the division. Check both stages so the loss stays visible.
+
+define i8 @udiv_exact_assume(i8 %x, i8 %y) {
+; INSTCOMBINE-LABEL: define i8 @udiv_exact_assume(
+; INSTCOMBINE-SAME: i8 [[X:%.*]], i8 [[Y:%.*]]) {
+; INSTCOMBINE-NEXT: [[REM:%.*]] = urem i8 [[X]], [[Y]]
+; INSTCOMBINE-NEXT: [[CMP:%.*]] = icmp eq i8 [[REM]], 0
+; INSTCOMBINE-NEXT: tail call void @llvm.assume(i1 [[CMP]])
+; INSTCOMBINE-NEXT: [[DIV3:%.*]] = udiv exact i8 [[X]], [[Y]]
+; INSTCOMBINE-NEXT: ret i8 [[DIV3]]
+;
+; CHECK-LABEL: define i8 @udiv_exact_assume(
+; CHECK-SAME: i8 [[X:%.*]], i8 [[Y:%.*]]) {
+; CHECK-NEXT: [[X_FROZEN:%.*]] = freeze i8 [[X]]
+; CHECK-NEXT: [[Y_FROZEN:%.*]] = freeze i8 [[Y]]
+; CHECK-NEXT: [[DIV3:%.*]] = udiv i8 [[X_FROZEN]], [[Y_FROZEN]]
+; CHECK-NEXT: [[TMP1:%.*]] = mul i8 [[DIV3]], [[Y_FROZEN]]
+; CHECK-NEXT: [[REM:%.*]] = sub i8 [[X_FROZEN]], [[TMP1]]
+; CHECK-NEXT: [[CMP:%.*]] = icmp eq i8 [[REM]], 0
+; CHECK-NEXT: tail call void @llvm.assume(i1 [[CMP]])
+; CHECK-NEXT: ret i8 [[DIV3]]
+;
+ %rem = urem i8 %x, %y
+ %cmp = icmp eq i8 %rem, 0
+ tail call void @llvm.assume(i1 %cmp)
+ %div3 = udiv i8 %x, %y
+ ret i8 %div3
+}
+
+define i8 @udiv_exact_assume_negative(i8 %x, i8 %y) {
+; INSTCOMBINE-LABEL: define i8 @udiv_exact_assume_negative(
+; INSTCOMBINE-SAME: i8 [[X:%.*]], i8 [[Y:%.*]]) {
+; INSTCOMBINE-NEXT: [[REM:%.*]] = urem i8 [[X]], [[Y]]
+; INSTCOMBINE-NEXT: [[CMP:%.*]] = icmp eq i8 [[REM]], 1
+; INSTCOMBINE-NEXT: tail call void @llvm.assume(i1 [[CMP]])
+; INSTCOMBINE-NEXT: [[DIV3:%.*]] = udiv i8 [[X]], [[Y]]
+; INSTCOMBINE-NEXT: ret i8 [[DIV3]]
+;
+; CHECK-LABEL: define i8 @udiv_exact_assume_negative(
+; CHECK-SAME: i8 [[X:%.*]], i8 [[Y:%.*]]) {
+; CHECK-NEXT: [[X_FROZEN:%.*]] = freeze i8 [[X]]
+; CHECK-NEXT: [[Y_FROZEN:%.*]] = freeze i8 [[Y]]
+; CHECK-NEXT: [[DIV3:%.*]] = udiv i8 [[X_FROZEN]], [[Y_FROZEN]]
+; CHECK-NEXT: [[TMP1:%.*]] = mul i8 [[DIV3]], [[Y_FROZEN]]
+; CHECK-NEXT: [[REM_DECOMPOSED:%.*]] = sub i8 [[X_FROZEN]], [[TMP1]]
+; CHECK-NEXT: [[CMP:%.*]] = icmp eq i8 [[REM_DECOMPOSED]], 1
+; CHECK-NEXT: tail call void @llvm.assume(i1 [[CMP]])
+; CHECK-NEXT: ret i8 [[DIV3]]
+;
+ %rem = urem i8 %x, %y
+ %cmp = icmp eq i8 %rem, 1
+ tail call void @llvm.assume(i1 %cmp)
+ %div3 = udiv i8 %x, %y
+ ret i8 %div3
+}
+
+
+; Exactness inferred from a dominating condition is also lost when the
+; remainder is decomposed.
+
+define i8 @infer_exact_from_dom_cond_negative(i8 %X, i8 %Y) {
+; INSTCOMBINE-LABEL: define i8 @infer_exact_from_dom_cond_negative(
+; INSTCOMBINE-SAME: i8 [[X:%.*]], i8 [[Y:%.*]]) {
+; INSTCOMBINE-NEXT: [[ENTRY:.*]]:
+; INSTCOMBINE-NEXT: [[REM:%.*]] = srem i8 [[X]], [[Y]]
+; INSTCOMBINE-NEXT: [[CMP:%.*]] = icmp eq i8 [[REM]], 0
+; INSTCOMBINE-NEXT: br i1 [[CMP]], label %[[IF_THEN:.*]], label %[[RETURN:.*]]
+; INSTCOMBINE: [[IF_THEN]]:
+; INSTCOMBINE-NEXT: [[DIV:%.*]] = sdiv exact i8 [[X]], [[Y]]
+; INSTCOMBINE-NEXT: br label %[[RETURN]]
+; INSTCOMBINE: [[RETURN]]:
+; INSTCOMBINE-NEXT: [[RETVAL_0:%.*]] = phi i8 [ [[DIV]], %[[IF_THEN]] ], [ 0, %[[ENTRY]] ]
+; INSTCOMBINE-NEXT: ret i8 [[RETVAL_0]]
+;
+; CHECK-LABEL: define i8 @infer_exact_from_dom_cond_negative(
+; CHECK-SAME: i8 [[X:%.*]], i8 [[Y:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*]]:
+; CHECK-NEXT: [[X_FROZEN:%.*]] = freeze i8 [[X]]
+; CHECK-NEXT: [[Y_FROZEN:%.*]] = freeze i8 [[Y]]
+; CHECK-NEXT: [[DIV:%.*]] = sdiv i8 [[X_FROZEN]], [[Y_FROZEN]]
+; CHECK-NEXT: [[TMP0:%.*]] = mul i8 [[DIV]], [[Y_FROZEN]]
+; CHECK-NEXT: [[REM_DECOMPOSED:%.*]] = sub i8 [[X_FROZEN]], [[TMP0]]
+; CHECK-NEXT: [[CMP:%.*]] = icmp eq i8 [[REM_DECOMPOSED]], 0
+; CHECK-NEXT: br i1 [[CMP]], label %[[IF_THEN:.*]], label %[[RETURN:.*]]
+; CHECK: [[IF_THEN]]:
+; CHECK-NEXT: br label %[[RETURN]]
+; CHECK: [[RETURN]]:
+; CHECK-NEXT: [[RETVAL_0:%.*]] = phi i8 [ [[DIV]], %[[IF_THEN]] ], [ 0, %[[ENTRY]] ]
+; CHECK-NEXT: ret i8 [[RETVAL_0]]
+;
+entry:
+ %rem = srem i8 %X, %Y
+ %cmp = icmp eq i8 %rem, 0
+ br i1 %cmp, label %if.then, label %return
+
+if.then:
+ %div = sdiv i8 %X, %Y
+ br label %return
+
+return:
+ %retval.0 = phi i8 [ %div, %if.then ], [ 0, %entry ]
+ ret i8 %retval.0
+}
+
+define i8 @infer_exact_from_dom_cond_false_path_negative(i8 %X, i8 %Y) {
+; INSTCOMBINE-LABEL: define i8 @infer_exact_from_dom_cond_false_path_negative(
+; INSTCOMBINE-SAME: i8 [[X:%.*]], i8 [[Y:%.*]]) {
+; INSTCOMBINE-NEXT: [[ENTRY:.*:]]
+; INSTCOMBINE-NEXT: [[REM:%.*]] = srem i8 [[X]], [[Y]]
+; INSTCOMBINE-NEXT: [[CMP_NOT:%.*]] = icmp eq i8 [[REM]], 0
+; INSTCOMBINE-NEXT: br i1 [[CMP_NOT]], label %[[IF_ELSE:.*]], label %[[IF_THEN:.*]]
+; INSTCOMBINE: [[IF_THEN]]:
+; INSTCOMBINE-NEXT: br label %[[RETURN:.*]]
+; INSTCOMBINE: [[IF_ELSE]]:
+; INSTCOMBINE-NEXT: [[DIV:%.*]] = sdiv exact i8 [[X]], [[Y]]
+; INSTCOMBINE-NEXT: br label %[[RETURN]]
+; INSTCOMBINE: [[RETURN]]:
+; INSTCOMBINE-NEXT: [[RETVAL_0:%.*]] = phi i8 [ 0, %[[IF_THEN]] ], [ [[DIV]], %[[IF_ELSE]] ]
+; INSTCOMBINE-NEXT: ret i8 [[RETVAL_0]]
+;
+; CHECK-LABEL: define i8 @infer_exact_from_dom_cond_false_path_negative(
+; CHECK-SAME: i8 [[X:%.*]], i8 [[Y:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[X_FROZEN:%.*]] = freeze i8 [[X]]
+; CHECK-NEXT: [[Y_FROZEN:%.*]] = freeze i8 [[Y]]
+; CHECK-NEXT: [[DIV:%.*]] = sdiv i8 [[X_FROZEN]], [[Y_FROZEN]]
+; CHECK-NEXT: [[TMP0:%.*]] = mul i8 [[DIV]], [[Y_FROZEN]]
+; CHECK-NEXT: [[REM_DECOMPOSED:%.*]] = sub i8 [[X_FROZEN]], [[TMP0]]
+; CHECK-NEXT: [[CMP_NOT:%.*]] = icmp eq i8 [[REM_DECOMPOSED]], 0
+; CHECK-NEXT: br i1 [[CMP_NOT]], label %[[IF_ELSE:.*]], label %[[IF_THEN:.*]]
+; CHECK: [[IF_THEN]]:
+; CHECK-NEXT: br label %[[RETURN:.*]]
+; CHECK: [[IF_ELSE]]:
+; CHECK-NEXT: br label %[[RETURN]]
+; CHECK: [[RETURN]]:
+; CHECK-NEXT: [[RETVAL_0:%.*]] = phi i8 [ 0, %[[IF_THEN]] ], [ [[DIV]], %[[IF_ELSE]] ]
+; CHECK-NEXT: ret i8 [[RETVAL_0]]
+;
+entry:
+ %rem = srem i8 %X, %Y
+ %cmp = icmp ne i8 %rem, 0
+ br i1 %cmp, label %if.then, label %if.else
+
+if.then:
+ br label %return
+
+if.else:
+ %div = sdiv i8 %X, %Y
+ br label %return
+
+return:
+ %retval.0 = phi i8 [ 0, %if.then ], [ %div, %if.else ]
+ ret i8 %retval.0
+}
diff --git a/llvm/test/Transforms/DivRemPairs/X86/div-rem-pairs-infer-exact.ll b/llvm/test/Transforms/DivRemPairs/X86/div-rem-pairs-infer-exact.ll
new file mode 100644
index 0000000000000..e038f3f422639
--- /dev/null
+++ b/llvm/test/Transforms/DivRemPairs/X86/div-rem-pairs-infer-exact.ll
@@ -0,0 +1,127 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
+; RUN: opt < %s -passes="instcombine,div-rem-pairs" -S -mtriple=x86_64-unknown-unknown | FileCheck %s
+
+; Exactness inferred from assumptions and dominating conditions is preserved
+; when DivRemPairs can keep separate division and remainder instructions.
+
+define i8 @udiv_exact_assume(i8 %x, i8 %y) {
+; CHECK-LABEL: define i8 @udiv_exact_assume(
+; CHECK-SAME: i8 [[X:%.*]], i8 [[Y:%.*]]) {
+; CHECK-NEXT: [[REM:%.*]] = urem i8 [[X]], [[Y]]
+; CHECK-NEXT: [[CMP:%.*]] = icmp eq i8 [[REM]], 0
+; CHECK-NEXT: tail call void @llvm.assume(i1 [[CMP]])
+; CHECK-NEXT: [[DIV3:%.*]] = udiv exact i8 [[X]], [[Y]]
+; CHECK-NEXT: ret i8 [[DIV3]]
+;
+ %rem = urem i8 %x, %y
+ %cmp = icmp eq i8 %rem, 0
+ tail call void @llvm.assume(i1 %cmp)
+ %div3 = udiv i8 %x, %y
+ ret i8 %div3
+}
+
+define i8 @udiv_exact_assume_negative(i8 %x, i8 %y) {
+; CHECK-LABEL: define i8 @udiv_exact_assume_negative(
+; CHECK-SAME: i8 [[X:%.*]], i8 [[Y:%.*]]) {
+; CHECK-NEXT: [[REM:%.*]] = urem i8 [[X]], [[Y]]
+; CHECK-NEXT: [[CMP:%.*]] = icmp eq i8 [[REM]], 1
+; CHECK-NEXT: tail call void @llvm.assume(i1 [[CMP]])
+; CHECK-NEXT: [[DIV3:%.*]] = udiv i8 [[X]], [[Y]]
+; CHECK-NEXT: ret i8 [[DIV3]]
+;
+ %rem = urem i8 %x, %y
+ %cmp = icmp eq i8 %rem, 1
+ tail call void @llvm.assume(i1 %cmp)
+ %div3 = udiv i8 %x, %y
+ ret i8 %div3
+}
+
+define i8 @infer_exact_from_dom_cond(i8 %X, i8 %Y) {
+; CHECK-LABEL: define i8 @infer_exact_from_dom_cond(
+; CHECK-SAME: i8 [[X:%.*]], i8 [[Y:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*]]:
+; CHECK-NEXT: [[REM:%.*]] = srem i8 [[X]], [[Y]]
+; CHECK-NEXT: [[DIV:%.*]] = sdiv exact i8 [[X]], [[Y]]
+; CHECK-NEXT: [[CMP:%.*]] = icmp eq i8 [[REM]], 0
+; CHECK-NEXT: br i1 [[CMP]], label %[[IF_THEN:.*]], label %[[RETURN:.*]]
+; CHECK: [[IF_THEN]]:
+; CHECK-NEXT: br label %[[RETURN]]
+; CHECK: [[RETURN]]:
+; CHECK-NEXT: [[RETVAL_0:%.*]] = phi i8 [ [[DIV]], %[[IF_THEN]] ], [ 0, %[[ENTRY]] ]
+; CHECK-NEXT: ret i8 [[RETVAL_0]]
+;
+entry:
+ %rem = srem i8 %X, %Y
+ %cmp = icmp eq i8 %rem, 0
+ br i1 %cmp, label %if.then, label %return
+
+if.then:
+ %div = sdiv i8 %X, %Y
+ br label %return
+
+return:
+ %retval.0 = phi i8 [ %div, %if.then ], [ 0, %entry ]
+ ret i8 %retval.0
+}
+
+define i8 @infer_exact_from_dom_cond_false_path(i8 %X, i8 %Y) {
+; CHECK-LABEL: define i8 @infer_exact_from_dom_cond_false_path(
+; CHECK-SAME: i8 [[X:%.*]], i8 [[Y:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[REM:%.*]] = srem i8 [[X]], [[Y]]
+; CHECK-NEXT: [[DIV:%.*]] = sdiv exact i8 [[X]], [[Y]]
+; CHECK-NEXT: [[CMP_NOT:%.*]] = icmp eq i8 [[REM]], 0
+; CHECK-NEXT: br i1 [[CMP_NOT]], label %[[IF_ELSE:.*]], label %[[IF_THEN:.*]]
+; CHECK: [[IF_THEN]]:
+; CHECK-NEXT: br label %[[RETURN:.*]]
+; CHECK: [[IF_ELSE]]:
+; CHECK-NEXT: br label %[[RETURN]]
+; CHECK: [[RETURN]]:
+; CHECK-NEXT: [[RETVAL_0:%.*]] = phi i8 [ 0, %[[IF_THEN]] ], [ [[DIV]], %[[IF_ELSE]] ]
+; CHECK-NEXT: ret i8 [[RETVAL_0]]
+;
+entry:
+ %rem = srem i8 %X, %Y
+ %cmp = icmp ne i8 %rem, 0
+ br i1 %cmp, label %if.then, label %if.else
+
+if.then:
+ br label %return
+
+if.else:
+ %div = sdiv i8 %X, %Y
+ br label %return
+
+return:
+ %retval.0 = phi i8 [ 0, %if.then ], [ %div, %if.else ]
+ ret i8 %retval.0
+}
+
+
+define i8 @infer_exact_from_dom_cond_negative(i8 %X, i8 %Y) {
+; CHECK-LABEL: define i8 @infer_exact_from_dom_cond_negative(
+; CHECK-SAME: i8 [[X:%.*]], i8 [[Y:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*]]:
+; CHECK-NEXT: [[REM:%.*]] = srem i8 [[X]], [[Y]]
+; CHECK-NEXT: [[DIV:%.*]] = sdiv i8 [[X]], [[Y]]
+; CHECK-NEXT: [[CMP:%.*]] = icmp eq i8 [[REM]], 1
+; CHECK-NEXT: br i1 [[CMP]], label %[[IF_THEN:.*]], label %[[RETURN:.*]]
+; CHECK: [[IF_THEN]]:
+; CHECK-NEXT: br label %[[RETURN]]
+; CHECK: [[RETURN]]:
+; CHECK-NEXT: [[RETVAL_0:%.*]] = phi i8 [ [[DIV]], %[[IF_THEN]] ], [ 0, %[[ENTRY]] ]
+; CHECK-NEXT: ret i8 [[RETVAL_0]]
+;
+entry:
+ %rem = srem i8 %X, %Y
+ %cmp = icmp eq i8 %rem, 1
+ br i1 %cmp, label %if.then, label %return
+
+if.then:
+ %div = sdiv i8 %X, %Y
+ br label %return
+
+return:
+ %retval.0 = phi i8 [ %div, %if.then ], [ 0, %entry ]
+ ret i8 %retval.0
+}
diff --git a/llvm/test/Transforms/InstCombine/div-exact-from-rem-constant-lhs.ll b/llvm/test/Transforms/InstCombine/div-exact-from-rem-constant-lhs.ll
new file mode 100644
index 0000000000000..b9cea3924fd96
--- /dev/null
+++ b/llvm/test/Transforms/InstCombine/div-exact-from-rem-constant-lhs.ll
@@ -0,0 +1,104 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
+; RUN: opt < %s -passes=instcombine -S | FileCheck %s
+
+declare void @llvm.assume(i1 noundef)
+
+define i32 @ret_1_udiv_exact_from_zero_remainder(i32 %x) {
+; CHECK-LABEL: define i32 @ret_1_udiv_exact_from_zero_remainder(
+; CHECK-SAME: i32 [[X:%.*]]) {
+; CHECK-NEXT: [[DOTNOT:%.*]] = icmp eq i32 [[X]], 1
+; CHECK-NEXT: call void @llvm.assume(i1 [[DOTNOT]])
+; CHECK-NEXT: ret i32 1
+;
+ %rem = urem i32 1, %x
+ %is.zero = icmp eq i32 %rem, 0
+ call void @llvm.assume(i1 %is.zero)
+ %quot = udiv i32 1, %x
+ ret i32 %quot
+}
+
+define i32 @ret_pos_1_sdiv_exact_from_zero_remainder(i32 %x) {
+; CHECK-LABEL: define i32 @ret_pos_1_sdiv_exact_from_zero_remainder(
+; CHECK-SAME: i32 [[X:%.*]]) {
+; CHECK-NEXT: [[X_FR:%.*]] = freeze i32 [[X]]
+; CHECK-NEXT: [[REM:%.*]] = srem i32 1, [[X_FR]]
+; CHECK-NEXT: [[IS_ZERO:%.*]] = icmp eq i32 [[REM]], 0
+; CHECK-NEXT: call void @llvm.assume(i1 [[IS_ZERO]])
+; CHECK-NEXT: [[TMP1:%.*]] = add i32 [[X_FR]], 1
+; CHECK-NEXT: [[TMP2:%.*]] = icmp ult i32 [[TMP1]], 3
+; CHECK-NEXT: [[QUOT:%.*]] = select i1 [[TMP2]], i32 [[X_FR]], i32 0
+; CHECK-NEXT: ret i32 [[QUOT]]
+;
+ %rem = srem i32 1, %x
+ %is.zero = icmp eq i32 %rem, 0
+ call void @llvm.assume(i1 %is.zero)
+ %quot = sdiv i32 1, %x
+ ret i32 %quot
+}
+
+define i32 @ret_neg_1_sdiv_exact_from_zero_remainder(i32 %x) {
+; CHECK-LABEL: define i32 @ret_neg_1_sdiv_exact_from_zero_remainder(
+; CHECK-SAME: i32 [[X:%.*]]) {
+; CHECK-NEXT: [[REM:%.*]] = srem i32 -1, [[X]]
+; CHECK-NEXT: [[IS_ZERO:%.*]] = icmp eq i32 [[REM]], 0
+; CHECK-NEXT: call void @llvm.assume(i1 [[IS_ZERO]])
+; CHECK-NEXT: [[QUOT:%.*]] = sdiv exact i32 -1, [[X]]
+; CHECK-NEXT: ret i32 [[QUOT]]
+;
+ %rem = srem i32 -1, %x
+ %is.zero = icmp eq i32 %rem, 0
+ call void @llvm.assume(i1 %is.zero)
+ %quot = sdiv i32 -1, %x
+ ret i32 %quot
+}
+
+; Note that INT_MIN / -1 is undefined behavior.
+define i32 @ret_intmin_sdiv_exact_from_zero_remainder(i32 %x) {
+; CHECK-LABEL: define i32 @ret_intmin_sdiv_exact_from_zero_remainder(
+; CHECK-SAME: i32 [[X:%.*]]) {
+; CHECK-NEXT: [[REM:%.*]] = srem i32 -2147483648, [[X]]
+; CHECK-NEXT: [[IS_ZERO:%.*]] = icmp eq i32 [[REM]], 0
+; CHECK-NEXT: call void @llvm.assume(i1 [[IS_ZERO]])
+; CHECK-NEXT: [[QUOT:%.*]] = sdiv exact i32 -2147483648, [[X]]
+; CHECK-NEXT: ret i32 [[QUOT]]
+;
+ %rem = srem i32 -2147483648, %x
+ %is.zero = icmp eq i32 %rem, 0
+ call void @llvm.assume(i1 %is.zero)
+ %quot = sdiv i32 -2147483648, %x
+ ret i32 %quot
+}
+
+; 105 = 7 * 5 * 3, so it has a lot of factors.
+define i32 @ret_105_udiv_exact_from_zero_remainder(i32 %x) {
+; CHECK-LABEL: define i32 @ret_105_udiv_exact_from_zero_remainder(
+; CHECK-SAME: i32 [[X:%.*]]) {
+; CHECK-NEXT: [[REM:%.*]] = urem i32 105, [[X]]
+; CHECK-NEXT: [[IS_ZERO:%.*]] = icmp eq i32 [[REM]], 0
+; CHECK-NEXT: call void @llvm.assume(i1 [[IS_ZERO]])
+; CHECK-NEXT: [[QUOT:%.*]] = udiv exact i32 105, [[X]]
+; CHECK-NEXT: ret i32 [[QUOT]]
+;
+ %rem = urem i32 105, %x
+ %is.zero = icmp eq i32 %rem, 0
+ call void @llvm.assume(i1 %is.zero)
+ %quot = udiv i32 105, %x
+ ret i32 %quot
+}
+
+; 11 is prime, so x can only be 1 or 11 if the division is exact.
+define i32 @ret_11_udiv_exact_from_zero_remainder(i32 %x) {
+; CHECK-LABEL: define i32 @ret_11_udiv_exact_from_zero_remainder(
+; CHECK-SAME: i32 [[X:%.*]]) {
+; CHECK-NEXT: [[REM:%.*]] = urem i32 11, [[X]]
+; CHECK-NEXT: [[IS_ZERO:%.*]] = icmp eq i32 [[REM]], 0
+; CHECK-NEXT: call void @llvm.assume(i1 [[IS_ZERO]])
+; CHECK-NEXT: [[QUOT:%.*]] = udiv exact i32 11, [[X]]
+; CHECK-NEXT: ret i32 [[QUOT]]
+;
+ %rem = urem i32 11, %x
+ %is.zero = icmp eq i32 %rem, 0
+ call void @llvm.assume(i1 %is.zero)
+ %quot = udiv i32 11, %x
+ ret i32 %quot
+}
diff --git a/llvm/test/Transforms/InstCombine/div-exact-from-rem-constant-rhs.ll b/llvm/test/Transforms/InstCombine/div-exact-from-rem-constant-rhs.ll
new file mode 100644
index 0000000000000..d6bcc43da677d
--- /dev/null
+++ b/llvm/test/Transforms/InstCombine/div-exact-from-rem-constant-rhs.ll
@@ -0,0 +1,309 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
+; RUN: opt < %s -passes=instcombine -S | FileCheck %s
+
+declare void @llvm.assume(i1 noundef)
+
+; Unsigned division with a constant RHS.
+
+define i32 @ret_udiv_exact_3_from_zero_remainder(i32 %x) {
+; CHECK-LABEL: define i32 @ret_udiv_exact_3_from_zero_remainder(
+; CHECK-SAME: i32 [[X:%.*]]) {
+; CHECK-NEXT: [[REM:%.*]] = urem i32 [[X]], 3
+; CHECK-NEXT: [[IS_ZERO:%.*]] = icmp eq i32 [[REM]], 0
+; CHECK-NEXT: call void @llvm.assume(i1 [[IS_ZERO]])
+; CHECK-NEXT: [[QUOT:%.*]] = udiv exact i32 [[X]], 3
+; CHECK-NEXT: ret i32 [[QUOT]]
+;
+ %rem = urem i32 %x, 3
+ %is.zero = icmp eq i32 %rem, 0
+ call void @llvm.assume(i1 %is.zero)
+ %quot = udiv i32 %x, 3
+ ret i32 %quot
+}
+
+define i32 @ret_udiv_exact_3_from_zero_remainder_unreachable(i32 %x) {
+; CHECK-LABEL: define i32 @ret_udiv_exact_3_from_zero_remainder_unreachable(
+; CHECK-SAME: i32 [[X:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[REM:%.*]] = urem i32 [[X]], 3
+; CHECK-NEXT: [[NOT_ZERO_NOT:%.*]] = icmp eq i32 [[REM]], 0
+; CHECK-NEXT: br i1 [[NOT_ZERO_NOT]], label %[[DIV:.*]], label %[[UNREACHABLE:.*]]
+; CHECK: [[UNREACHABLE]]:
+; CHECK-NEXT: unreachable
+; CHECK: [[DIV]]:
+; CHECK-NEXT: [[QUOT:%.*]] = udiv exact i32 [[X]], 3
+; CHECK-NEXT: ret i32 [[QUOT]]
+;
+entry:
+ %rem = urem i32 %x, 3
+ %not.zero = icmp ne i32 %rem, 0
+ br i1 %not.zero, label %unreachable, label %div
+
+unreachable:
+ unreachable
+
+div:
+ %quot = udiv i32 %x, 3
+ ret i32 %quot
+}
+
+define i32 @ret_udiv_exact_10000_from_zero_remainder(i32 %x) {
+; CHECK-LABE...
[truncated]
``````````
</details>
https://github.com/llvm/llvm-project/pull/221509
More information about the llvm-commits
mailing list