[llvm] [ValueTracking] Refine known bits for linear interpolation patterns (PR #166378)

Valeriy Savchenko via llvm-commits llvm-commits at lists.llvm.org
Tue Nov 4 09:58:36 PST 2025


================
@@ -350,6 +350,140 @@ unsigned llvm::ComputeMaxSignificantBits(const Value *V, const DataLayout &DL,
   return V->getType()->getScalarSizeInBits() - SignBits + 1;
 }
 
+// Try to detect the lerp pattern: a * (b - c) + c * d
+// where a >= 0, b >= 0, c >= 0, d >= 0, and b >= c.
+//
+// In that particular case, we can use the following chain of reasoning:
+//
+//   a * (b - c) + c * d <= a' * (b - c) + a' * c = a' * b where a' = max(a, d)
+//
+// Since that is true for arbitrary a, b, c and d within our constraints, we can
+// conclude that:
+//
+//   max(a * (b - c) + c * d) <= max(max(a), max(d)) * max(b) = U
+//
+// Considering that any result of the lerp would be less or equal to U, it would
+// have at least the number of leading 0s as in U.
+//
+// While being quite a specific situation, it is fairly common in computer
+// graphics in the shape of alpha blending.
+//
+// Returns unknown bits if the pattern doesn't match or constraints don't apply
+// to the given operands.
+static KnownBits computeKnownBitsFromLerpPattern(const Value *Op0,
+                                                 const Value *Op1,
+                                                 const APInt &DemandedElts,
+                                                 const SimplifyQuery &Q,
+                                                 unsigned Depth) {
+
+  Type *Ty = Op0->getType();
+  const unsigned BitWidth = Ty->getScalarSizeInBits();
+
+  KnownBits Result(BitWidth);
+
+  // Only handle scalar types for now
+  if (Ty->isVectorTy())
+    return Result;
+
+  // Try to match: a * (b - c) + c * d.
+  // When a == 1 => A == nullptr, the same applies to d/D as well.
+  const Value *A = nullptr, *B = nullptr, *C = nullptr, *D = nullptr;
+
+  const auto MatchSubBC = [&]() {
+    // (b - c) can have two forms that interest us:
+    //
+    //   1. sub nuw %b, %c
+    //   2. xor %c, %b
+    //
+    // For the first case, nuw flag guarantees our requirement b >= c.
+    //
+    // The second case happens when the analysis can infer that b is a mask for
+    // c and we can transform sub operation into xor (that is usually true for
+    // constant b's). Even though xor is symmetrical, canonicalization ensures
+    // that the constant will be the RHS. xor of two positive integers is
+    // guaranteed to be non-negative as well.
+    return m_CombineOr(m_NUWSub(m_Value(B), m_Value(C)),
+                       m_Xor(m_Value(C), m_Value(B)));
+  };
----------------
SavchenkoValeriy wrote:

While I agree with this sentiment about the patches in general, I believe that this patch is quite small (effectively ~50 LoC) and general. Ripping it apart to support individual sub-cases feels like going out of the way just to follow that principle.

For example, subtraction vs exclusive or will affect literally this line alone and remove just that line of code.

If you insist, of course I can do it, but I seriously don't see a good reason for that  especially considering that the proof works in a more general case.

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


More information about the llvm-commits mailing list