[llvm] [InstCombine] Don't propagate ninf when hoisting fneg above fmul/fdiv (PR #228671)
via llvm-commits
llvm-commits at lists.llvm.org
Sat Oct 3 00:15:48 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-llvm-transforms
Author: Divyansh Yadav (schizophrenicmaniac)
<details>
<summary>Changes</summary>
hoistFNegAboveFMulFDiv turns -(X * Y) into X * (-Y) and -(X / Y) into (-X) / Y, and it copied all of the fneg's flags onto both new instructions. ninf on the fneg only tells us the product/quotient isn't infinite. It says nothing about the operands: -(inf * nan) is just a nan, but `fmul ninf inf, -nan` is poison.
This keeps ninf only when the original fmul/fdiv has it too, same as foldFNegIntoConstant already does for the constant cases. Other flags still come from the fneg.
Alive2 for the reproducers from the issue (both fail before this patch, as shown in the issue).
Fixes #<!-- -->228557
---
Full diff: https://github.com/llvm/llvm-project/pull/228671.diff
2 Files Affected:
- (modified) llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp (+14-4)
- (modified) llvm/test/Transforms/InstCombine/fneg.ll (+69)
``````````diff
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp b/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp
index 281e3b353f6dd..f676dddf5b2ac 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp
@@ -3168,17 +3168,27 @@ static Instruction *foldFNegIntoConstant(Instruction &I, const DataLayout &DL) {
Instruction *InstCombinerImpl::hoistFNegAboveFMulFDiv(Value *FNegOp,
Instruction &FMFSource) {
+ // ninf on the fneg does not apply to the operands of the fmul/fdiv, e.g.
+ // -(inf * nan) is not inf, so only keep it if the fmul/fdiv has it too.
+ auto GetFMF = [&]() {
+ FastMathFlags FMF = FMFSource.getFastMathFlags();
+ FMF.setNoInfs(FMF.noInfs() && cast<Instruction>(FNegOp)->hasNoInfs());
+ return FMF;
+ };
+
Value *X, *Y;
if (match(FNegOp, m_FMul(m_Value(X), m_Value(Y)))) {
// Push into RHS which is more likely to simplify (const or another fneg).
// FIXME: It would be better to invert the transform.
- return cast<Instruction>(Builder.CreateFMulFMF(
- X, Builder.CreateFNegFMF(Y, &FMFSource), &FMFSource));
+ FastMathFlags FMF = GetFMF();
+ return cast<Instruction>(
+ Builder.CreateFMulFMF(X, Builder.CreateFNegFMF(Y, FMF), FMF));
}
if (match(FNegOp, m_FDiv(m_Value(X), m_Value(Y)))) {
- auto *FDiv = cast<Instruction>(Builder.CreateFDivFMF(
- Builder.CreateFNegFMF(X, &FMFSource), Y, &FMFSource));
+ FastMathFlags FMF = GetFMF();
+ auto *FDiv = cast<Instruction>(
+ Builder.CreateFDivFMF(Builder.CreateFNegFMF(X, FMF), Y, FMF));
FDiv->copyMetadata(*cast<Instruction>(FNegOp));
return FDiv;
}
diff --git a/llvm/test/Transforms/InstCombine/fneg.ll b/llvm/test/Transforms/InstCombine/fneg.ll
index d863871eb3fc8..43fbbe5382f3c 100644
--- a/llvm/test/Transforms/InstCombine/fneg.ll
+++ b/llvm/test/Transforms/InstCombine/fneg.ll
@@ -122,6 +122,75 @@ define float @fdiv_fneg_fpmath(float %x, float %y) {
ret float %w
}
+; ninf on the fneg can only be kept if the fmul/fdiv has it too:
+; -(inf * nan) is nan, but fmul ninf inf, -nan is poison.
+
+define float @fmul_fneg_ninf(float %x, float %y) {
+; CHECK-LABEL: @fmul_fneg_ninf(
+; CHECK-NEXT: [[TMP1:%.*]] = fneg float [[Y:%.*]]
+; CHECK-NEXT: [[R:%.*]] = fmul float [[X:%.*]], [[TMP1]]
+; CHECK-NEXT: ret float [[R]]
+;
+ %m = fmul float %x, %y
+ %r = fneg ninf float %m
+ ret float %r
+}
+
+define float @fmul_ninf_fneg_ninf(float %x, float %y) {
+; CHECK-LABEL: @fmul_ninf_fneg_ninf(
+; CHECK-NEXT: [[TMP1:%.*]] = fneg ninf float [[Y:%.*]]
+; CHECK-NEXT: [[R:%.*]] = fmul ninf float [[X:%.*]], [[TMP1]]
+; CHECK-NEXT: ret float [[R]]
+;
+ %m = fmul ninf float %x, %y
+ %r = fneg ninf float %m
+ ret float %r
+}
+
+define float @fmul_fneg_nnan_ninf(float %x, float %y) {
+; CHECK-LABEL: @fmul_fneg_nnan_ninf(
+; CHECK-NEXT: [[TMP1:%.*]] = fneg nnan float [[Y:%.*]]
+; CHECK-NEXT: [[R:%.*]] = fmul nnan float [[X:%.*]], [[TMP1]]
+; CHECK-NEXT: ret float [[R]]
+;
+ %m = fmul float %x, %y
+ %r = fneg nnan ninf float %m
+ ret float %r
+}
+
+define float @fdiv_fneg_ninf(float %x, float %y) {
+; CHECK-LABEL: @fdiv_fneg_ninf(
+; CHECK-NEXT: [[TMP1:%.*]] = fneg float [[X:%.*]]
+; CHECK-NEXT: [[R:%.*]] = fdiv float [[TMP1]], [[Y:%.*]]
+; CHECK-NEXT: ret float [[R]]
+;
+ %d = fdiv float %x, %y
+ %r = fneg ninf float %d
+ ret float %r
+}
+
+define float @fdiv_ninf_fneg_ninf(float %x, float %y) {
+; CHECK-LABEL: @fdiv_ninf_fneg_ninf(
+; CHECK-NEXT: [[TMP1:%.*]] = fneg ninf float [[X:%.*]]
+; CHECK-NEXT: [[R:%.*]] = fdiv ninf float [[TMP1]], [[Y:%.*]]
+; CHECK-NEXT: ret float [[R]]
+;
+ %d = fdiv ninf float %x, %y
+ %r = fneg ninf float %d
+ ret float %r
+}
+
+define float @fdiv_fneg_nnan_ninf(float %x, float %y) {
+; CHECK-LABEL: @fdiv_fneg_nnan_ninf(
+; CHECK-NEXT: [[TMP1:%.*]] = fneg nnan float [[X:%.*]]
+; CHECK-NEXT: [[R:%.*]] = fdiv nnan float [[TMP1]], [[Y:%.*]]
+; CHECK-NEXT: ret float [[R]]
+;
+ %d = fdiv float %x, %y
+ %r = fneg nnan ninf float %d
+ ret float %r
+}
+
; -(X / C) --> X / (-C)
define float @fdiv_op1_constant_fsub(float %x) {
``````````
</details>
https://github.com/llvm/llvm-project/pull/228671
More information about the llvm-commits
mailing list