[llvm] [LVI] Merge ranges reliably and precisely (PR #173714)

Yingwei Zheng via llvm-commits llvm-commits at lists.llvm.org
Thu Jan 1 04:03:44 PST 2026


================
@@ -717,6 +720,250 @@ bool LazyValueInfoImpl::isNonNullAtEndOfBlock(Value *Val, BasicBlock *BB) {
   });
 }
 
+/// RAII helper for merging ValueLatticeElement into \p Result with a stable and
+/// precise ConstantRange.
+///
+/// Instead of incrementally unioning ConstantRanges (which is order-dependent
+/// and may over-approximate), this merger *collects* intervals and defers the
+/// actual union to destruction, where \c ConstantRange::unionOf is used to
+/// compute a deterministic and most-accurate result.
+///
+/// Example:
+///   Incremental merge:
+///     [0,1) \/ [2,3) \/ [3,0)  -> overdefined (order-dependent)
+///   Collected + unionOf:
+///     {[0,1), [2,3), [3,0)}    -> exact [2,1)
+///
+/// The merger updates \p Result eagerly (without deferring to destruction) in
+/// the following cases:
+///  (1) Collected ranges already form the full set exactly, in which case
+///      \p Result is immediately marked overdefined.
+///  (2) \p RHS cannot be represented as a ConstantRange, so it is merged
+///      directly via \c Result.mergeIn(RHS).
+///  (3) Merging a ConstantRange with a \c constant or \c notconstant lattice
+///      state, which by definition yields overdefined.
+///
+/// Wrapped ConstantRanges are split during collection; the final union is
+/// reconstructed reliably by \c ConstantRange::unionOf.
+class ValueLatticeMerger {
+  using MergeOptions = ValueLatticeElement::MergeOptions;
+
+public:
+  ValueLatticeMerger(ValueLatticeElement &Result) : Result(Result) {
+    // Try to collect some ranges from initial Result
+    InitialResultAsCR = tryToInsertRange(Result);
+  }
+  ~ValueLatticeMerger() {
+    assert((InitialResultAsCR || !Result.isConstantRange()) &&
+           "Result should not be constantrange without merging any constrange");
+    if (Result.isOverdefined() || Intervals.empty())
+      return;
+    RangeIncludingUndef |= Result.isUndef();
+    SmallVector<ConstantRange, 8> Ranges;
+    Ranges.reserve(Intervals.size());
+    for (const auto &[L, U] : Intervals)
+      Ranges.emplace_back(L, U);
+    // Get the most accurate approximation reliably
+    ConstantRange UnionCR = ConstantRange::unionOf(Ranges);
+    Result.markConstantRange(
+        std::move(UnionCR),
+        MergeOptions().setMayIncludeUndef(RangeIncludingUndef));
+  }
+  void mergeIn(const ValueLatticeElement &RHS) {
+    if (Result.isOverdefined())
+      return;
+    if (tryToInsertRange(RHS))
+      return;
+
+    // [constant | notconstant] \/ constantrange = overdefined
+    if (!Intervals.empty() && (RHS.isConstant() || RHS.isNotConstant())) {
+      Result.markOverdefined();
+      return;
+    }
+
+    // If EdgeResult is not a constant range, we can merge it directly into
+    // result, which should be precise enough.
+    Result.mergeIn(RHS);
+  }
+
+private:
+  struct APIntULTComparator {
+    bool operator()(const APInt &A, const APInt &B) const { return A.ult(B); }
+  };
+
+  // This is a precise version of Constant::toConstantRange
+  static bool collectAPInts(const Constant *C, SmallVectorImpl<APInt> &Ints) {
+    if (auto *CI = dyn_cast<ConstantInt>(C)) {
+      Ints.push_back(CI->getValue());
+      return true;
+    }
+
+    if (!C->getType()->isVectorTy())
+      return false;
+
+    if (auto *CI = dyn_cast_or_null<ConstantInt>(
+            C->getSplatValue(/*AllowPoison=*/true))) {
+      Ints.push_back(CI->getValue());
+      return true;
+    }
+
+    if (auto *CDV = dyn_cast<ConstantDataVector>(C)) {
+      unsigned NumElements = CDV->getNumElements();
+      Ints.reserve(Ints.size() + NumElements);
+      for (unsigned I = 0, E = NumElements; I < E; ++I)
+        Ints.push_back(CDV->getElementAsAPInt(I));
+      return true;
+    }
+
+    if (auto *CV = dyn_cast<ConstantVector>(C)) {
+      bool AnyNotAPIInt = false;
+      unsigned NumOperands = CV->getNumOperands();
+      Ints.reserve(Ints.size() + NumOperands);
+      for (unsigned I = 0, E = NumOperands; I < E; ++I) {
+        Constant *Elem = CV->getOperand(I);
+        if (!Elem) {
+          AnyNotAPIInt = true;
+          continue;
+        }
+        if (isa<PoisonValue>(Elem))
+          continue;
+        auto *CI = dyn_cast<ConstantInt>(Elem);
+        if (!CI) {
+          AnyNotAPIInt = true;
+          continue;
+        }
+        Ints.push_back(CI->getValue());
+      }
+      return AnyNotAPIInt;
+    }
+
+    return false;
+  }
+
+  /// Try to insert ranges extracted from constantrange / const lattice. Return
+  /// false if failed to insert.
+  bool tryToInsertRange(const ValueLatticeElement &IV) {
+    if (IV.isConstantRange()) {
+      RangeIncludingUndef |= IV.isConstantRangeIncludingUndef();
+      insertRange(IV.getConstantRange());
+      return true;
+    }
+    assert(!IV.isConstantRange() &&
+           "Constant range RHS should be handled earlier");
+    if (/*int vec*/ IV.isConstant(true)) {
+      IntsInVec.clear(); // Reuse the vector to avoid reallocations
+      if (!collectAPInts(IV.getConstant(), IntsInVec))
----------------
dtcxzyw wrote:

Is it the main contribution of compile-time overhead? I'd suggest focusing on the merge ordering issue in this patch.

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


More information about the llvm-commits mailing list