[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