[InstCombine] Missing copysign idiom fold for X - 1 when X is positive
- Dominant language
- LLVM
- Stars
- 40.5k
- Forks
- 18.7k
- PR merge metrics
- PR metrics pending
Description
InstCombine recognizes the following bit-manipulation idiom when `%mag` is nonnegative:
```text
bitcast(or(and(bitcast(x), SignMask), mag))
```
Because the sign bit of `%mag` is clear, the result copies the sign of `%x` onto the magnitude represented by `%mag`. InstCombine can therefore replace the expression with `llvm.copysign`.
For the following LLVM IR:
```llvm
define float @src(float %x, i32 %magx) {
entry:
%mag = add i32 %magx, -1
%positive = icmp sgt i32 %magx, 0
call void @llvm.assume(i1 %positive)
%bits = bitcast float %x to i32
%sign = and i32 %bits, -2147483648
%res = or i32 %mag, %sign
%y = bitcast i32 %res to float
ret float %y
}
```
On current LLVM trunk, `opt -O3` infers `nsw` on the subtraction but keeps the bit-manipulation idiom (Godbolt: https://godbolt.org/z/KhPq4feea):
```llvm
define float @src(float %x, i32 %magx) {
entry:
%mag = add nsw i32 %magx, -1
%positive = icmp sgt i32 %magx, 0
call void @llvm.assume(i1 %positive)
%bits = bitcast float %x to i32
%sign = and i32 %bits, -2147483648
%res = or i32 %mag, %sign
%y = bitcast i32 %res to float
ret float %y
}
```
If nonnegativity is instead stated on `%mag`, as the input:
```llvm
define float @copysign_idiom_commuted(float %x, i32 %magx) {
%mag = add i32 %magx, -1
%cond = icmp sgt i32 %mag, -1
call void @llvm.assume(i1 %cond)
%bits = bitcast float %x to i32
%sign = and i32 %bits, -2147483648
%res = or i32 %mag, %sign
%y = bitcast i32 %res to float
ret float %y
}
```
O3 then recognizes the copysign idiom:
```llvm
define float @copysign_idiom_commuted(float %x, i32 %magx) {
%mag = add i32 %magx, -1
%1 = bitcast i32 %mag to float
%y = tail call float @llvm.copysign.f32(float %1, float %x)
ret float %y
}
```
The condition `%magx >s 0` implies that `%magx` is at least `1`, so `%mag = %magx - 1` is nonnegative and the same copysign rewrite is valid (alive2: https://alive2.llvm.org/ce/z/QWeNKE).
`foldCopySignIdioms()` calls `isKnownNonNegative(%mag, SQ)`, but the known bits of `%magx` lose the nonzero part of the positive input assumption, so the subtraction's sign remains unknown. In `isKnownPositive()`, LLVM already combines known nonnegativity with a separate `isKnownNonZero()` query to prove something is positive. Thus a possible fix is to use the same pattern in `computeKnownBitsAddSub()` to prove `X - 1` nonnegative when `X` is positive.
Contributor guide
Research direction
Read foldCopySignIdioms() and computeKnownBitsAddSub(), then compare the nonnegativity and nonzero reasoning in isKnownPositive(). Reproduce both IR cases with opt -O3 and verify that the positive-input form folds to llvm.copysign while the existing commuted case remains correctly handled.
Written by the indexing model from the issue text.
Assessment
- Domain
- compilers
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 68/100