[llvm] [InstCombine] Infer exact division from a known-zero remainder (PR #221509)

via llvm-commits llvm-commits at lists.llvm.org
Sat Sep 5 19:50:12 PDT 2026


https://github.com/ZERICO2005 updated https://github.com/llvm/llvm-project/pull/221509

>From 280f129ec3ed64b2f9a539b466b7c008f5cdc8c7 Mon Sep 17 00:00:00 2001
From: zerico <zerico2005 at gmail.com>
Date: Sat, 5 Sep 2026 19:04:03 -0600
Subject: [PATCH] [InstCombine] Infer exact division from a known-zero
 remainder

---
 .../InstCombine/InstCombineMulDivRem.cpp      |  43 ++
 .../InstCombine/InstructionCombining.cpp      |   5 +
 .../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        | 258 +++++++
 .../InstCombine/div-exact-from-rem.ll         | 682 ++++++++++++++++++
 llvm/test/Transforms/InstCombine/icmp.ll      |  18 +-
 .../InstCombine/sink_instruction.ll           |  19 +-
 9 files changed, 1390 insertions(+), 25 deletions(-)
 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/lib/Transforms/InstCombine/InstructionCombining.cpp b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
index b877f79a000cf..b6e63d7df46a4 100644
--- a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
@@ -6156,6 +6156,11 @@ bool InstCombinerImpl::prepareWorklist(Function &F) {
       continue;
     }
 
+    // Sinking may visit an instruction before the branches that dominate its
+    // new location. Make their conditions available before processing starts.
+    if (auto *BI = dyn_cast<CondBrInst>(Inst))
+      DC.registerBranch(BI);
+
     Worklist.push(Inst);
   }
 
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..ab8dd69f5b1ad
--- /dev/null
+++ b/llvm/test/Transforms/InstCombine/div-exact-from-rem-constant-rhs.ll
@@ -0,0 +1,258 @@
+; 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
+}
+
+; 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_6_from_factors(i32 %x) {
+; CHECK-LABEL: define i32 @ret_udiv_exact_6_from_factors(
+; 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_factors(i32 %x) {
+; CHECK-LABEL: define i32 @ret_udiv_exact_3_from_factors(
+; 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
+}
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..23a51a3798a46
--- /dev/null
+++ b/llvm/test/Transforms/InstCombine/div-exact-from-rem.ll
@@ -0,0 +1,682 @@
+; 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
+}
+
+; 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
+}
+
+; The dominating condition must be available when the division is sunk into
+; the exit block, even though the branch has not been visited yet.
+define i32 @udiv_exact_assume_after_division_unreachable(i32 %x, i32 %y) {
+; CHECK-LABEL: define i32 @udiv_exact_assume_after_division_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 %[[EXIT:.*]], label %[[UNREACHABLE:.*]]
+; CHECK:       [[UNREACHABLE]]:
+; CHECK-NEXT:    unreachable
+; CHECK:       [[EXIT]]:
+; CHECK-NEXT:    [[QUOT:%.*]] = udiv exact 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
+}
diff --git a/llvm/test/Transforms/InstCombine/icmp.ll b/llvm/test/Transforms/InstCombine/icmp.ll
index c7bae58a0b197..906f925f82a80 100644
--- a/llvm/test/Transforms/InstCombine/icmp.ll
+++ b/llvm/test/Transforms/InstCombine/icmp.ll
@@ -2126,26 +2126,18 @@ define i1 @icmp_ule_offset_with_common_divisor(i64 %x, i64 %y) {
 ; when the or disjoint is matched as an add, as nuw of add does not imply nowrap
 ; of the unsigned subtraction.
 ;
-; Note: by the time the first InstCombine iteration is completed, the %xor has been
-; sunk into the `if` block, and the first icmp ult has been canonicalized to an equality.
-; This exposes a constant-folding opportunity knowing that `%arg` is zero from the dominating
-; condition. However, %xor was already visited before the canonicalization, and not re-added
-; to the worklist (as it does not directly use the branch condition), thus we fail to reach a
-; fixpoint within the same iteration.
-define i1 @icmp_ult_neg_offset_or_disjoint(i16 %arg) "instcombine-no-verify-fixpoint" {
+; The dominating condition must be available when %xor is sunk into the `if`
+; block, before the branch is visited, so that we reach a fixpoint.
+define i1 @icmp_ult_neg_offset_or_disjoint(i16 %arg) {
 ; CHECK-LABEL: define i1 @icmp_ult_neg_offset_or_disjoint(
-; CHECK-SAME: i16 [[ARG:%.*]]) #[[ATTR1:[0-9]+]] {
+; CHECK-SAME: i16 [[ARG:%.*]]) {
 ; CHECK-NEXT:  [[ENTRY:.*:]]
 ; CHECK-NEXT:    [[ZEXT:%.*]] = zext nneg i16 [[ARG]] to i32
 ; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i16 [[ARG]], 0
 ; CHECK-NEXT:    call void @use_i32(i32 [[ZEXT]])
 ; CHECK-NEXT:    br i1 [[CMP]], label %[[IF:.*]], label %[[ELSE:.*]]
 ; CHECK:       [[IF]]:
-; CHECK-NEXT:    [[XOR:%.*]] = xor i16 [[ARG]], -32768
-; CHECK-NEXT:    [[OR:%.*]] = or disjoint i32 [[ZEXT]], -32768
-; CHECK-NEXT:    [[SEXT:%.*]] = sext i16 [[XOR]] to i32
-; CHECK-NEXT:    [[RES:%.*]] = icmp ult i32 [[OR]], [[SEXT]]
-; CHECK-NEXT:    ret i1 [[RES]]
+; CHECK-NEXT:    ret i1 false
 ; CHECK:       [[ELSE]]:
 ; CHECK-NEXT:    ret i1 false
 ;
diff --git a/llvm/test/Transforms/InstCombine/sink_instruction.ll b/llvm/test/Transforms/InstCombine/sink_instruction.ll
index c3af8163830b8..bfd74ad5e0033 100644
--- a/llvm/test/Transforms/InstCombine/sink_instruction.ll
+++ b/llvm/test/Transforms/InstCombine/sink_instruction.ll
@@ -27,32 +27,27 @@ endif:          ; preds = %entry
   ret i32 %tmp.2
 }
 
-; We fail to reach a fixpoint, because sunk instructions get revisited too
-; early. In @test2 the sunk add is revisited before the dominating condition
-; is visited and added to the DomConditionCache.
+; The dominating condition must be available when the add is sunk, before
+; the branch is visited, so that we reach a fixpoint.
 
 ;; PHI use, sink divide before call.
-define i32 @test2(i32 %x) nounwind ssp "instcombine-no-verify-fixpoint" {
+define i32 @test2(i32 %x) nounwind ssp {
 ; CHECK-LABEL: @test2(
 ; CHECK-NEXT:  entry:
 ; CHECK-NEXT:    br label [[BB:%.*]]
 ; CHECK:       bb:
-; CHECK-NEXT:    [[X_ADDR_17:%.*]] = phi i32 [ [[X:%.*]], [[ENTRY:%.*]] ], [ [[X_ADDR_0:%.*]], [[BB2:%.*]] ]
-; CHECK-NEXT:    [[I_06:%.*]] = phi i32 [ 0, [[ENTRY]] ], [ [[TMP4:%.*]], [[BB2]] ]
-; CHECK-NEXT:    [[TMP0:%.*]] = icmp eq i32 [[X_ADDR_17]], 0
+; CHECK-NEXT:    [[I_06:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[TMP4:%.*]], [[BB2:%.*]] ]
+; CHECK-NEXT:    [[TMP0:%.*]] = icmp eq i32 [[X_ADDR_17:%.*]], 0
 ; CHECK-NEXT:    br i1 [[TMP0]], label [[BB1:%.*]], label [[BB2]]
 ; CHECK:       bb1:
-; CHECK-NEXT:    [[TMP1:%.*]] = add nsw i32 [[X_ADDR_17]], 1
-; CHECK-NEXT:    [[TMP2:%.*]] = sdiv i32 [[TMP1]], [[X_ADDR_17]]
-; CHECK-NEXT:    [[TMP3:%.*]] = tail call i32 @bar() #[[ATTR3:[0-9]+]]
+; CHECK-NEXT:    [[TMP1:%.*]] = tail call i32 @bar() #[[ATTR3:[0-9]+]]
 ; CHECK-NEXT:    br label [[BB2]]
 ; CHECK:       bb2:
-; CHECK-NEXT:    [[X_ADDR_0]] = phi i32 [ [[TMP2]], [[BB1]] ], [ [[X_ADDR_17]], [[BB]] ]
 ; CHECK-NEXT:    [[TMP4]] = add nuw nsw i32 [[I_06]], 1
 ; CHECK-NEXT:    [[EXITCOND:%.*]] = icmp eq i32 [[TMP4]], 1000000
 ; CHECK-NEXT:    br i1 [[EXITCOND]], label [[BB4:%.*]], label [[BB]]
 ; CHECK:       bb4:
-; CHECK-NEXT:    ret i32 [[X_ADDR_0]]
+; CHECK-NEXT:    ret i32 [[X_ADDR_17]]
 ;
 entry:
   br label %bb



More information about the llvm-commits mailing list