[llvm] [InstCombine] Don't propagate ninf when hoisting fneg above fmul/fdiv (PR #228671)
Divyansh Yadav via llvm-commits
llvm-commits at lists.llvm.org
Sat Oct 3 00:15:09 PDT 2026
https://github.com/schizophrenicmaniac created https://github.com/llvm/llvm-project/pull/228671
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
>From 34267387f875ac68b7fd3e691871ea6da6167f89 Mon Sep 17 00:00:00 2001
From: Divyansh Yadav <anshmcs at gmail.com>
Date: Sat, 3 Oct 2026 12:16:36 +0530
Subject: [PATCH] [InstCombine] Don't propagate ninf when hoisting fneg above
fmul/fdiv
---
.../InstCombine/InstCombineAddSub.cpp | 18 +++--
llvm/test/Transforms/InstCombine/fneg.ll | 69 +++++++++++++++++++
2 files changed, 83 insertions(+), 4 deletions(-)
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp b/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp
index 281e3b353f6dd2..f676dddf5b2ac3 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 d863871eb3fc8d..43fbbe5382f3cb 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) {
More information about the llvm-commits
mailing list