[llvm] [ValueTracking] Use dominating conditions in computeConstantRange (PR #210412)

via llvm-commits llvm-commits at lists.llvm.org
Fri Jul 17 14:03:22 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-llvm-analysis

Author: Joel Walker  (Joel-Wwalker)

<details>
<summary>Changes</summary>

`computeConstantRange` already restricts a value's range using `llvm.assume`, but not using dominating branch conditions, even though `computeKnownBits`, `computeKnownFPClass`, and `isKnownNonEqual` all do. As a result a range guard like `if (a <= 15 && b <= 15)` does not tighten the range of `a` or `b`, so a derived compare such as `smin(a, b) > 15` does not fold, while the equivalent bitmask guard (which goes through knownbits) does.

This adds a dominating-condition walk mirroring the `llvm.assume` handling directly below it: for a condition `icmp <pred> V, C` whose true (or false) edge dominates the context, intersect the range with `makeAllowedICmpRegion(<pred>, range(C))` (or the inverse predicate on the false edge). It is gated on a populated `DomConditionCache`/`DominatorTree`, so only passes that already build one pay any cost, and those already run the same walk for knownbits.

Motivated by #<!-- -->207131. This resolves the missing analysis the issue identifies (the dead branch is eliminated when the guards are present as dominating conditions); the fully flattened `-O3` form in that report, where SimplifyCFG has merged the guards into a single `and` before the compare, needs separate work and is not addressed here.

Assisted by Claude (Anthropic).


---
Full diff: https://github.com/llvm/llvm-project/pull/210412.diff


7 Files Affected:

- (modified) llvm/lib/Analysis/ValueTracking.cpp (+26) 
- (added) llvm/test/Transforms/InstCombine/icmp-dominating-condition-range.ll (+167) 
- (modified) llvm/test/Transforms/LoopVectorize/induction.ll (+1-1) 
- (modified) llvm/test/Transforms/LoopVectorize/interleaved-accesses-2.ll (+1-1) 
- (modified) llvm/test/Transforms/LoopVectorize/interleaved-accesses.ll (+4-4) 
- (modified) llvm/test/Transforms/PhaseOrdering/AArch64/hoist-load-from-vector-loop.ll (+1-1) 
- (modified) llvm/unittests/Analysis/ValueTrackingTest.cpp (+38) 


``````````diff
diff --git a/llvm/lib/Analysis/ValueTracking.cpp b/llvm/lib/Analysis/ValueTracking.cpp
index efc14f9a639da..83e6848122c78 100644
--- a/llvm/lib/Analysis/ValueTracking.cpp
+++ b/llvm/lib/Analysis/ValueTracking.cpp
@@ -10588,6 +10588,32 @@ ConstantRange llvm::computeConstantRange(const Value *V, bool ForSigned,
     }
   }
 
