[llvm] [Instcombine] Fold a zero-select hexadecimal digit count into ctlz(x | 1) (PR #215073)
via llvm-commits
llvm-commits at lists.llvm.org
Sun Aug 23 13:41:28 PDT 2026
================
----------------
ParkHanbum wrote:
I wonder if we can avoid scanning X->users() here and reuse the information we already get from the first simplifyWithOpReplaced() attempt.
As I understand this fold, the existing path is effectively checking:
select (X == 0), C, F(ctlz(X, true))
by extending ctlz(X, true) to ctlz(X, false) for the X == 0 case. That gives:
ctlz(0, false) = BW
so the existing equivalence fold succeeds when:
F(BW) == C
For the new case, the only difference seems to be that F(BW) is not C, but another safe extension is possible:
ctlz(X, true) -> ctlz(X | 1, true)
This preserves the value for every X != 0, while for X == 0 it gives:
ctlz(0 | 1, true) = ctlz(1, true) = BW - 1
so the additional condition we want to test is simply:
F(BW - 1) == C
The first simplifyWithOpReplaced(FalseVal, X, 0, ..., &DropFlags) already walks FalseVal, and when evaluating the zero case it records the zero-poison ctlz(X, true) in DropFlags. Therefore, instead of doing another search through all users of X, could we reuse that information?
Conceptually, I was thinking of something like:
1. Try the existing equivalence fold:
F(BW) == TrueVal
If this succeeds, keep the existing smaller form:
ctlz(X, false)
2. If it fails, and the zero-substitution required dropping
`is_zero_poison` from a ctlz(X, true), use that ctlz as the
candidate.
3. Retry the FalseVal simplification with that ctlz result replaced
by BW - 1:
F(BW - 1) == TrueVal
4. If that succeeds, rewrite only that ctlz as:
ctlz(X | 1, true)
and remove the select.
For example:
%cmp = icmp eq i32 %x, 0
%lz = call i32 @llvm.ctlz.i32(i32 %x, i1 true)
%v = sub i32 32, %lz
%sel = select i1 %cmp, i32 1, i32 %v
The normal attempt gives:
F(32) = 32 - 32 = 0
so it does not match the true value 1.
But the fallback gives:
F(31) = 32 - 31 = 1
and ctlz(X | 1, true) is equivalent to ctlz(X, true) whenever X != 0, so this is enough to remove the select.
This seems a little more local to what foldSelectValueEquivalence() is already doing: first test the ordinary F(BW) extension, then test the alternative F(BW - 1) extension using the ctlz that was already discovered while simplifying FalseVal.
It would also avoid searching unrelated users of X, and makes the reason for the fallback fairly explicit in the implementation.
What do you think about structuring the fold this way?
https://github.com/llvm/llvm-project/pull/215073
More information about the llvm-commits
mailing list