[ValueTracking] `computeKnownFPClass` incorrectly infers !+0.0 for `fadd nsz x, x`
- Dominant language
- LLVM
- Stars
- 40.5k
- Forks
- 18.7k
- PR merge metrics
- PR metrics pending
Description
This is a bug when reviewing https://github.com/llvm/llvm-project/pull/174123 and https://github.com/llvm/llvm-project/pull/174569:
```llvm
define float @src_fadd_double_known_negative_zero_nsz(float noundef nofpclass(ninf pzero sub nnorm) %arg) {
%add = fadd nsz float %arg, %arg
ret float %add
}
```
Compiler Explorer: https://godbolt.org/z/E7539xa4n
Alive2 proof: https://alive2.llvm.org/ce/z/AnJmK5
> Note: This is a review assisted with a self-built agent. The reproducer was validated manually. Please let me know if anything is wrong.
**Bug Triggering Analysis:**
The provided test case `fadd_double_known_negative_zero_nsz` triggers the bug because it uses the `nsz` (no signed zeros) fast-math flag on the `fadd` instruction. The input `%arg` is constrained by `nofpclass(ninf pzero sub nnorm)`, which means it cannot be `-Inf`, `+0.0`, subnormal, or negative normal. The only possible values are `-0.0`, `+Inf`, `NaN`, or positive normal. If `%arg` is `-0.0`, the `fadd nsz` instruction computes `-0.0 + -0.0`. According to IEEE 754, this is `-0.0`. However, the `nsz` flag allows the compiler to treat `-0.0` as `+0.0`, so the result can legally be `+0.0`.
**Fix Weakness Analysis:**
The fix introduced in `computeKnownFPClass` for `fadd` checks if the operation is a self-add (`fadd x, x`). If it is, and the input `x` is known never to be logical positive zero (`KnownRHS.isKnownNeverLogicalPosZero(Mode)`), it deduces that the result cannot be positive zero (`Known.knownNot(fcPosZero)`). This logic is correct for strict IEEE 754 arithmetic. However, it fails to account for the `nsz` fast-math flag. When `nsz` is present, the result of `-0.0 + -0.0` can be `+0.0`. By unconditionally setting `knownNot(fcPosZero)`, the compiler incorrectly assumes the result cannot be `+0.0`, leading to a contradiction where the actual result is `+0.0` but the compiler assumes it is not. This causes Alive2 to report a verification failure ("Source is more defined than target") because the target (optimized) IR triggers Undefined Behavior when returning `+0.0` while claiming it cannot be `+0.0` via the `nofpclass` attribute.
Contributor guide
Research direction
Start at the ValueTracking computeKnownFPClass entry point and reproduce the supplied LLVM IR case with the linked Alive2 proof. Trace the fadd x, x handling when nsz is present, then add coverage for the reproducer; done means the inferred floating-point class no longer contradicts the allowed +0.0 result and the relevant tests pass.
Written by the indexing model from the issue text.
Assessment
- Domain
- compilers
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 42/100