llvm / llvm/llvm-project

[InstCombine] Missing copysign idiom fold for X - 1 when X is positive

Open
#221,943 1 comment 0 reactions 0 assignees View on GitHub
llvm:instcombine missed-optimization
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

Open the contributing 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.