llvm / llvm/llvm-project

[InstCombine] Missed fcmp simplification with FP class known from dominating branch

Open
#217,542 0 comments 0 reactions 1 assignee Claimed by @ParkHanbum View on GitHub
floating-point llvm:instcombine missed-optimization
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

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.