[llvm] [ValueTracking] Refine known bits for linear interpolation patterns (PR #166378)
Valeriy Savchenko via llvm-commits
llvm-commits at lists.llvm.org
Wed Nov 5 10:51:42 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:
That is a great catch, thank you!
As a matter of fact, we can't even make this conclusion for `xor` and a constraint b >= c: https://gist.github.com/SavchenkoValeriy/fff5f7d58daf8f7d2c5b2eb955fbbf89
We have to match exactly the situation when we replace `sub` with `xor` -- when `b` is a mask for `c`. Basically, we need to check if b == known zeros of c. The proof is here: https://gist.github.com/SavchenkoValeriy/6d3d0da4cc68b6d7f6a4ef97d6502c9d
https://github.com/llvm/llvm-project/pull/166378
More information about the llvm-commits
mailing list