[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