[llvm] [InstCombine] Infer exact division from a known-zero remainder (PR #221509)
via llvm-commits
llvm-commits at lists.llvm.org
Sat Sep 5 20:28:29 PDT 2026
https://github.com/ZERICO2005 updated https://github.com/llvm/llvm-project/pull/221509
>From 7fd14075aad311fc02a847a7d07f15f3eb15eb1b Mon Sep 17 00:00:00 2001
From: zerico <zerico2005 at gmail.com>
Date: Sat, 5 Sep 2026 20:56:09 -0600
Subject: [PATCH] [InstCombine] Infer exact division from a known-zero
remainder
---
.../InstCombine/InstCombineMulDivRem.cpp | 43 ++
.../PowerPC/div-rem-pairs-infer-exact.ll | 159 ++++
.../X86/div-rem-pairs-infer-exact.ll | 127 ++++
.../div-exact-from-rem-constant-lhs.ll | 104 +++
.../div-exact-from-rem-constant-rhs.ll | 309 ++++++++
.../InstCombine/div-exact-from-rem.ll | 695 ++++++++++++++++++
6 files changed, 1437 insertions(+)
create mode 100644 llvm/test/Transforms/DivRemPairs/PowerPC/div-rem-pairs-infer-exact.ll
create mode 100644 llvm/test/Transforms/DivRemPairs/X86/div-rem-pairs-infer-exact.ll
create mode 100644 llvm/test/Transforms/InstCombine/div-exact-from-rem-constant-lhs.ll
create mode 100644 llvm/test/Transforms/InstCombine/div-exact-from-rem-constant-rhs.ll
create mode 100644 llvm/test/Transforms/InstCombine/div-exact-from-rem.ll
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-LABEL: define i32 @ret_udiv_exact_10000_from_zero_remainder(
+; CHECK-SAME: i32 [[X:%.*]]) {
+; CHECK-NEXT: [[REM:%.*]] = urem i32 [[X]], 10000
+; 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]], 10000
+; CHECK-NEXT: ret i32 [[QUOT]]
+;
+ %rem = urem i32 %x, 10000
+ %is.zero = icmp eq i32 %rem, 0
+ call void @llvm.assume(i1 %is.zero)
+ %quot = udiv i32 %x, 10000
+ ret i32 %quot
+}
+
+; If x is divisible by both 2 and 3, it must also be divisible by 6.
+define i32 @ret_udiv_exact_6_from_products(i32 %x) {
+; CHECK-LABEL: define i32 @ret_udiv_exact_6_from_products(
+; CHECK-SAME: i32 [[X:%.*]]) {
+; CHECK-NEXT: [[REM1:%.*]] = and i32 [[X]], 1
+; CHECK-NEXT: [[IS_ZERO1:%.*]] = icmp eq i32 [[REM1]], 0
+; CHECK-NEXT: call void @llvm.assume(i1 [[IS_ZERO1]])
+; CHECK-NEXT: [[REM2:%.*]] = urem i32 [[X]], 3
+; CHECK-NEXT: [[IS_ZERO2:%.*]] = icmp eq i32 [[REM2]], 0
+; CHECK-NEXT: call void @llvm.assume(i1 [[IS_ZERO2]])
+; CHECK-NEXT: [[QUOT:%.*]] = udiv i32 [[X]], 6
+; CHECK-NEXT: ret i32 [[QUOT]]
+;
+ %rem1 = urem i32 %x, 2
+ %is.zero1 = icmp eq i32 %rem1, 0
+ call void @llvm.assume(i1 %is.zero1)
+
+ %rem2 = urem i32 %x, 3
+ %is.zero2 = icmp eq i32 %rem2, 0
+ call void @llvm.assume(i1 %is.zero2)
+
+ %quot = udiv i32 %x, 6
+ ret i32 %quot
+}
+
+; If x is divisible by both 3 and 5, it must also be divisible by 15.
+define i32 @ret_udiv_exact_15_from_products(i32 %x) {
+; CHECK-LABEL: define i32 @ret_udiv_exact_15_from_products(
+; CHECK-SAME: i32 [[X:%.*]]) {
+; CHECK-NEXT: [[REM1:%.*]] = urem i32 [[X]], 3
+; CHECK-NEXT: [[IS_ZERO1:%.*]] = icmp eq i32 [[REM1]], 0
+; CHECK-NEXT: call void @llvm.assume(i1 [[IS_ZERO1]])
+; CHECK-NEXT: [[REM2:%.*]] = urem i32 [[X]], 5
+; CHECK-NEXT: [[IS_ZERO2:%.*]] = icmp eq i32 [[REM2]], 0
+; CHECK-NEXT: call void @llvm.assume(i1 [[IS_ZERO2]])
+; CHECK-NEXT: [[QUOT:%.*]] = udiv i32 [[X]], 15
+; CHECK-NEXT: ret i32 [[QUOT]]
+;
+ %rem1 = urem i32 %x, 3
+ %is.zero1 = icmp eq i32 %rem1, 0
+ call void @llvm.assume(i1 %is.zero1)
+
+ %rem2 = urem i32 %x, 5
+ %is.zero2 = icmp eq i32 %rem2, 0
+ call void @llvm.assume(i1 %is.zero2)
+
+ %quot = udiv i32 %x, 15
+ ret i32 %quot
+}
+
+; If x is divisible by 6, it must also be divisible by 3.
+define i32 @ret_udiv_exact_3_from_rem_6(i32 %x) {
+; CHECK-LABEL: define i32 @ret_udiv_exact_3_from_rem_6(
+; CHECK-SAME: i32 [[X:%.*]]) {
+; CHECK-NEXT: [[REM:%.*]] = urem i32 [[X]], 6
+; CHECK-NEXT: [[IS_ZERO:%.*]] = icmp eq i32 [[REM]], 0
+; CHECK-NEXT: call void @llvm.assume(i1 [[IS_ZERO]])
+; CHECK-NEXT: [[QUOT:%.*]] = udiv i32 [[X]], 3
+; CHECK-NEXT: ret i32 [[QUOT]]
+;
+ %rem = urem i32 %x, 6
+ %is.zero = icmp eq i32 %rem, 0
+ call void @llvm.assume(i1 %is.zero)
+ %quot = udiv i32 %x, 3
+ ret i32 %quot
+}
+
+; If x is divisible by 15, it must also be divisible by 3.
+define i32 @ret_udiv_exact_3_from_rem_15(i32 %x) {
+; CHECK-LABEL: define i32 @ret_udiv_exact_3_from_rem_15(
+; CHECK-SAME: i32 [[X:%.*]]) {
+; CHECK-NEXT: [[REM:%.*]] = urem i32 [[X]], 15
+; CHECK-NEXT: [[IS_ZERO:%.*]] = icmp eq i32 [[REM]], 0
+; CHECK-NEXT: call void @llvm.assume(i1 [[IS_ZERO]])
+; CHECK-NEXT: [[QUOT:%.*]] = udiv i32 [[X]], 3
+; CHECK-NEXT: ret i32 [[QUOT]]
+;
+ %rem = urem i32 %x, 15
+ %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_uintmax_from_zero_remainder(i32 %x) {
+; CHECK-LABEL: define i32 @ret_udiv_exact_uintmax_from_zero_remainder(
+; CHECK-SAME: i32 [[X:%.*]]) {
+; CHECK-NEXT: [[X_FR:%.*]] = freeze i32 [[X]]
+; CHECK-NEXT: [[TMP1:%.*]] = add i32 [[X_FR]], 1
+; CHECK-NEXT: [[IS_ZERO:%.*]] = icmp ult i32 [[TMP1]], 2
+; CHECK-NEXT: call void @llvm.assume(i1 [[IS_ZERO]])
+; CHECK-NEXT: [[TMP2:%.*]] = icmp eq i32 [[X_FR]], -1
+; CHECK-NEXT: [[QUOT:%.*]] = zext i1 [[TMP2]] to i32
+; CHECK-NEXT: ret i32 [[QUOT]]
+;
+ %rem = urem i32 %x, 4294967295
+ %is.zero = icmp eq i32 %rem, 0
+ call void @llvm.assume(i1 %is.zero)
+ %quot = udiv i32 %x, 4294967295
+ ret i32 %quot
+}
+
+; Signed division with a constant RHS.
+
+define i32 @ret_sdiv_exact_3_from_zero_remainder(i32 %x) {
+; CHECK-LABEL: define i32 @ret_sdiv_exact_3_from_zero_remainder(
+; CHECK-SAME: i32 [[X:%.*]]) {
+; CHECK-NEXT: [[REM:%.*]] = srem i32 [[X]], 3
+; CHECK-NEXT: [[IS_ZERO:%.*]] = icmp eq i32 [[REM]], 0
+; CHECK-NEXT: call void @llvm.assume(i1 [[IS_ZERO]])
+; CHECK-NEXT: [[QUOT:%.*]] = sdiv exact i32 [[X]], 3
+; CHECK-NEXT: ret i32 [[QUOT]]
+;
+ %rem = srem i32 %x, 3
+ %is.zero = icmp eq i32 %rem, 0
+ call void @llvm.assume(i1 %is.zero)
+ %quot = sdiv i32 %x, 3
+ ret i32 %quot
+}
+
+define i32 @ret_sdiv_exact_3_from_zero_remainder_unreachable(i32 %x) {
+; CHECK-LABEL: define i32 @ret_sdiv_exact_3_from_zero_remainder_unreachable(
+; CHECK-SAME: i32 [[X:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[REM:%.*]] = srem 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:%.*]] = sdiv exact i32 [[X]], 3
+; CHECK-NEXT: ret i32 [[QUOT]]
+;
+entry:
+ %rem = srem i32 %x, 3
+ %not.zero = icmp ne i32 %rem, 0
+ br i1 %not.zero, label %unreachable, label %div
+
+unreachable:
+ unreachable
+
+div:
+ %quot = sdiv i32 %x, 3
+ ret i32 %quot
+}
+
+; TODO: A zero remainder modulo -3 also makes division by -3 exact.
+define i32 @ret_sdiv_exact_neg_3_from_rem_neg_3_is_zero(i32 %x) {
+; CHECK-LABEL: define i32 @ret_sdiv_exact_neg_3_from_rem_neg_3_is_zero(
+; CHECK-SAME: i32 [[X:%.*]]) {
+; CHECK-NEXT: [[REM:%.*]] = srem i32 [[X]], 3
+; CHECK-NEXT: [[IS_ZERO:%.*]] = icmp eq i32 [[REM]], 0
+; CHECK-NEXT: call void @llvm.assume(i1 [[IS_ZERO]])
+; CHECK-NEXT: [[QUOT:%.*]] = sdiv i32 [[X]], -3
+; CHECK-NEXT: ret i32 [[QUOT]]
+;
+ %rem = srem i32 %x, -3
+ %is.zero = icmp eq i32 %rem, 0
+ call void @llvm.assume(i1 %is.zero)
+ %quot = sdiv i32 %x, -3
+ ret i32 %quot
+}
+
+define i32 @ret_sdiv_exact_pos_3_from_rem_neg_3_is_zero(i32 %x) {
+; CHECK-LABEL: define i32 @ret_sdiv_exact_pos_3_from_rem_neg_3_is_zero(
+; CHECK-SAME: i32 [[X:%.*]]) {
+; CHECK-NEXT: [[REM:%.*]] = srem i32 [[X]], 3
+; CHECK-NEXT: [[IS_ZERO:%.*]] = icmp eq i32 [[REM]], 0
+; CHECK-NEXT: call void @llvm.assume(i1 [[IS_ZERO]])
+; CHECK-NEXT: [[QUOT:%.*]] = sdiv exact i32 [[X]], 3
+; CHECK-NEXT: ret i32 [[QUOT]]
+;
+ %rem = srem i32 %x, -3
+ %is.zero = icmp eq i32 %rem, 0
+ call void @llvm.assume(i1 %is.zero)
+ %quot = sdiv i32 %x, 3
+ ret i32 %quot
+}
+
+; TODO: A zero remainder modulo +3 also makes division by -3 exact.
+define i32 @ret_sdiv_exact_neg_3_from_rem_pos_3_is_zero(i32 %x) {
+; CHECK-LABEL: define i32 @ret_sdiv_exact_neg_3_from_rem_pos_3_is_zero(
+; CHECK-SAME: i32 [[X:%.*]]) {
+; CHECK-NEXT: [[REM:%.*]] = srem i32 [[X]], 3
+; CHECK-NEXT: [[IS_ZERO:%.*]] = icmp eq i32 [[REM]], 0
+; CHECK-NEXT: call void @llvm.assume(i1 [[IS_ZERO]])
+; CHECK-NEXT: [[QUOT:%.*]] = sdiv i32 [[X]], -3
+; CHECK-NEXT: ret i32 [[QUOT]]
+;
+ %rem = srem i32 %x, 3
+ %is.zero = icmp eq i32 %rem, 0
+ call void @llvm.assume(i1 %is.zero)
+ %quot = sdiv i32 %x, -3
+ ret i32 %quot
+}
+
+define i32 @ret_sdiv_exact_intmax_from_zero_remainder(i32 %x) {
+; CHECK-LABEL: define i32 @ret_sdiv_exact_intmax_from_zero_remainder(
+; CHECK-SAME: i32 [[X:%.*]]) {
+; CHECK-NEXT: [[REM:%.*]] = srem i32 [[X]], 2147483647
+; CHECK-NEXT: [[IS_ZERO:%.*]] = icmp eq i32 [[REM]], 0
+; CHECK-NEXT: call void @llvm.assume(i1 [[IS_ZERO]])
+; CHECK-NEXT: [[QUOT:%.*]] = sdiv exact i32 [[X]], 2147483647
+; CHECK-NEXT: ret i32 [[QUOT]]
+;
+ %rem = srem i32 %x, 2147483647
+ %is.zero = icmp eq i32 %rem, 0
+ call void @llvm.assume(i1 %is.zero)
+ %quot = sdiv i32 %x, 2147483647
+ ret i32 %quot
+}
+
+define i32 @ret_sdiv_exact_pos_10000_from_zero_remainder(i32 %x) {
+; CHECK-LABEL: define i32 @ret_sdiv_exact_pos_10000_from_zero_remainder(
+; CHECK-SAME: i32 [[X:%.*]]) {
+; CHECK-NEXT: [[REM:%.*]] = srem i32 [[X]], 10000
+; CHECK-NEXT: [[IS_ZERO:%.*]] = icmp eq i32 [[REM]], 0
+; CHECK-NEXT: call void @llvm.assume(i1 [[IS_ZERO]])
+; CHECK-NEXT: [[QUOT:%.*]] = sdiv exact i32 [[X]], 10000
+; CHECK-NEXT: ret i32 [[QUOT]]
+;
+ %rem = srem i32 %x, 10000
+ %is.zero = icmp eq i32 %rem, 0
+ call void @llvm.assume(i1 %is.zero)
+ %quot = sdiv i32 %x, 10000
+ ret i32 %quot
+}
+
+; TODO: A zero remainder modulo -10000 also makes division by -10000 exact.
+define i32 @ret_sdiv_exact_neg_10000_from_zero_remainder(i32 %x) {
+; CHECK-LABEL: define i32 @ret_sdiv_exact_neg_10000_from_zero_remainder(
+; CHECK-SAME: i32 [[X:%.*]]) {
+; CHECK-NEXT: [[REM:%.*]] = srem i32 [[X]], 10000
+; CHECK-NEXT: [[IS_ZERO:%.*]] = icmp eq i32 [[REM]], 0
+; CHECK-NEXT: call void @llvm.assume(i1 [[IS_ZERO]])
+; CHECK-NEXT: [[QUOT:%.*]] = sdiv i32 [[X]], -10000
+; CHECK-NEXT: ret i32 [[QUOT]]
+;
+ %rem = srem i32 %x, -10000
+ %is.zero = icmp eq i32 %rem, 0
+ call void @llvm.assume(i1 %is.zero)
+ %quot = sdiv i32 %x, -10000
+ ret i32 %quot
+}
diff --git a/llvm/test/Transforms/InstCombine/div-exact-from-rem.ll b/llvm/test/Transforms/InstCombine/div-exact-from-rem.ll
new file mode 100644
index 0000000000000..f9913048d34e9
--- /dev/null
+++ b/llvm/test/Transforms/InstCombine/div-exact-from-rem.ll
@@ -0,0 +1,695 @@
+; 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)
+declare i32 @llvm.abs.i32(i32, i1 immarg)
+declare i1 @func_bool()
+declare void @func1()
+declare void @func2()
+
+define i32 @udiv_exact_assume(i32 %x, i32 %y) {
+; CHECK-LABEL: define i32 @udiv_exact_assume(
+; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]]) {
+; CHECK-NEXT: [[REM:%.*]] = urem i32 [[X]], [[Y]]
+; 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]], [[Y]]
+; CHECK-NEXT: ret i32 [[QUOT]]
+;
+ %rem = urem i32 %x, %y
+ %is.zero = icmp eq i32 %rem, 0
+ call void @llvm.assume(i1 %is.zero)
+ %quot = udiv i32 %x, %y
+ ret i32 %quot
+}
+
+define i32 @sdiv_exact_assume(i32 %x, i32 %y) {
+; CHECK-LABEL: define i32 @sdiv_exact_assume(
+; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]]) {
+; CHECK-NEXT: [[REM:%.*]] = srem i32 [[X]], [[Y]]
+; CHECK-NEXT: [[IS_ZERO:%.*]] = icmp eq i32 [[REM]], 0
+; CHECK-NEXT: call void @llvm.assume(i1 [[IS_ZERO]])
+; CHECK-NEXT: [[QUOT:%.*]] = sdiv exact i32 [[X]], [[Y]]
+; CHECK-NEXT: ret i32 [[QUOT]]
+;
+ %rem = srem i32 %x, %y
+ %is.zero = icmp eq i32 %rem, 0
+ call void @llvm.assume(i1 %is.zero)
+ %quot = sdiv i32 %x, %y
+ ret i32 %quot
+}
+
+; Self-division folds to one without needing to infer exact.
+define i32 @udiv_exact_assume_self(i32 %x) {
+; CHECK-LABEL: define i32 @udiv_exact_assume_self(
+; CHECK-SAME: i32 [[X:%.*]]) {
+; CHECK-NEXT: ret i32 1
+;
+ %rem = urem i32 %x, %x
+ %is.zero = icmp eq i32 %rem, 0
+ call void @llvm.assume(i1 %is.zero)
+ %quot = udiv i32 %x, %x
+ ret i32 %quot
+}
+
+; TODO: The following unconditional assumption makes the earlier division exact.
+define i32 @udiv_exact_assume_after_division(i32 %x, i32 %y) {
+; CHECK-LABEL: define i32 @udiv_exact_assume_after_division(
+; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]]) {
+; CHECK-NEXT: [[QUOT:%.*]] = udiv i32 [[X]], [[Y]]
+; CHECK-NEXT: [[REM:%.*]] = urem i32 [[X]], [[Y]]
+; CHECK-NEXT: [[IS_ZERO:%.*]] = icmp eq i32 [[REM]], 0
+; CHECK-NEXT: call void @llvm.assume(i1 [[IS_ZERO]])
+; CHECK-NEXT: ret i32 [[QUOT]]
+;
+ %quot = udiv i32 %x, %y
+ %rem = urem i32 %x, %y
+ %is.zero = icmp eq i32 %rem, 0
+ call void @llvm.assume(i1 %is.zero)
+ ret i32 %quot
+}
+
+; TODO: Sinking revisits the division before the dominating condition is added
+; to the cache, so inferring exact requires another InstCombine iteration.
+define i32 @udiv_exact_assume_after_division_unreachable(i32 %x, i32 %y) "instcombine-no-verify-fixpoint" {
+; CHECK-LABEL: define i32 @udiv_exact_assume_after_division_unreachable(
+; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]]) #[[ATTR2:[0-9]+]] {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[REM:%.*]] = urem i32 [[X]], [[Y]]
+; CHECK-NEXT: [[NOT_ZERO_NOT:%.*]] = icmp eq i32 [[REM]], 0
+; CHECK-NEXT: br i1 [[NOT_ZERO_NOT]], label %[[EXIT:.*]], label %[[UNREACHABLE:.*]]
+; CHECK: [[UNREACHABLE]]:
+; CHECK-NEXT: unreachable
+; CHECK: [[EXIT]]:
+; CHECK-NEXT: [[QUOT:%.*]] = udiv i32 [[X]], [[Y]]
+; CHECK-NEXT: ret i32 [[QUOT]]
+;
+entry:
+ %quot = udiv i32 %x, %y
+ %rem = urem i32 %x, %y
+ %not.zero = icmp ne i32 %rem, 0
+ br i1 %not.zero, label %unreachable, label %exit
+
+unreachable:
+ unreachable
+
+exit:
+ ret i32 %quot
+}
+
+; Only the checked path requires a zero remainder, so the shared division
+; must not be marked exact.
+define i32 @udiv_before_remainder_comparison_on_one_path(i32 %x, i32 %y, i1 %cond) {
+; CHECK-LABEL: define i32 @udiv_before_remainder_comparison_on_one_path(
+; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]], i1 [[COND:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[QUOT:%.*]] = udiv i32 [[X]], [[Y]]
+; CHECK-NEXT: br i1 [[COND]], label %[[CHECK:.*]], label %[[UNCHECKED:.*]]
+; CHECK: [[CHECK]]:
+; CHECK-NEXT: [[REM:%.*]] = urem i32 [[X]], [[Y]]
+; CHECK-NEXT: [[NOT_ZERO_NOT:%.*]] = icmp eq i32 [[REM]], 0
+; CHECK-NEXT: br i1 [[NOT_ZERO_NOT]], label %[[CHECKED:.*]], label %[[UNREACHABLE:.*]]
+; CHECK: [[UNREACHABLE]]:
+; CHECK-NEXT: unreachable
+; CHECK: [[CHECKED]]:
+; CHECK-NEXT: call void @func1()
+; CHECK-NEXT: ret i32 [[QUOT]]
+; CHECK: [[UNCHECKED]]:
+; CHECK-NEXT: call void @func2()
+; CHECK-NEXT: ret i32 [[QUOT]]
+;
+entry:
+ %quot = udiv i32 %x, %y
+ %rem = urem i32 %x, %y
+ br i1 %cond, label %check, label %unchecked
+
+check:
+ %not.zero = icmp ne i32 %rem, 0
+ br i1 %not.zero, label %unreachable, label %checked
+
+unreachable:
+ unreachable
+
+checked:
+ call void @func1()
+ ret i32 %quot
+
+unchecked:
+ call void @func2()
+ ret i32 %quot
+}
+
+; Only the checked path requires a zero remainder, so the shared division
+; must not be marked exact.
+define i32 @udiv_before_remainder_on_one_path(i32 %x, i32 %y, i1 %cond) {
+; CHECK-LABEL: define i32 @udiv_before_remainder_on_one_path(
+; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]], i1 [[COND:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[QUOT:%.*]] = udiv i32 [[X]], [[Y]]
+; CHECK-NEXT: br i1 [[COND]], label %[[CHECK:.*]], label %[[UNCHECKED:.*]]
+; CHECK: [[CHECK]]:
+; CHECK-NEXT: [[REM:%.*]] = urem i32 [[X]], [[Y]]
+; CHECK-NEXT: [[NOT_ZERO_NOT:%.*]] = icmp eq i32 [[REM]], 0
+; CHECK-NEXT: br i1 [[NOT_ZERO_NOT]], label %[[CHECKED:.*]], label %[[UNREACHABLE:.*]]
+; CHECK: [[UNREACHABLE]]:
+; CHECK-NEXT: unreachable
+; CHECK: [[CHECKED]]:
+; CHECK-NEXT: call void @func1()
+; CHECK-NEXT: ret i32 [[QUOT]]
+; CHECK: [[UNCHECKED]]:
+; CHECK-NEXT: call void @func2()
+; CHECK-NEXT: ret i32 [[QUOT]]
+;
+entry:
+ %quot = udiv i32 %x, %y
+ br i1 %cond, label %check, label %unchecked
+
+check:
+ %rem = urem i32 %x, %y
+ %not.zero = icmp ne i32 %rem, 0
+ br i1 %not.zero, label %unreachable, label %checked
+
+unreachable:
+ unreachable
+
+checked:
+ call void @func1()
+ ret i32 %quot
+
+unchecked:
+ call void @func2()
+ ret i32 %quot
+}
+
+; One path increments the quotient, the other leaves it unchanged.
+; TODO: Use the shared exit check to mark the earlier division exact.
+define i32 @udiv_exact_check_on_shared_exit(i32 %x, i32 %y, i1 %cond) {
+; CHECK-LABEL: define i32 @udiv_exact_check_on_shared_exit(
+; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]], i1 [[COND:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*]]:
+; CHECK-NEXT: [[QUOT:%.*]] = udiv i32 [[X]], [[Y]]
+; CHECK-NEXT: br i1 [[COND]], label %[[MODIFY:.*]], label %[[EXIT:.*]]
+; CHECK: [[MODIFY]]:
+; CHECK-NEXT: [[MODIFY_RESULT:%.*]] = add nuw nsw i32 [[QUOT]], 1
+; CHECK-NEXT: br label %[[EXIT]]
+; CHECK: [[EXIT]]:
+; CHECK-NEXT: [[RESULT:%.*]] = phi i32 [ [[MODIFY_RESULT]], %[[MODIFY]] ], [ [[QUOT]], %[[ENTRY]] ]
+; CHECK-NEXT: [[REM:%.*]] = urem i32 [[X]], [[Y]]
+; CHECK-NEXT: [[IS_ZERO:%.*]] = icmp eq i32 [[REM]], 0
+; CHECK-NEXT: call void @llvm.assume(i1 [[IS_ZERO]])
+; CHECK-NEXT: ret i32 [[RESULT]]
+;
+entry:
+ %rem = urem i32 %x, %y
+ %quot = udiv i32 %x, %y
+ br i1 %cond, label %modify, label %exit
+
+modify:
+ %modify.result = add nuw nsw i32 %quot, 1
+ br label %exit
+
+exit:
+ %result = phi i32 [ %modify.result, %modify ], [ %quot, %entry ]
+ %is.zero = icmp eq i32 %rem, 0
+ call void @llvm.assume(i1 %is.zero)
+ ret i32 %result
+}
+
+; One path increments the quotient, the other leaves it unchanged.
+; TODO: Use the shared exit check to mark the earlier division exact.
+define i32 @udiv_exact_check_on_shared_exit_unreachable(i32 %x, i32 %y, i1 %cond) {
+; CHECK-LABEL: define i32 @udiv_exact_check_on_shared_exit_unreachable(
+; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]], i1 [[COND:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*]]:
+; CHECK-NEXT: [[REM:%.*]] = urem i32 [[X]], [[Y]]
+; CHECK-NEXT: [[QUOT:%.*]] = udiv i32 [[X]], [[Y]]
+; CHECK-NEXT: br i1 [[COND]], label %[[MODIFY:.*]], label %[[EXIT:.*]]
+; CHECK: [[MODIFY]]:
+; CHECK-NEXT: [[MODIFY_RESULT:%.*]] = add nuw nsw i32 [[QUOT]], 1
+; CHECK-NEXT: br label %[[EXIT]]
+; CHECK: [[EXIT]]:
+; CHECK-NEXT: [[RESULT:%.*]] = phi i32 [ [[MODIFY_RESULT]], %[[MODIFY]] ], [ [[QUOT]], %[[ENTRY]] ]
+; CHECK-NEXT: [[NOT_ZERO_NOT:%.*]] = icmp eq i32 [[REM]], 0
+; CHECK-NEXT: br i1 [[NOT_ZERO_NOT]], label %[[RETURN:.*]], label %[[UNREACHABLE:.*]]
+; CHECK: [[UNREACHABLE]]:
+; CHECK-NEXT: unreachable
+; CHECK: [[RETURN]]:
+; CHECK-NEXT: ret i32 [[RESULT]]
+;
+entry:
+ %rem = urem i32 %x, %y
+ %quot = udiv i32 %x, %y
+ br i1 %cond, label %modify, label %exit
+
+modify:
+ %modify.result = add nuw nsw i32 %quot, 1
+ br label %exit
+
+exit:
+ %result = phi i32 [ %modify.result, %modify ], [ %quot, %entry ]
+ %not.zero = icmp ne i32 %rem, 0
+ br i1 %not.zero, label %unreachable, label %return
+
+unreachable:
+ unreachable
+
+return:
+ ret i32 %result
+}
+
+; TODO: A zero signed remainder also makes division by abs(y) exact.
+define i32 @sdiv_exact_abs_denominator(i32 %x, i32 %y) {
+; CHECK-LABEL: define i32 @sdiv_exact_abs_denominator(
+; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]]) {
+; CHECK-NEXT: [[REM:%.*]] = srem i32 [[X]], [[Y]]
+; CHECK-NEXT: [[IS_ZERO:%.*]] = icmp eq i32 [[REM]], 0
+; CHECK-NEXT: call void @llvm.assume(i1 [[IS_ZERO]])
+; CHECK-NEXT: [[ABS_Y:%.*]] = call i32 @llvm.abs.i32(i32 [[Y]], i1 true)
+; CHECK-NEXT: [[QUOT:%.*]] = sdiv i32 [[X]], [[ABS_Y]]
+; CHECK-NEXT: ret i32 [[QUOT]]
+;
+ %rem = srem i32 %x, %y
+ %is.zero = icmp eq i32 %rem, 0
+ call void @llvm.assume(i1 %is.zero)
+ %abs.y = call i32 @llvm.abs.i32(i32 %y, i1 true)
+ %quot = sdiv i32 %x, %abs.y
+ ret i32 %quot
+}
+
+; TODO: A zero signed remainder also makes division by abs(y) exact.
+define i32 @sdiv_exact_abs_denominator_unreachable(i32 %x, i32 %y) {
+; CHECK-LABEL: define i32 @sdiv_exact_abs_denominator_unreachable(
+; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[REM:%.*]] = srem i32 [[X]], [[Y]]
+; 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: [[ABS_Y:%.*]] = call i32 @llvm.abs.i32(i32 [[Y]], i1 true)
+; CHECK-NEXT: [[QUOT:%.*]] = sdiv i32 [[X]], [[ABS_Y]]
+; CHECK-NEXT: ret i32 [[QUOT]]
+;
+entry:
+ %rem = srem i32 %x, %y
+ %not.zero = icmp ne i32 %rem, 0
+ br i1 %not.zero, label %unreachable, label %div
+
+unreachable:
+ unreachable
+
+div:
+ %abs.y = call i32 @llvm.abs.i32(i32 %y, i1 true)
+ %quot = sdiv i32 %x, %abs.y
+ ret i32 %quot
+}
+
+define i32 @udiv_from_srem(i32 %x, i32 %y) {
+; CHECK-LABEL: define i32 @udiv_from_srem(
+; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]]) {
+; CHECK-NEXT: [[REM:%.*]] = srem i32 [[X]], [[Y]]
+; CHECK-NEXT: [[IS_ZERO:%.*]] = icmp eq i32 [[REM]], 0
+; CHECK-NEXT: call void @llvm.assume(i1 [[IS_ZERO]])
+; CHECK-NEXT: [[QUOT:%.*]] = udiv i32 [[X]], [[Y]]
+; CHECK-NEXT: ret i32 [[QUOT]]
+;
+ %rem = srem i32 %x, %y
+ %is.zero = icmp eq i32 %rem, 0
+ call void @llvm.assume(i1 %is.zero)
+ %quot = udiv i32 %x, %y
+ ret i32 %quot
+}
+
+define i32 @udiv_exact_from_srem_nonnegative(i32 %x, i32 %y) {
+; CHECK-LABEL: define i32 @udiv_exact_from_srem_nonnegative(
+; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]]) {
+; CHECK-NEXT: [[NUMERATOR:%.*]] = and i32 [[X]], 2147483647
+; CHECK-NEXT: [[DENOMINATOR:%.*]] = and i32 [[Y]], 2147483647
+; CHECK-NEXT: [[REM:%.*]] = urem i32 [[NUMERATOR]], [[DENOMINATOR]]
+; CHECK-NEXT: [[IS_ZERO:%.*]] = icmp eq i32 [[REM]], 0
+; CHECK-NEXT: call void @llvm.assume(i1 [[IS_ZERO]])
+; CHECK-NEXT: [[QUOT:%.*]] = udiv exact i32 [[NUMERATOR]], [[DENOMINATOR]]
+; CHECK-NEXT: ret i32 [[QUOT]]
+;
+ %numerator = and i32 %x, 2147483647
+ %denominator = and i32 %y, 2147483647
+ %rem = srem i32 %numerator, %denominator
+ %is.zero = icmp eq i32 %rem, 0
+ call void @llvm.assume(i1 %is.zero)
+ %quot = udiv i32 %numerator, %denominator
+ ret i32 %quot
+}
+
+define i32 @sdiv_from_urem(i32 %x, i32 %y) {
+; CHECK-LABEL: define i32 @sdiv_from_urem(
+; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]]) {
+; CHECK-NEXT: [[REM:%.*]] = urem i32 [[X]], [[Y]]
+; CHECK-NEXT: [[IS_ZERO:%.*]] = icmp eq i32 [[REM]], 0
+; CHECK-NEXT: call void @llvm.assume(i1 [[IS_ZERO]])
+; CHECK-NEXT: [[QUOT:%.*]] = sdiv i32 [[X]], [[Y]]
+; CHECK-NEXT: ret i32 [[QUOT]]
+;
+ %rem = urem i32 %x, %y
+ %is.zero = icmp eq i32 %rem, 0
+ call void @llvm.assume(i1 %is.zero)
+ %quot = sdiv i32 %x, %y
+ ret i32 %quot
+}
+
+define i32 @sdiv_exact_from_urem_nonnegative(i32 %x, i32 %y) {
+; CHECK-LABEL: define i32 @sdiv_exact_from_urem_nonnegative(
+; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]]) {
+; CHECK-NEXT: [[NUMERATOR:%.*]] = and i32 [[X]], 2147483647
+; CHECK-NEXT: [[DENOMINATOR:%.*]] = and i32 [[Y]], 2147483647
+; CHECK-NEXT: [[REM:%.*]] = urem i32 [[NUMERATOR]], [[DENOMINATOR]]
+; CHECK-NEXT: [[IS_ZERO:%.*]] = icmp eq i32 [[REM]], 0
+; CHECK-NEXT: call void @llvm.assume(i1 [[IS_ZERO]])
+; CHECK-NEXT: [[QUOT:%.*]] = udiv exact i32 [[NUMERATOR]], [[DENOMINATOR]]
+; CHECK-NEXT: ret i32 [[QUOT]]
+;
+ %numerator = and i32 %x, 2147483647
+ %denominator = and i32 %y, 2147483647
+ %rem = urem i32 %numerator, %denominator
+ %is.zero = icmp eq i32 %rem, 0
+ call void @llvm.assume(i1 %is.zero)
+ %quot = sdiv i32 %numerator, %denominator
+ ret i32 %quot
+}
+
+define i32 @udiv_exact_known_zero_remainder(i32 %x, i32 %y) {
+; CHECK-LABEL: define i32 @udiv_exact_known_zero_remainder(
+; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[REM:%.*]] = urem i32 [[X]], [[Y]]
+; CHECK-NEXT: [[IS_ZERO:%.*]] = icmp eq i32 [[REM]], 0
+; CHECK-NEXT: br i1 [[IS_ZERO]], label %[[DIV:.*]], label %[[EXIT:.*]]
+; CHECK: [[DIV]]:
+; CHECK-NEXT: [[QUOT:%.*]] = udiv exact i32 [[X]], [[Y]]
+; CHECK-NEXT: ret i32 [[QUOT]]
+; CHECK: [[EXIT]]:
+; CHECK-NEXT: ret i32 0
+;
+entry:
+ %rem = urem i32 %x, %y
+ %is.zero = icmp eq i32 %rem, 0
+ br i1 %is.zero, label %div, label %exit
+
+div:
+ %quot = udiv i32 %x, %y
+ ret i32 %quot
+
+exit:
+ ret i32 0
+}
+
+define i32 @sdiv_exact_known_zero_remainder(i32 %x, i32 %y) {
+; CHECK-LABEL: define i32 @sdiv_exact_known_zero_remainder(
+; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[REM:%.*]] = srem i32 [[X]], [[Y]]
+; CHECK-NEXT: [[IS_ZERO:%.*]] = icmp eq i32 [[REM]], 0
+; CHECK-NEXT: br i1 [[IS_ZERO]], label %[[DIV:.*]], label %[[EXIT:.*]]
+; CHECK: [[DIV]]:
+; CHECK-NEXT: [[QUOT:%.*]] = sdiv exact i32 [[X]], [[Y]]
+; CHECK-NEXT: ret i32 [[QUOT]]
+; CHECK: [[EXIT]]:
+; CHECK-NEXT: ret i32 0
+;
+entry:
+ %rem = srem i32 %x, %y
+ %is.zero = icmp eq i32 %rem, 0
+ br i1 %is.zero, label %div, label %exit
+
+div:
+ %quot = sdiv i32 %x, %y
+ ret i32 %quot
+
+exit:
+ ret i32 0
+}
+
+define i32 @udiv_exact_known_zero_remainder_extra_branch(i32 %x, i32 %y) {
+; CHECK-LABEL: define i32 @udiv_exact_known_zero_remainder_extra_branch(
+; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[REM:%.*]] = urem i32 [[X]], [[Y]]
+; CHECK-NEXT: [[IS_ZERO:%.*]] = icmp eq i32 [[REM]], 0
+; CHECK-NEXT: br i1 [[IS_ZERO]], label %[[MIDDLE:.*]], label %[[EXIT:.*]]
+; CHECK: [[MIDDLE]]:
+; CHECK-NEXT: [[COND:%.*]] = call i1 @func_bool()
+; CHECK-NEXT: br i1 [[COND]], label %[[PATH1:.*]], label %[[PATH2:.*]]
+; CHECK: [[PATH1]]:
+; CHECK-NEXT: call void @func1()
+; CHECK-NEXT: br label %[[DIV:.*]]
+; CHECK: [[PATH2]]:
+; CHECK-NEXT: call void @func2()
+; CHECK-NEXT: br label %[[DIV]]
+; CHECK: [[DIV]]:
+; CHECK-NEXT: [[QUOT:%.*]] = udiv exact i32 [[X]], [[Y]]
+; CHECK-NEXT: ret i32 [[QUOT]]
+; CHECK: [[EXIT]]:
+; CHECK-NEXT: ret i32 0
+;
+entry:
+ %rem = urem i32 %x, %y
+ %is.zero = icmp eq i32 %rem, 0
+ br i1 %is.zero, label %middle, label %exit
+
+middle:
+ %cond = call i1 @func_bool()
+ br i1 %cond, label %path1, label %path2
+
+path1:
+ call void @func1()
+ br label %div
+
+path2:
+ call void @func2()
+ br label %div
+
+div:
+ %quot = udiv i32 %x, %y
+ ret i32 %quot
+
+exit:
+ ret i32 0
+}
+
+define i32 @udiv_exact_unreachable(i32 %x, i32 %y) {
+; CHECK-LABEL: define i32 @udiv_exact_unreachable(
+; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[REM:%.*]] = urem i32 [[X]], [[Y]]
+; CHECK-NEXT: [[NOT_ZERO_NOT:%.*]] = icmp eq i32 [[REM]], 0
+; CHECK-NEXT: br i1 [[NOT_ZERO_NOT]], label %[[UNREACHABLE:.*]], label %[[DIV:.*]]
+; CHECK: [[UNREACHABLE]]:
+; CHECK-NEXT: unreachable
+; CHECK: [[DIV]]:
+; CHECK-NEXT: [[QUOT:%.*]] = udiv i32 [[X]], [[Y]]
+; CHECK-NEXT: ret i32 [[QUOT]]
+;
+entry:
+ %rem = urem i32 %x, %y
+ %not.zero = icmp ne i32 %rem, 0
+ br i1 %not.zero, label %div, label %unreachable
+
+unreachable:
+ unreachable
+
+div:
+ %quot = udiv i32 %x, %y
+ ret i32 %quot
+}
+
+define i32 @udiv_exact_inverted_unreachable(i32 %x, i32 %y) {
+; CHECK-LABEL: define i32 @udiv_exact_inverted_unreachable(
+; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[REM:%.*]] = urem i32 [[X]], [[Y]]
+; 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]], [[Y]]
+; CHECK-NEXT: ret i32 [[QUOT]]
+;
+entry:
+ %rem = urem i32 %x, %y
+ %not.zero = icmp ne i32 %rem, 0
+ br i1 %not.zero, label %unreachable, label %div
+
+unreachable:
+ unreachable
+
+div:
+ %quot = udiv i32 %x, %y
+ ret i32 %quot
+}
+
+define i32 @udiv_exact_condition_on_other_path(i32 %x, i32 %y) {
+; CHECK-LABEL: define i32 @udiv_exact_condition_on_other_path(
+; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[REM:%.*]] = urem i32 [[X]], [[Y]]
+; CHECK-NEXT: [[IS_ZERO:%.*]] = icmp eq i32 [[REM]], 0
+; CHECK-NEXT: br i1 [[IS_ZERO]], label %[[ZERO:.*]], label %[[NONZERO:.*]]
+; CHECK: [[ZERO]]:
+; CHECK-NEXT: call void @func1()
+; CHECK-NEXT: [[ZERO_QUOT:%.*]] = udiv exact i32 [[X]], [[Y]]
+; CHECK-NEXT: ret i32 [[ZERO_QUOT]]
+; CHECK: [[NONZERO]]:
+; CHECK-NEXT: call void @func2()
+; CHECK-NEXT: [[NONZERO_QUOT:%.*]] = udiv i32 [[X]], [[Y]]
+; CHECK-NEXT: ret i32 [[NONZERO_QUOT]]
+;
+entry:
+ %rem = urem i32 %x, %y
+ %is.zero = icmp eq i32 %rem, 0
+ br i1 %is.zero, label %zero, label %nonzero
+
+zero:
+ call void @func1()
+ %zero.quot = udiv i32 %x, %y
+ ret i32 %zero.quot
+
+nonzero:
+ call void @func2()
+ %nonzero.quot = udiv i32 %x, %y
+ ret i32 %nonzero.quot
+}
+
+define i32 @udiv_exact_different_numerator(i32 %x, i32 %y, i32 %z) {
+; CHECK-LABEL: define i32 @udiv_exact_different_numerator(
+; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]], i32 [[Z:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[REM:%.*]] = urem i32 [[Z]], [[Y]]
+; CHECK-NEXT: [[IS_ZERO:%.*]] = icmp eq i32 [[REM]], 0
+; CHECK-NEXT: br i1 [[IS_ZERO]], label %[[DIV:.*]], label %[[EXIT:.*]]
+; CHECK: [[DIV]]:
+; CHECK-NEXT: [[QUOT:%.*]] = udiv i32 [[X]], [[Y]]
+; CHECK-NEXT: ret i32 [[QUOT]]
+; CHECK: [[EXIT]]:
+; CHECK-NEXT: ret i32 0
+;
+entry:
+ %rem = urem i32 %z, %y
+ %is.zero = icmp eq i32 %rem, 0
+ br i1 %is.zero, label %div, label %exit
+
+div:
+ %quot = udiv i32 %x, %y
+ ret i32 %quot
+
+exit:
+ ret i32 0
+}
+
+define i32 @udiv_exact_different_denominator(i32 %x, i32 %y, i32 %z) {
+; CHECK-LABEL: define i32 @udiv_exact_different_denominator(
+; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]], i32 [[Z:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[REM:%.*]] = urem i32 [[X]], [[Z]]
+; CHECK-NEXT: [[IS_ZERO:%.*]] = icmp eq i32 [[REM]], 0
+; CHECK-NEXT: br i1 [[IS_ZERO]], label %[[DIV:.*]], label %[[EXIT:.*]]
+; CHECK: [[DIV]]:
+; CHECK-NEXT: [[QUOT:%.*]] = udiv i32 [[X]], [[Y]]
+; CHECK-NEXT: ret i32 [[QUOT]]
+; CHECK: [[EXIT]]:
+; CHECK-NEXT: ret i32 0
+;
+entry:
+ %rem = urem i32 %x, %z
+ %is.zero = icmp eq i32 %rem, 0
+ br i1 %is.zero, label %div, label %exit
+
+div:
+ %quot = udiv i32 %x, %y
+ ret i32 %quot
+
+exit:
+ ret i32 0
+}
+
+; The zero-remainder assumption does not hold on every path to the division.
+define i32 @udiv_exact_assume_on_one_incoming_path(i32 %x, i32 %y, i1 %cond) {
+; CHECK-LABEL: define i32 @udiv_exact_assume_on_one_incoming_path(
+; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]], i1 [[COND:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: br i1 [[COND]], label %[[ASSUMED:.*]], label %[[OTHER:.*]]
+; CHECK: [[ASSUMED]]:
+; CHECK-NEXT: [[REM:%.*]] = urem i32 [[X]], [[Y]]
+; CHECK-NEXT: [[IS_ZERO:%.*]] = icmp eq i32 [[REM]], 0
+; CHECK-NEXT: call void @llvm.assume(i1 [[IS_ZERO]])
+; CHECK-NEXT: call void @func1()
+; CHECK-NEXT: br label %[[DIV:.*]]
+; CHECK: [[OTHER]]:
+; CHECK-NEXT: call void @func2()
+; CHECK-NEXT: br label %[[DIV]]
+; CHECK: [[DIV]]:
+; CHECK-NEXT: [[QUOT:%.*]] = udiv i32 [[X]], [[Y]]
+; CHECK-NEXT: ret i32 [[QUOT]]
+;
+entry:
+ %rem = urem i32 %x, %y
+ br i1 %cond, label %assumed, label %other
+
+assumed:
+ %is.zero = icmp eq i32 %rem, 0
+ call void @llvm.assume(i1 %is.zero)
+ call void @func1()
+ br label %div
+
+other:
+ call void @func2()
+ br label %div
+
+div:
+ %quot = udiv i32 %x, %y
+ ret i32 %quot
+}
+
+; A zero remainder must be true in all lanes for the division to be exact.
+define <2 x i32> @udiv_exact_vector_one_lane_zero_remainder(<2 x i32> %x, <2 x i32> %y) {
+; CHECK-LABEL: define <2 x i32> @udiv_exact_vector_one_lane_zero_remainder(
+; CHECK-SAME: <2 x i32> [[X:%.*]], <2 x i32> [[Y:%.*]]) {
+; CHECK-NEXT: [[REM:%.*]] = urem <2 x i32> [[X]], [[Y]]
+; CHECK-NEXT: [[LANE0:%.*]] = extractelement <2 x i32> [[REM]], i64 0
+; CHECK-NEXT: [[IS_ZERO:%.*]] = icmp eq i32 [[LANE0]], 0
+; CHECK-NEXT: call void @llvm.assume(i1 [[IS_ZERO]])
+; CHECK-NEXT: [[QUOT:%.*]] = udiv <2 x i32> [[X]], [[Y]]
+; CHECK-NEXT: ret <2 x i32> [[QUOT]]
+;
+ %rem = urem <2 x i32> %x, %y
+ %lane0 = extractelement <2 x i32> %rem, i32 0
+ %is.zero = icmp eq i32 %lane0, 0
+ call void @llvm.assume(i1 %is.zero)
+ %quot = udiv <2 x i32> %x, %y
+ ret <2 x i32> %quot
+}
+
+; TODO: Zero remainders in all lanes make the vector division exact.
+define <2 x i32> @udiv_exact_vector_all_lanes_zero_remainder(<2 x i32> %x, <2 x i32> %y) {
+; CHECK-LABEL: define <2 x i32> @udiv_exact_vector_all_lanes_zero_remainder(
+; CHECK-SAME: <2 x i32> [[X:%.*]], <2 x i32> [[Y:%.*]]) {
+; CHECK-NEXT: [[REM:%.*]] = urem <2 x i32> [[X]], [[Y]]
+; CHECK-NEXT: [[LANE0:%.*]] = extractelement <2 x i32> [[REM]], i64 0
+; CHECK-NEXT: [[IS_ZERO0:%.*]] = icmp eq i32 [[LANE0]], 0
+; CHECK-NEXT: call void @llvm.assume(i1 [[IS_ZERO0]])
+; CHECK-NEXT: [[LANE1:%.*]] = extractelement <2 x i32> [[REM]], i64 1
+; CHECK-NEXT: [[IS_ZERO1:%.*]] = icmp eq i32 [[LANE1]], 0
+; CHECK-NEXT: call void @llvm.assume(i1 [[IS_ZERO1]])
+; CHECK-NEXT: [[QUOT:%.*]] = udiv <2 x i32> [[X]], [[Y]]
+; CHECK-NEXT: ret <2 x i32> [[QUOT]]
+;
+ %rem = urem <2 x i32> %x, %y
+ %lane0 = extractelement <2 x i32> %rem, i32 0
+ %is.zero0 = icmp eq i32 %lane0, 0
+ call void @llvm.assume(i1 %is.zero0)
+ %lane1 = extractelement <2 x i32> %rem, i32 1
+ %is.zero1 = icmp eq i32 %lane1, 0
+ call void @llvm.assume(i1 %is.zero1)
+ %quot = udiv <2 x i32> %x, %y
+ ret <2 x i32> %quot
+}
More information about the llvm-commits
mailing list