[InstCombine] Missed fcmp simplification with FP class known from dominating branch
- Dominant language
- LLVM
- Stars
- 40.5k
- Forks
- 18.7k
- PR merge metrics
- PR metrics pending
Description
## Summary
LLVM can miss `fcmp` folds when an FP-class property is known from the dominating control-flow edge.
In the example below, reaching `nan.marker` proves that `%marker` is NaN:
```llvm
define float @fold_known_nan_oeq_scalar(float %x, float %marker,
float %replacement) {
entry:
%marker.nan = fcmp uno float %marker, 0.0
br i1 %marker.nan, label %nan.marker, label %common.ret
nan.marker:
%x.nan = fcmp uno float %x, 0.0
%x.eq.marker = fcmp oeq float %x, %marker
%missing = or i1 %x.nan, %x.eq.marker
%result = select i1 %missing, float %replacement, float %x
br label %common.ret
common.ret:
%ret = phi float [ %result, %nan.marker ], [ %x, %entry ]
ret float %ret
}
```
On the `nan.marker` edge, `%marker` is known to be NaN. Therefore:
```llvm
fcmp oeq float %x, %marker
```
must be `false`, because an ordered comparison is false if either operand is NaN.
This reduces the condition to:
```text
isnan(marker) && isnan(x)
```
and allows the CFG to simplify to:
```llvm
define float @fold_known_nan_oeq_scalar(float %x, float %marker,
float %replacement) {
entry:
%marker.nan = fcmp uno float %marker, 0.0
%x.nan = fcmp uno float %x, 0.0
%cond = and i1 %marker.nan, %x.nan
%result = select i1 %cond, float %replacement, float %x
ret float %result
}
```
The missed optimization appears to be that the path-sensitive FP-class information established by the dominating `fcmp uno` branch is not available to the later `fcmp oeq` simplification.
It may be useful for `fcmp` simplification / `computeKnownFPClass` reasoning to consume dominating branch conditions in cases like this.
This pattern originates from ONNX Runtime's ComputeByType in core/providers/cpu/ml/imputer.cc. The source checks whether both the input and replacement marker are NaN, followed by a normal equality check against the replacement marker. Since ordered equality against a value known to be NaN is necessarily false, this provides a real-world case where path-sensitive FP-class information can eliminate the comparison.
```c++
if (std::isnan(static_cast(x_data[i])) &&
std::isnan(static_cast(replaced_value))) {
y_data[i] = imputed_values[i % stride];
} else if (x_data[i] == replaced_value) {
y_data[i] = imputed_values[i % stride];
} else {
y_data[i] = x_data[i];
}
```
Contributor guide
Assessment
This issue has not been assessed yet.