llvm / llvm/llvm-project

[ValueTracking] `computeKnownFPClass` incorrectly infers !+0.0 for `fadd nsz x, x`

Open
#185,920 2 comments 0 reactions 0 assignees View on GitHub
floating-point llvm:analysis miscompilation
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

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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.