+  if (SQ.CxtI && SQ.DC && SQ.DT) {
+    // Try to restrict the range based on dominating conditions. This mirrors
+    // the dominating-condition handling in computeKnownBitsFromContext, which
+    // computeConstantRange previously lacked.
+    for (CondBrInst *BI : SQ.DC->conditionsFor(V)) {
+      auto *Cmp = dyn_cast<ICmpInst>(BI->getCondition());
+      // Currently we just use information from comparisons directly on V.
+      if (!Cmp || Cmp->getOperand(0) != V)
+        continue;
+      // TODO: Set "ForSigned" parameter via Cmp->isSigned()?
+      ConstantRange RHS =
+          computeConstantRange(Cmp->getOperand(1), /*ForSigned=*/false,
+                               SQ.getWithInstruction(BI), Depth + 1);
+      // The true edge implies the condition; the false edge implies its
+      // inverse. At most one of them can dominate the context.
+      BasicBlockEdge TrueEdge(BI->getParent(), BI->getSuccessor(0));
+      if (SQ.DT->dominates(TrueEdge, SQ.CxtI->getParent()))
+        CR = CR.intersectWith(
+            ConstantRange::makeAllowedICmpRegion(Cmp->getCmpPredicate(), RHS));
+      BasicBlockEdge FalseEdge(BI->getParent(), BI->getSuccessor(1));
+      if (SQ.DT->dominates(FalseEdge, SQ.CxtI->getParent()))
+        CR = CR.intersectWith(ConstantRange::makeAllowedICmpRegion(
+            Cmp->getInverseCmpPredicate(), RHS));
+    }
+  }
+
   if (SQ.CxtI && SQ.AC) {
     // Try to restrict the range based on information from assumptions.
     for (auto &AssumeVH : SQ.AC->assumptionsFor(V)) {
diff --git a/llvm/test/Transforms/InstCombine/icmp-dominating-condition-range.ll b/llvm/test/Transforms/InstCombine/icmp-dominating-condition-range.ll
new file mode 100644
index 0000000000000..ee9df0c3f14f1
--- /dev/null
+++ b/llvm/test/Transforms/InstCombine/icmp-dominating-condition-range.ll
@@ -0,0 +1,167 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
+; RUN: opt < %s -passes=instcombine -S | FileCheck %s
+
+; Tests that computeConstantRange picks up dominating conditions, not just
+; llvm.assume. Each query is on a value derived from operands that are only
+; range-constrained by a dominating branch, through a diamond so that a
+; single-predecessor walk cannot see the guard. See issue #207131.
+
+declare void @use()
+declare i32 @llvm.smin.i32(i32, i32)
+declare i32 @llvm.umax.i32(i32, i32)
+
+; a < 16 and b < 16 dominate %join, so smin(a, b) <= 15 and "smin > 15" is false.
+define i1 @smin_sgt_dominating_range(i32 %a, i32 %b, i32 %c) {
+; CHECK-LABEL: @smin_sgt_dominating_range(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[GA:%.*]] = icmp ult i32 [[A:%.*]], 16
+; CHECK-NEXT:    br i1 [[GA]], label [[G2:%.*]], label [[ELSE:%.*]]
+; CHECK:       g2:
+; CHECK-NEXT:    [[GB:%.*]] = icmp ult i32 [[B:%.*]], 16
+; CHECK-NEXT:    br i1 [[GB]], label [[MID:%.*]], label [[ELSE]]
+; CHECK:       mid:
+; CHECK-NEXT:    [[SPLIT:%.*]] = icmp ult i32 [[C:%.*]], 5
+; CHECK-NEXT:    br i1 [[SPLIT]], label [[LEFT:%.*]], label [[RIGHT:%.*]]
+; CHECK:       left:
+; CHECK-NEXT:    br label [[JOIN:%.*]]
+; CHECK:       right:
+; CHECK-NEXT:    br label [[JOIN]]
+; CHECK:       join:
+; CHECK-NEXT:    ret i1 false
+; CHECK:       else:
+; CHECK-NEXT:    ret i1 false
+;
+entry:
+  %ga = icmp ult i32 %a, 16
+  br i1 %ga, label %g2, label %else
+g2:
+  %gb = icmp ult i32 %b, 16
+  br i1 %gb, label %mid, label %else
+mid:
+  %split = icmp ult i32 %c, 5
+  br i1 %split, label %left, label %right
+left:
+  br label %join
+right:
+  br label %join
+join:
+  %m = call i32 @llvm.smin.i32(i32 %a, i32 %b)
+  %r = icmp sgt i32 %m, 15
+  ret i1 %r
+else:
+  ret i1 false
+}
+
+; a < 11 and b < 11 dominate %join, so umax(a, b) <= 10 and "umax < 20" is true.
+define i1 @umax_ult_dominating_range(i32 %a, i32 %b, i32 %c) {
+; CHECK-LABEL: @umax_ult_dominating_range(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[GA:%.*]] = icmp ult i32 [[A:%.*]], 11
+; CHECK-NEXT:    br i1 [[GA]], label [[G2:%.*]], label [[ELSE:%.*]]
+; CHECK:       g2:
+; CHECK-NEXT:    [[GB:%.*]] = icmp ult i32 [[B:%.*]], 11
+; CHECK-NEXT:    br i1 [[GB]], label [[MID:%.*]], label [[ELSE]]
+; CHECK:       mid:
+; CHECK-NEXT:    [[SPLIT:%.*]] = icmp ult i32 [[C:%.*]], 5
+; CHECK-NEXT:    br i1 [[SPLIT]], label [[LEFT:%.*]], label [[RIGHT:%.*]]
+; CHECK:       left:
+; CHECK-NEXT:    br label [[JOIN:%.*]]
+; CHECK:       right:
+; CHECK-NEXT:    br label [[JOIN]]
+; CHECK:       join:
+; CHECK-NEXT:    ret i1 true
+; CHECK:       else:
+; CHECK-NEXT:    ret i1 false
+;
+entry:
+  %ga = icmp ult i32 %a, 11
+  br i1 %ga, label %g2, label %else
+g2:
+  %gb = icmp ult i32 %b, 11
+  br i1 %gb, label %mid, label %else
+mid:
+  %split = icmp ult i32 %c, 5
+  br i1 %split, label %left, label %right
+left:
+  br label %join
+right:
+  br label %join
+join:
+  %m = call i32 @llvm.umax.i32(i32 %a, i32 %b)
+  %r = icmp ult i32 %m, 20
+  ret i1 %r
+else:
+  ret i1 false
+}
+
+; The false edge of "a >= 16" dominates %join, implying a < 16. Exercises the
+; inverse-predicate path.
+define i1 @smin_inverted_edge(i32 %a, i32 %b, i32 %c) {
+; CHECK-LABEL: @smin_inverted_edge(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[GA:%.*]] = icmp ugt i32 [[A:%.*]], 15
+; CHECK-NEXT:    br i1 [[GA]], label [[ELSE:%.*]], label [[G2:%.*]]
+; CHECK:       g2:
+; CHECK-NEXT:    [[GB:%.*]] = icmp ult i32 [[B:%.*]], 16
+; CHECK-NEXT:    br i1 [[GB]], label [[MID:%.*]], label [[ELSE]]
+; CHECK:       mid:
+; CHECK-NEXT:    [[SPLIT:%.*]] = icmp ult i32 [[C:%.*]], 5
+; CHECK-NEXT:    br i1 [[SPLIT]], label [[LEFT:%.*]], label [[RIGHT:%.*]]
+; CHECK:       left:
+; CHECK-NEXT:    br label [[JOIN:%.*]]
+; CHECK:       right:
+; CHECK-NEXT:    br label [[JOIN]]
+; CHECK:       join:
+; CHECK-NEXT:    ret i1 false
+; CHECK:       else:
+; CHECK-NEXT:    ret i1 false
+;
+entry:
+  %ga = icmp uge i32 %a, 16
+  br i1 %ga, label %else, label %g2
+g2:
+  %gb = icmp ult i32 %b, 16
+  br i1 %gb, label %mid, label %else
+mid:
+  %split = icmp ult i32 %c, 5
+  br i1 %split, label %left, label %right
+left:
+  br label %join
+right:
+  br label %join
+join:
+  %m = call i32 @llvm.smin.i32(i32 %a, i32 %b)
+  %r = icmp sgt i32 %m, 15
+  ret i1 %r
+else:
+  ret i1 false
+}
+
+; Negative test: the guard does not dominate %join (sibling edge), so the range
+; stays unknown and the compare must not fold.
+define i1 @not_dominating(i32 %a, i32 %b, i1 %cc) {
+; CHECK-LABEL: @not_dominating(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    br i1 [[CC:%.*]], label [[GUARD:%.*]], label [[JOIN:%.*]]
+; CHECK:       guard:
+; CHECK-NEXT:    [[GA:%.*]] = icmp ult i32 [[A:%.*]], 16
+; CHECK-NEXT:    br i1 [[GA]], label [[JOIN]], label [[ELSE:%.*]]
+; CHECK:       join:
+; CHECK-NEXT:    [[M:%.*]] = call i32 @llvm.smin.i32(i32 [[A]], i32 [[B:%.*]])
+; CHECK-NEXT:    [[R:%.*]] = icmp sgt i32 [[M]], 15
+; CHECK-NEXT:    ret i1 [[R]]
+; CHECK:       else:
+; CHECK-NEXT:    ret i1 false
+;
+entry:
+  br i1 %cc, label %guard, label %join
+guard:
+  %ga = icmp ult i32 %a, 16
+  br i1 %ga, label %join, label %else
+join:
+  %m = call i32 @llvm.smin.i32(i32 %a, i32 %b)
+  %r = icmp sgt i32 %m, 15
+  ret i1 %r
+else:
+  ret i1 false
+}
diff --git a/llvm/test/Transforms/LoopVectorize/induction.ll b/llvm/test/Transforms/LoopVectorize/induction.ll
index f2ede41effb54..adfad0f65610a 100644
--- a/llvm/test/Transforms/LoopVectorize/induction.ll
+++ b/llvm/test/Transforms/LoopVectorize/induction.ll
@@ -1442,7 +1442,7 @@ define void @scalarize_induction_variable_03(ptr %p, i32 %y, i64 %n) {
 ; INTERLEAVE-NEXT:    [[N_MOD_VF:%.*]] = and i64 [[N]], 7
 ; INTERLEAVE-NEXT:    [[TMP0:%.*]] = icmp eq i64 [[N_MOD_VF]], 0
 ; INTERLEAVE-NEXT:    [[TMP1:%.*]] = select i1 [[TMP0]], i64 8, i64 [[N_MOD_VF]]
-; INTERLEAVE-NEXT:    [[N_VEC:%.*]] = sub nsw i64 [[N]], [[TMP1]]
+; INTERLEAVE-NEXT:    [[N_VEC:%.*]] = sub nuw nsw i64 [[N]], [[TMP1]]
 ; INTERLEAVE-NEXT:    [[BROADCAST_SPLATINSERT:%.*]] = insertelement <4 x i32> poison, i32 [[Y:%.*]], i64 0
 ; INTERLEAVE-NEXT:    [[BROADCAST_SPLAT:%.*]] = shufflevector <4 x i32> [[BROADCAST_SPLATINSERT]], <4 x i32> poison, <4 x i32> zeroinitializer
 ; INTERLEAVE-NEXT:    br label [[VECTOR_BODY:%.*]]
diff --git a/llvm/test/Transforms/LoopVectorize/interleaved-accesses-2.ll b/llvm/test/Transforms/LoopVectorize/interleaved-accesses-2.ll
index 04d07cfdecd1b..9f22302c2347b 100644
--- a/llvm/test/Transforms/LoopVectorize/interleaved-accesses-2.ll
+++ b/llvm/test/Transforms/LoopVectorize/interleaved-accesses-2.ll
@@ -35,7 +35,7 @@ define void @_Z4testPfS_m(ptr noalias nocapture %out, ptr noalias nocapture read
 ; CHECK-NEXT:    [[N_MOD_VF:%.*]] = and i64 [[SIZE]], 3
 ; CHECK-NEXT:    [[TMP0:%.*]] = icmp eq i64 [[N_MOD_VF]], 0
 ; CHECK-NEXT:    [[TMP1:%.*]] = select i1 [[TMP0]], i64 4, i64 [[N_MOD_VF]]
-; CHECK-NEXT:    [[N_VEC:%.*]] = sub i64 [[SIZE]], [[TMP1]]
+; CHECK-NEXT:    [[N_VEC:%.*]] = sub nuw i64 [[SIZE]], [[TMP1]]
 ; CHECK-NEXT:    br label %[[VECTOR_BODY:.*]]
 ; CHECK:       [[VECTOR_BODY]]:
 ; CHECK-NEXT:    [[INDEX:%.*]] = phi i64 [ 0, %[[VECTOR_PH]] ], [ [[INDEX_NEXT:%.*]], %[[VECTOR_BODY]] ]
diff --git a/llvm/test/Transforms/LoopVectorize/interleaved-accesses.ll b/llvm/test/Transforms/LoopVectorize/interleaved-accesses.ll
index 1a97a87d2edfa..047688e5420c8 100644
--- a/llvm/test/Transforms/LoopVectorize/interleaved-accesses.ll
+++ b/llvm/test/Transforms/LoopVectorize/interleaved-accesses.ll
@@ -863,7 +863,7 @@ define void @PR27626_0(ptr %p, i32 %z, i64 %n) {
 ; CHECK-NEXT:    [[N_MOD_VF:%.*]] = and i64 [[N]], 3
 ; CHECK-NEXT:    [[TMP0:%.*]] = icmp eq i64 [[N_MOD_VF]], 0
 ; CHECK-NEXT:    [[TMP1:%.*]] = select i1 [[TMP0]], i64 4, i64 [[N_MOD_VF]]
-; CHECK-NEXT:    [[N_VEC:%.*]] = sub nsw i64 [[N]], [[TMP1]]
+; CHECK-NEXT:    [[N_VEC:%.*]] = sub nuw nsw i64 [[N]], [[TMP1]]
 ; CHECK-NEXT:    br label [[VECTOR_BODY:%.*]]
 ; CHECK:       vector.body:
 ; CHECK-NEXT:    [[INDEX:%.*]] = phi i64 [ 0, [[VECTOR_PH]] ], [ [[INDEX_NEXT:%.*]], [[VECTOR_BODY]] ]
@@ -955,7 +955,7 @@ define i32 @PR27626_1(ptr %p, i64 %n) {
 ; CHECK-NEXT:    [[N_MOD_VF:%.*]] = and i64 [[N]], 3
 ; CHECK-NEXT:    [[TMP0:%.*]] = icmp eq i64 [[N_MOD_VF]], 0
 ; CHECK-NEXT:    [[TMP1:%.*]] = select i1 [[TMP0]], i64 4, i64 [[N_MOD_VF]]
-; CHECK-NEXT:    [[N_VEC:%.*]] = sub nsw i64 [[N]], [[TMP1]]
+; CHECK-NEXT:    [[N_VEC:%.*]] = sub nuw nsw i64 [[N]], [[TMP1]]
 ; CHECK-NEXT:    br label [[VECTOR_BODY:%.*]]
 ; CHECK:       vector.body:
 ; CHECK-NEXT:    [[INDEX:%.*]] = phi i64 [ 0, [[VECTOR_PH]] ], [ [[INDEX_NEXT:%.*]], [[VECTOR_BODY]] ]
@@ -1047,7 +1047,7 @@ define void @PR27626_2(ptr %p, i64 %n, i32 %z) {
 ; CHECK-NEXT:    [[N_MOD_VF:%.*]] = and i64 [[N]], 3
 ; CHECK-NEXT:    [[TMP0:%.*]] = icmp eq i64 [[N_MOD_VF]], 0
 ; CHECK-NEXT:    [[TMP1:%.*]] = select i1 [[TMP0]], i64 4, i64 [[N_MOD_VF]]
-; CHECK-NEXT:    [[N_VEC:%.*]] = sub nsw i64 [[N]], [[TMP1]]
+; CHECK-NEXT:    [[N_VEC:%.*]] = sub nuw nsw i64 [[N]], [[TMP1]]
 ; CHECK-NEXT:    br label [[VECTOR_BODY:%.*]]
 ; CHECK:       vector.body:
 ; CHECK-NEXT:    [[INDEX:%.*]] = phi i64 [ 0, [[VECTOR_PH]] ], [ [[INDEX_NEXT:%.*]], [[VECTOR_BODY]] ]
@@ -1143,7 +1143,7 @@ define i32 @PR27626_3(ptr %p, i64 %n, i32 %z) {
 ; CHECK-NEXT:    [[N_MOD_VF:%.*]] = and i64 [[N]], 3
 ; CHECK-NEXT:    [[TMP0:%.*]] = icmp eq i64 [[N_MOD_VF]], 0
 ; CHECK-NEXT:    [[TMP1:%.*]] = select i1 [[TMP0]], i64 4, i64 [[N_MOD_VF]]
-; CHECK-NEXT:    [[N_VEC:%.*]] = sub nsw i64 [[N]], [[TMP1]]
+; CHECK-NEXT:    [[N_VEC:%.*]] = sub nuw nsw i64 [[N]], [[TMP1]]
 ; CHECK-NEXT:    br label [[VECTOR_BODY:%.*]]
 ; CHECK:       vector.body:
 ; CHECK-NEXT:    [[INDEX:%.*]] = phi i64 [ 0, [[VECTOR_PH]] ], [ [[INDEX_NEXT:%.*]], [[VECTOR_BODY]] ]
diff --git a/llvm/test/Transforms/PhaseOrdering/AArch64/hoist-load-from-vector-loop.ll b/llvm/test/Transforms/PhaseOrdering/AArch64/hoist-load-from-vector-loop.ll
index b0c163737c206..5372dcef2b69e 100644
--- a/llvm/test/Transforms/PhaseOrdering/AArch64/hoist-load-from-vector-loop.ll
+++ b/llvm/test/Transforms/PhaseOrdering/AArch64/hoist-load-from-vector-loop.ll
@@ -27,7 +27,7 @@ define void @hoist_invariant_load(ptr %invariant_ptr, i64 %num_elements, ptr %ar
 ; CHECK-NEXT:    [[N_MOD_VF:%.*]] = and i64 [[NUM_ELEMENTS]], 3
 ; CHECK-NEXT:    [[TMP2:%.*]] = icmp eq i64 [[N_MOD_VF]], 0
 ; CHECK-NEXT:    [[TMP3:%.*]] = select i1 [[TMP2]], i64 4, i64 [[N_MOD_VF]]
-; CHECK-NEXT:    [[N_VEC:%.*]] = sub i64 [[NUM_ELEMENTS]], [[TMP3]]
+; CHECK-NEXT:    [[N_VEC:%.*]] = sub nuw i64 [[NUM_ELEMENTS]], [[TMP3]]
 ; CHECK-NEXT:    [[TMP4:%.*]] = load double, ptr [[INVARIANT_PTR]], align 8, !alias.scope [[META0:![0-9]+]]
 ; CHECK-NEXT:    [[BROADCAST_SPLATINSERT:%.*]] = insertelement <2 x double> poison, double [[TMP4]], i64 0
 ; CHECK-NEXT:    [[BROADCAST_SPLAT:%.*]] = shufflevector <2 x double> [[BROADCAST_SPLATINSERT]], <2 x double> poison, <2 x i32> zeroinitializer
diff --git a/llvm/unittests/Analysis/ValueTrackingTest.cpp b/llvm/unittests/Analysis/ValueTrackingTest.cpp
index 382cf0f2b57ed..0acb3287a762f 100644
--- a/llvm/unittests/Analysis/ValueTrackingTest.cpp
+++ b/llvm/unittests/Analysis/ValueTrackingTest.cpp
@@ -9,6 +9,7 @@
 #include "llvm/Analysis/ValueTracking.h"
 #include "llvm/ADT/FloatingPointMode.h"
 #include "llvm/Analysis/AssumptionCache.h"
+#include "llvm/Analysis/DomConditionCache.h"
 #include "llvm/Analysis/FloatingPointPredicateUtils.h"
 #include "llvm/AsmParser/Parser.h"
 #include "llvm/IR/ConstantRange.h"
@@ -3856,6 +3857,43 @@ TEST_F(ValueTrackingTest, ComputeConstantRange) {
     // If we don't know the value of x.2, we don't know the value of x.1.
     EXPECT_TRUE(CR1.isFullSet());
   }
+  {
+    // Dominating condition:
+    //  * x < 42 on the taken edge
+    //
+    // x = [0, 42)
+    auto M = parseModule(R"(
+  define i32 @test(i32 %x) {
+  entry:
+    %c = icmp ult i32 %x, 42
+    br i1 %c, label %taken, label %exit
+  taken:
+    %x.plus.one = add nsw nuw i32 %x, 1
+    ret i32 %x.plus.one
+  exit:
+    ret i32 0
+  })");
+    Function *F = M->getFunction("test");
+
+    DominatorTree DT(*F);
+    DomConditionCache DC;
+    DC.registerBranch(cast<CondBrInst>(F->getEntryBlock().getTerminator()));
+    Value *X = &*F->arg_begin();
+
+    // Without a context instruction the branch does not apply.
+    SimplifyQuery SQ(M->getDataLayout(), /*TLI=*/nullptr, &DT, /*AC=*/nullptr,
+                     /*CXTI=*/nullptr, /*UseInstrInfo=*/true,
+                     /*CanUseUndef=*/true, &DC);
+    ConstantRange CR1 = computeConstantRange(X, /*ForSigned=*/false, SQ);
+    EXPECT_TRUE(CR1.isFullSet());
+
+    // At an instruction dominated by the taken edge, x is known to be [0, 42).
+    Instruction *I = &findInstructionByName(F, "x.plus.one");
+    ConstantRange CR2 =
+        computeConstantRange(X, /*ForSigned=*/false, SQ.getWithInstruction(I));
+    EXPECT_EQ(0, CR2.getLower());
+    EXPECT_EQ(42, CR2.getUpper());
+  }
 }
 
 struct FindAllocaForValueTestParams {

``````````

</details>


https://github.com/llvm/llvm-project/pull/210412


More information about the llvm-commits mailing list