[InstSimplify] Missed optimization: fsub x, x -> 0 does not use computeKnownFPClass-proven no-NaN facts
- Dominant language
- LLVM
- Stars
- 40.5k
- Forks
- 18.7k
- PR merge metrics
- PR metrics pending
Description
InstSimplify folds:
```llvm ir
%d = fsub nnan float %x, %x
```
to 0.0, but the fold only checks explicit fast-math flags. In some cases LLVM can already prove the relevant no-NaN fact through `computeKnownFPClass`, but the fsub x, x -> 0.0 simplifier does not consume that implicit fact.
Illustrative example:
```llvm ir
; opt -S -passes=instcombine repro.ll
declare i1 @llvm.is.fpclass.f32(float, i32 immarg)
declare double @llvm.fmuladd.f64(double, double, double)
define i1 @known_nan_query(float nofpclass(nan inf) %x) {
entry:
%d = fsub float %x, %x
%isnan = call i1 @llvm.is.fpclass.f32(float %d, i32 3) ; qnan|snan
ret i1 %isnan
}
```
Observed behavior:
```llvm ir
define i1 @known_nan_query(float nofpclass(nan inf) %x) {
entry:
ret i1 false
}
```
So LLVM can prove the result of `%d = fsub %x, %x` is not NaN in this context.
But @missed_selfsub_consumer keeps the chain not touched (https://godbolt.org/z/zPdsncvzn):
```llvm ir
define double @missed_selfsub_consumer(
float nofpclass(nan inf) %x, double %scale, double %acc) {
entry:
%d = fsub float %x, %x
%de = fpext float %d to double
%mul = fmul double %scale, %de
%fma = call double @llvm.fmuladd.f64(double %mul, double %de, double %acc)
ret double %fma
}
```
Only if I add explicit `nnan` to `fsub`:
```llvm ir
define double @missed_selfsub_consumer(
float nofpclass(nan inf) %x, double %scale, double %acc) {
entry:
%d = fsub nnan float %x, %x
%de = fpext float %d to double
%mul = fmul double %scale, %de
%fma = call double @llvm.fmuladd.f64(double %mul, double %de, double %acc)
ret double %fma
}
```
It could be reduced as (https://godbolt.org/z/j5sEnTs7n):
```llvm ir
define double @missed_selfsub_consumer(
float nofpclass(nan inf) %x, double %scale, double %acc) {
entry:
%mul = fmul double %scale, 0.000000e+00
%fma = tail call double @llvm.fmuladd.f64(double %mul, double 0.000000e+00, double %acc)
ret double %fma
}
```
The root cause seems to be here:
```c++
// llvm/lib/Analysis/InstructionSimplify.cpp
if (FMF.noNaNs()) {
// fsub nnan x, x ==> 0.0
if (Op0 == Op1)
return Constant::getNullValue(Op0->getType());
}
```
This was observed in SPEC CPU 2017 538.imagick_r, `magick/enhance.c`, `EnhanceImage`. The source computes a 5x5 weighted neighborhood. The center tap copies `pixel = *r`, later reaches the same center pointer again, and the `Enhance(80.0)` macro computes per-channel differences of the form `component - same component`, which should be wrapped into 0.0 but actually not.
Contributor guide
Assessment
This issue has not been assessed yet.