[llvm] 3ec1c43 - [ConstraintElim] Strengthen flags at the closest dominator of all uses. (#220918)

via llvm-commits llvm-commits at lists.llvm.org
Fri Sep 11 00:21:17 PDT 2026


Author: Florian Hahn
Date: 2026-09-11T07:21:12Z
New Revision: 3ec1c433b3a4bacbb055f2a2ce2d5ef3ca4172ae

URL: https://github.com/llvm/llvm-project/commit/3ec1c433b3a4bacbb055f2a2ce2d5ef3ca4172ae
DIFF: https://github.com/llvm/llvm-project/commit/3ec1c433b3a4bacbb055f2a2ce2d5ef3ca4172ae.diff

LOG: [ConstraintElim] Strengthen flags at the closest dominator of all uses. (#220918)

Flags are currently only strengthened using the facts that hold on entry
to the block defining the instruction. This overly pessimistic. It is
sufficient to check at the closest instruction dominating all uses.

Extend FactOrCheck to carry this point as context instruction (in union
with the mutually exclusive DoesHold to avoid size growth) and retrieve
the common dominating instruction when queuing instructions for flag
strengthening.

This strengthens flag inference in a number of real-world cases, as well
as enables additional folds due stronger flags:
https://github.com/dtcxzyw/llvm-opt-benchmark-nightly/pull/1171

Compile-time impact is in the noise

https://llvm-compile-time-tracker.com/compare.php?from=11bc6ce46e227718c3b54aa6889d7458656e8ab4&to=6f67fdcdb62a5fe56060cb266448db8b763e13d7&stat=instructions:u

PR: https://github.com/llvm/llvm-project/pull/220918

Added: 
    llvm/test/Transforms/ConstraintElimination/strengthen-flags-at-uses.ll

Modified: 
    llvm/lib/Transforms/Scalar/ConstraintElimination.cpp
    llvm/test/Transforms/ConstraintElimination/monotonic-int-phis-signed.ll

Removed: 
    


################################################################################
diff  --git a/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp b/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp
index 3f96887d16fec..ed95686e9e5d9 100644
--- a/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp
+++ b/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp
@@ -75,6 +75,25 @@ static Instruction *getContextInstForUse(Use &U) {
   return UserI;
 }
 
+/// Returns the closest program point dominating all uses of \p I.
+static Instruction *findCommonDominatorOfUses(Instruction &I,
+                                              DominatorTree &DT) {
+  Instruction *CommonDom = nullptr;
+  unsigned NumUses = 0;
+  for (Use &U : I.uses()) {
+    // Conservatively use original instruction, if there are too many uses.
+    if (++NumUses == 16)
+      return &I;
+    Instruction *UserI = getContextInstForUse(U);
+    CommonDom =
+        CommonDom ? DT.findNearestCommonDominator(CommonDom, UserI) : UserI;
+  }
+  if (!CommonDom)
+    return &I;
+  // Uses in unreachable blocks are not in the dominator tree.
+  return DT.getNode(CommonDom->getParent()) ? CommonDom : &I;
+}
+
 namespace {
 using Entry = ConstraintSystem::Entry;
 using RowTy = ConstraintSystem::RowTy;
@@ -111,21 +130,28 @@ struct FactOrCheck {
     ConditionTy Cond;
   };
 
-  /// A pre-condition that must hold for the current fact to be added to the
-  /// system.
-  ConditionTy DoesHold;
+  union {
+    /// A pre-condition that must hold for the current fact to be added to the
+    /// system. Only used by condition facts.
+    ConditionTy DoesHold;
+
+    /// Context instruction for the point where conditions are checked for
+    /// InstCheck simplifications.
+    Instruction *ContextInst;
+  };
 
   unsigned NumIn;
   unsigned NumOut;
   EntryTy Ty;
 
-  FactOrCheck(EntryTy Ty, DomTreeNode *DTN, Instruction *Inst)
-      : Inst(Inst), NumIn(DTN->getDFSNumIn()), NumOut(DTN->getDFSNumOut()),
-        Ty(Ty) {}
+  FactOrCheck(EntryTy Ty, DomTreeNode *DTN, Instruction *Inst,
+              Instruction *ContextInst = nullptr)
+      : Inst(Inst), ContextInst(ContextInst ? ContextInst : Inst),
+        NumIn(DTN->getDFSNumIn()), NumOut(DTN->getDFSNumOut()), Ty(Ty) {}
 
   FactOrCheck(DomTreeNode *DTN, Use *U)
-      : U(U), NumIn(DTN->getDFSNumIn()), NumOut(DTN->getDFSNumOut()),
-        Ty(EntryTy::UseCheck) {}
+      : U(U), ContextInst(nullptr), NumIn(DTN->getDFSNumIn()),
+        NumOut(DTN->getDFSNumOut()), Ty(EntryTy::UseCheck) {}
 
   FactOrCheck(DomTreeNode *DTN, CmpPredicate Pred, Value *Op0, Value *Op1,
               ConditionTy Precond = {})
@@ -146,8 +172,11 @@ struct FactOrCheck {
     return FactOrCheck(DTN, U);
   }
 
-  static FactOrCheck getCheck(DomTreeNode *DTN, Instruction *I) {
-    return FactOrCheck(EntryTy::InstCheck, DTN, I);
+  static FactOrCheck getCheck(DomTreeNode *DTN, Instruction *I,
+                              Instruction *ContextInst = nullptr) {
+    assert((ContextInst ? ContextInst : I)->getParent() == DTN->getBlock() &&
+           "anchoring instruction must be in DTN's block");
+    return FactOrCheck(EntryTy::InstCheck, DTN, I, ContextInst);
   }
 
   bool isCheck() const {
@@ -158,7 +187,7 @@ struct FactOrCheck {
     assert(!isConditionFact());
     if (Ty == EntryTy::UseCheck)
       return getContextInstForUse(*U);
-    return Inst;
+    return ContextInst;
   }
 
   Instruction *getInstructionToSimplify() const {
@@ -1559,10 +1588,13 @@ void State::addInfoFor(BasicBlock &BB) {
         WorkList.push_back(FactOrCheck::getInstFact(DT.getNode(&BB), BO));
     }
 
-    // Queue instructions whose flags may be strengthened based on the facts
-    // that hold on entry to BB.
-    if (canStrengthenFlags(&I))
-      WorkList.push_back(FactOrCheck::getCheck(DT.getNode(&BB), &I));
+    // Queue instructions whose flags may be strengthened, checked at the
+    // closest point dominating all uses.
+    if (canStrengthenFlags(&I)) {
+      Instruction *CommonDom = findCommonDominatorOfUses(I, DT);
+      WorkList.push_back(FactOrCheck::getCheck(
+          DT.getNode(CommonDom->getParent()), &I, CommonDom));
+    }
 
     GuaranteedToExecute &= isGuaranteedToTransferExecutionToSuccessor(&I);
   }

diff  --git a/llvm/test/Transforms/ConstraintElimination/monotonic-int-phis-signed.ll b/llvm/test/Transforms/ConstraintElimination/monotonic-int-phis-signed.ll
index a5c0702896efd..ff27d0f8007db 100644
--- a/llvm/test/Transforms/ConstraintElimination/monotonic-int-phis-signed.ll
+++ b/llvm/test/Transforms/ConstraintElimination/monotonic-int-phis-signed.ll
@@ -46,7 +46,7 @@ define void @signed_iv_step_4(i64 %count) {
 ; CHECK-LABEL: define void @signed_iv_step_4(
 ; CHECK-SAME: i64 [[COUNT:%.*]]) {
 ; CHECK-NEXT:  entry:
-; CHECK-NEXT:    [[END:%.*]] = shl nsw i64 [[COUNT]], 2
+; CHECK-NEXT:    [[END:%.*]] = shl nuw nsw i64 [[COUNT]], 2
 ; CHECK-NEXT:    [[PRECOND:%.*]] = icmp sgt i64 [[COUNT]], -1
 ; CHECK-NEXT:    br i1 [[PRECOND]], label [[LOOP:%.*]], label [[EXIT:%.*]]
 ; CHECK:       loop:
@@ -130,7 +130,7 @@ define void @signed_iv_step_4_start_4(i64 %count) {
 ; CHECK-LABEL: define void @signed_iv_step_4_start_4(
 ; CHECK-SAME: i64 [[COUNT:%.*]]) {
 ; CHECK-NEXT:  entry:
-; CHECK-NEXT:    [[END:%.*]] = shl nsw i64 [[COUNT]], 2
+; CHECK-NEXT:    [[END:%.*]] = shl nuw nsw i64 [[COUNT]], 2
 ; CHECK-NEXT:    [[PRECOND:%.*]] = icmp sgt i64 [[COUNT]], 0
 ; CHECK-NEXT:    br i1 [[PRECOND]], label [[LOOP:%.*]], label [[EXIT:%.*]]
 ; CHECK:       loop:
@@ -171,7 +171,7 @@ define void @signed_iv_step_4_start_4_missing_precond(i64 %count) {
 ; CHECK-LABEL: define void @signed_iv_step_4_start_4_missing_precond(
 ; CHECK-SAME: i64 [[COUNT:%.*]]) {
 ; CHECK-NEXT:  entry:
-; CHECK-NEXT:    [[END:%.*]] = shl nsw i64 [[COUNT]], 2
+; CHECK-NEXT:    [[END:%.*]] = shl nuw nsw i64 [[COUNT]], 2
 ; CHECK-NEXT:    [[PRECOND:%.*]] = icmp sgt i64 [[COUNT]], -1
 ; CHECK-NEXT:    br i1 [[PRECOND]], label [[LOOP:%.*]], label [[EXIT:%.*]]
 ; CHECK:       loop:

diff  --git a/llvm/test/Transforms/ConstraintElimination/strengthen-flags-at-uses.ll b/llvm/test/Transforms/ConstraintElimination/strengthen-flags-at-uses.ll
new file mode 100644
index 0000000000000..8929a35c3fa0d
--- /dev/null
+++ b/llvm/test/Transforms/ConstraintElimination/strengthen-flags-at-uses.ll
@@ -0,0 +1,393 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
+; RUN: opt -passes=constraint-elimination -S %s | FileCheck %s
+
+define i64 @sub_hoisted_single_use(i64 %a, i64 %b) {
+; CHECK-LABEL: define i64 @sub_hoisted_single_use(
+; CHECK-SAME: i64 [[A:%.*]], i64 [[B:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[C:%.*]] = icmp uge i64 [[A]], [[B]]
+; CHECK-NEXT:    [[SUB:%.*]] = sub nuw i64 [[A]], [[B]]
+; CHECK-NEXT:    br i1 [[C]], label %[[IF_END:.*]], label %[[EXIT:.*]]
+; CHECK:       [[IF_END]]:
+; CHECK-NEXT:    ret i64 [[SUB]]
+; CHECK:       [[EXIT]]:
+; CHECK-NEXT:    ret i64 0
+;
+entry:
+  %c = icmp uge i64 %a, %b
+  %sub = sub i64 %a, %b
+  br i1 %c, label %if.end, label %exit
+
+if.end:
+  ret i64 %sub
+
+exit:
+  ret i64 0
+}
+
+define i64 @sub_hoisted_use_outside(i64 %a, i64 %b) {
+; CHECK-LABEL: define i64 @sub_hoisted_use_outside(
+; CHECK-SAME: i64 [[A:%.*]], i64 [[B:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[C:%.*]] = icmp uge i64 [[A]], [[B]]
+; CHECK-NEXT:    [[SUB:%.*]] = sub i64 [[A]], [[B]]
+; CHECK-NEXT:    br i1 [[C]], label %[[IF_END:.*]], label %[[EXIT:.*]]
+; CHECK:       [[IF_END]]:
+; CHECK-NEXT:    ret i64 [[SUB]]
+; CHECK:       [[EXIT]]:
+; CHECK-NEXT:    ret i64 [[SUB]]
+;
+entry:
+  %c = icmp uge i64 %a, %b
+  %sub = sub i64 %a, %b
+  br i1 %c, label %if.end, label %exit
+
+if.end:
+  ret i64 %sub
+
+exit:
+  ret i64 %sub
+}
+
+define i64 @sub_anchored_after_assume(i64 %a, i64 %b) {
+; CHECK-LABEL: define i64 @sub_anchored_after_assume(
+; CHECK-SAME: i64 [[A:%.*]], i64 [[B:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[SUB:%.*]] = sub nuw i64 [[A]], [[B]]
+; CHECK-NEXT:    br label %[[MID:.*]]
+; CHECK:       [[MID]]:
+; CHECK-NEXT:    call void @barrier()
+; CHECK-NEXT:    [[C:%.*]] = icmp uge i64 [[A]], [[B]]
+; CHECK-NEXT:    call void @llvm.assume(i1 [[C]])
+; CHECK-NEXT:    ret i64 [[SUB]]
+;
+entry:
+  %sub = sub i64 %a, %b
+  br label %mid
+
+mid:
+  call void @barrier()
+  %c = icmp uge i64 %a, %b
+  call void @llvm.assume(i1 %c)
+  ret i64 %sub
+}
+
+define i64 @sub_used_before_assume(i64 %a, i64 %b) {
+; CHECK-LABEL: define i64 @sub_used_before_assume(
+; CHECK-SAME: i64 [[A:%.*]], i64 [[B:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[SUB:%.*]] = sub i64 [[A]], [[B]]
+; CHECK-NEXT:    br label %[[MID:.*]]
+; CHECK:       [[MID]]:
+; CHECK-NEXT:    call void @use(i64 [[SUB]])
+; CHECK-NEXT:    [[C:%.*]] = icmp uge i64 [[A]], [[B]]
+; CHECK-NEXT:    call void @llvm.assume(i1 [[C]])
+; CHECK-NEXT:    ret i64 [[SUB]]
+;
+entry:
+  %sub = sub i64 %a, %b
+  br label %mid
+
+mid:
+  call void @use(i64 %sub)
+  %c = icmp uge i64 %a, %b
+  call void @llvm.assume(i1 %c)
+  ret i64 %sub
+}
+
+; The use in %other is not dominated by the assume, so no flag can be inferred.
+define i64 @sub_use_not_dominated_by_assume(i64 %a, i64 %b, i1 %cc) {
+; CHECK-LABEL: define i64 @sub_use_not_dominated_by_assume(
+; CHECK-SAME: i64 [[A:%.*]], i64 [[B:%.*]], i1 [[CC:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[SUB:%.*]] = sub i64 [[A]], [[B]]
+; CHECK-NEXT:    br i1 [[CC]], label %[[MID:.*]], label %[[OTHER:.*]]
+; CHECK:       [[MID]]:
+; CHECK-NEXT:    [[C:%.*]] = icmp uge i64 [[A]], [[B]]
+; CHECK-NEXT:    call void @llvm.assume(i1 [[C]])
+; CHECK-NEXT:    ret i64 [[SUB]]
+; CHECK:       [[OTHER]]:
+; CHECK-NEXT:    ret i64 [[SUB]]
+;
+entry:
+  %sub = sub i64 %a, %b
+  br i1 %cc, label %mid, label %other
+
+mid:
+  %c = icmp uge i64 %a, %b
+  call void @llvm.assume(i1 %c)
+  ret i64 %sub
+
+other:
+  ret i64 %sub
+}
+
+define i64 @mul_hoisted_single_use(i64 %a) {
+; CHECK-LABEL: define i64 @mul_hoisted_single_use(
+; CHECK-SAME: i64 [[A:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[MUL:%.*]] = mul nuw nsw i64 [[A]], 4
+; CHECK-NEXT:    [[C:%.*]] = icmp sge i64 [[A]], 0
+; CHECK-NEXT:    br i1 [[C]], label %[[IF_END:.*]], label %[[EXIT:.*]]
+; CHECK:       [[IF_END]]:
+; CHECK-NEXT:    ret i64 [[MUL]]
+; CHECK:       [[EXIT]]:
+; CHECK-NEXT:    ret i64 0
+;
+entry:
+  %mul = mul nsw i64 %a, 4
+  %c = icmp sge i64 %a, 0
+  br i1 %c, label %if.end, label %exit
+
+if.end:
+  ret i64 %mul
+
+exit:
+  ret i64 0
+}
+
+define void @sub_15_uses_fact_at_common_dominator(i64 %a, i64 %b) {
+; CHECK-LABEL: define void @sub_15_uses_fact_at_common_dominator(
+; CHECK-SAME: i64 [[A:%.*]], i64 [[B:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[SUB:%.*]] = sub nuw i64 [[A]], [[B]]
+; CHECK-NEXT:    br label %[[USE_BB:.*]]
+; CHECK:       [[USE_BB]]:
+; CHECK-NEXT:    [[C:%.*]] = icmp uge i64 [[A]], [[B]]
+; CHECK-NEXT:    call void @llvm.assume(i1 [[C]])
+; CHECK-NEXT:    call void @use(i64 [[SUB]])
+; CHECK-NEXT:    call void @use(i64 [[SUB]])
+; CHECK-NEXT:    call void @use(i64 [[SUB]])
+; CHECK-NEXT:    call void @use(i64 [[SUB]])
+; CHECK-NEXT:    call void @use(i64 [[SUB]])
+; CHECK-NEXT:    call void @use(i64 [[SUB]])
+; CHECK-NEXT:    call void @use(i64 [[SUB]])
+; CHECK-NEXT:    call void @use(i64 [[SUB]])
+; CHECK-NEXT:    call void @use(i64 [[SUB]])
+; CHECK-NEXT:    call void @use(i64 [[SUB]])
+; CHECK-NEXT:    call void @use(i64 [[SUB]])
+; CHECK-NEXT:    call void @use(i64 [[SUB]])
+; CHECK-NEXT:    call void @use(i64 [[SUB]])
+; CHECK-NEXT:    call void @use(i64 [[SUB]])
+; CHECK-NEXT:    call void @use(i64 [[SUB]])
+; CHECK-NEXT:    ret void
+;
+entry:
+  %sub = sub i64 %a, %b
+  br label %use.bb
+
+use.bb:
+  %c = icmp uge i64 %a, %b
+  call void @llvm.assume(i1 %c)
+  call void @use(i64 %sub)
+  call void @use(i64 %sub)
+  call void @use(i64 %sub)
+  call void @use(i64 %sub)
+  call void @use(i64 %sub)
+  call void @use(i64 %sub)
+  call void @use(i64 %sub)
+  call void @use(i64 %sub)
+  call void @use(i64 %sub)
+  call void @use(i64 %sub)
+  call void @use(i64 %sub)
+  call void @use(i64 %sub)
+  call void @use(i64 %sub)
+  call void @use(i64 %sub)
+  call void @use(i64 %sub)
+  ret void
+}
+
+; Same as @sub_15_uses_fact_at_common_dominator, but with 16 uses the scan gives
+; up and the flags are checked at %sub itself, which the assume does not
+; dominate. No flag can be inferred.
+define void @sub_16_uses_fact_at_common_dominator(i64 %a, i64 %b) {
+; CHECK-LABEL: define void @sub_16_uses_fact_at_common_dominator(
+; CHECK-SAME: i64 [[A:%.*]], i64 [[B:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[SUB:%.*]] = sub i64 [[A]], [[B]]
+; CHECK-NEXT:    br label %[[USE_BB:.*]]
+; CHECK:       [[USE_BB]]:
+; CHECK-NEXT:    [[C:%.*]] = icmp uge i64 [[A]], [[B]]
+; CHECK-NEXT:    call void @llvm.assume(i1 [[C]])
+; CHECK-NEXT:    call void @use(i64 [[SUB]])
+; CHECK-NEXT:    call void @use(i64 [[SUB]])
+; CHECK-NEXT:    call void @use(i64 [[SUB]])
+; CHECK-NEXT:    call void @use(i64 [[SUB]])
+; CHECK-NEXT:    call void @use(i64 [[SUB]])
+; CHECK-NEXT:    call void @use(i64 [[SUB]])
+; CHECK-NEXT:    call void @use(i64 [[SUB]])
+; CHECK-NEXT:    call void @use(i64 [[SUB]])
+; CHECK-NEXT:    call void @use(i64 [[SUB]])
+; CHECK-NEXT:    call void @use(i64 [[SUB]])
+; CHECK-NEXT:    call void @use(i64 [[SUB]])
+; CHECK-NEXT:    call void @use(i64 [[SUB]])
+; CHECK-NEXT:    call void @use(i64 [[SUB]])
+; CHECK-NEXT:    call void @use(i64 [[SUB]])
+; CHECK-NEXT:    call void @use(i64 [[SUB]])
+; CHECK-NEXT:    call void @use(i64 [[SUB]])
+; CHECK-NEXT:    ret void
+;
+entry:
+  %sub = sub i64 %a, %b
+  br label %use.bb
+
+use.bb:
+  %c = icmp uge i64 %a, %b
+  call void @llvm.assume(i1 %c)
+  call void @use(i64 %sub)
+  call void @use(i64 %sub)
+  call void @use(i64 %sub)
+  call void @use(i64 %sub)
+  call void @use(i64 %sub)
+  call void @use(i64 %sub)
+  call void @use(i64 %sub)
+  call void @use(i64 %sub)
+  call void @use(i64 %sub)
+  call void @use(i64 %sub)
+  call void @use(i64 %sub)
+  call void @use(i64 %sub)
+  call void @use(i64 %sub)
+  call void @use(i64 %sub)
+  call void @use(i64 %sub)
+  call void @use(i64 %sub)
+  ret void
+}
+
+define void @sub_16_uses_fact_at_def(i64 %a, i64 %b) {
+; CHECK-LABEL: define void @sub_16_uses_fact_at_def(
+; CHECK-SAME: i64 [[A:%.*]], i64 [[B:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[C:%.*]] = icmp uge i64 [[A]], [[B]]
+; CHECK-NEXT:    call void @llvm.assume(i1 [[C]])
+; CHECK-NEXT:    [[SUB:%.*]] = sub nuw i64 [[A]], [[B]]
+; CHECK-NEXT:    br label %[[USE_BB:.*]]
+; CHECK:       [[USE_BB]]:
+; CHECK-NEXT:    call void @use(i64 [[SUB]])
+; CHECK-NEXT:    call void @use(i64 [[SUB]])
+; CHECK-NEXT:    call void @use(i64 [[SUB]])
+; CHECK-NEXT:    call void @use(i64 [[SUB]])
+; CHECK-NEXT:    call void @use(i64 [[SUB]])
+; CHECK-NEXT:    call void @use(i64 [[SUB]])
+; CHECK-NEXT:    call void @use(i64 [[SUB]])
+; CHECK-NEXT:    call void @use(i64 [[SUB]])
+; CHECK-NEXT:    call void @use(i64 [[SUB]])
+; CHECK-NEXT:    call void @use(i64 [[SUB]])
+; CHECK-NEXT:    call void @use(i64 [[SUB]])
+; CHECK-NEXT:    call void @use(i64 [[SUB]])
+; CHECK-NEXT:    call void @use(i64 [[SUB]])
+; CHECK-NEXT:    call void @use(i64 [[SUB]])
+; CHECK-NEXT:    call void @use(i64 [[SUB]])
+; CHECK-NEXT:    call void @use(i64 [[SUB]])
+; CHECK-NEXT:    ret void
+;
+entry:
+  %c = icmp uge i64 %a, %b
+  call void @llvm.assume(i1 %c)
+  %sub = sub i64 %a, %b
+  br label %use.bb
+
+use.bb:
+  call void @use(i64 %sub)
+  call void @use(i64 %sub)
+  call void @use(i64 %sub)
+  call void @use(i64 %sub)
+  call void @use(i64 %sub)
+  call void @use(i64 %sub)
+  call void @use(i64 %sub)
+  call void @use(i64 %sub)
+  call void @use(i64 %sub)
+  call void @use(i64 %sub)
+  call void @use(i64 %sub)
+  call void @use(i64 %sub)
+  call void @use(i64 %sub)
+  call void @use(i64 %sub)
+  call void @use(i64 %sub)
+  call void @use(i64 %sub)
+  ret void
+}
+
+; The only use of %sub is in an unreachable block.
+define i64 @sub_single_use_in_unreachable_block(i64 %a, i64 %b) {
+; CHECK-LABEL: define i64 @sub_single_use_in_unreachable_block(
+; CHECK-SAME: i64 [[A:%.*]], i64 [[B:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[C:%.*]] = icmp uge i64 [[A]], [[B]]
+; CHECK-NEXT:    call void @llvm.assume(i1 [[C]])
+; CHECK-NEXT:    [[SUB:%.*]] = sub nuw i64 [[A]], [[B]]
+; CHECK-NEXT:    ret i64 0
+; CHECK:       [[UNREACHABLE_BB:.*:]]
+; CHECK-NEXT:    call void @use(i64 [[SUB]])
+; CHECK-NEXT:    ret i64 0
+;
+entry:
+  %c = icmp uge i64 %a, %b
+  call void @llvm.assume(i1 %c)
+  %sub = sub i64 %a, %b
+  ret i64 0
+
+unreachable.bb:
+  call void @use(i64 %sub)
+  ret i64 0
+}
+
+; As above, but with the uses spread over two unreachable blocks.
+define i64 @sub_uses_in_two_unreachable_blocks(i64 %a, i64 %b) {
+; CHECK-LABEL: define i64 @sub_uses_in_two_unreachable_blocks(
+; CHECK-SAME: i64 [[A:%.*]], i64 [[B:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[C:%.*]] = icmp uge i64 [[A]], [[B]]
+; CHECK-NEXT:    call void @llvm.assume(i1 [[C]])
+; CHECK-NEXT:    [[SUB:%.*]] = sub nuw i64 [[A]], [[B]]
+; CHECK-NEXT:    ret i64 0
+; CHECK:       [[UNREACHABLE_BB_1:.*:]]
+; CHECK-NEXT:    call void @use(i64 [[SUB]])
+; CHECK-NEXT:    br label %[[UNREACHABLE_BB_2:.*]]
+; CHECK:       [[UNREACHABLE_BB_2]]:
+; CHECK-NEXT:    call void @use(i64 [[SUB]])
+; CHECK-NEXT:    ret i64 0
+;
+entry:
+  %c = icmp uge i64 %a, %b
+  call void @llvm.assume(i1 %c)
+  %sub = sub i64 %a, %b
+  ret i64 0
+
+unreachable.bb.1:
+  call void @use(i64 %sub)
+  br label %unreachable.bb.2
+
+unreachable.bb.2:
+  call void @use(i64 %sub)
+  ret i64 0
+}
+
+define i64 @sub_use_in_unreachable_block_ignored(i64 %a, i64 %b) {
+; CHECK-LABEL: define i64 @sub_use_in_unreachable_block_ignored(
+; CHECK-SAME: i64 [[A:%.*]], i64 [[B:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[SUB:%.*]] = sub nuw i64 [[A]], [[B]]
+; CHECK-NEXT:    br label %[[MID:.*]]
+; CHECK:       [[MID]]:
+; CHECK-NEXT:    [[C:%.*]] = icmp uge i64 [[A]], [[B]]
+; CHECK-NEXT:    call void @llvm.assume(i1 [[C]])
+; CHECK-NEXT:    ret i64 [[SUB]]
+; CHECK:       [[UNREACHABLE_BB:.*:]]
+; CHECK-NEXT:    call void @use(i64 [[SUB]])
+; CHECK-NEXT:    ret i64 0
+;
+entry:
+  %sub = sub i64 %a, %b
+  br label %mid
+
+mid:
+  %c = icmp uge i64 %a, %b
+  call void @llvm.assume(i1 %c)
+  ret i64 %sub
+
+unreachable.bb:
+  call void @use(i64 %sub)
+  ret i64 0
+}
+
+declare void @use(i64)
+declare void @barrier()


        


More information about the llvm-commits mailing list