[llvm] [ConstantFolding] Don't fold constrained fsub/fadd to zero with round.dynamic (PR #184919)
via llvm-commits
llvm-commits at lists.llvm.org
Sat Apr 18 06:57:05 PDT 2026
https://github.com/TusharSariya updated https://github.com/llvm/llvm-project/pull/184919
>From e157adbe0520161684c995b2baf5535259037d3c Mon Sep 17 00:00:00 2001
From: Tushar Sariya <tushar.97 at hotmail.com>
Date: Thu, 5 Mar 2026 20:22:16 -0330
Subject: [PATCH 1/2] [ConstantFolding] Don't fold constrained fsub/fadd to
zero with round.dynamic
The sign of an exact zero result from fadd/fsub depends on the rounding
mode (IEEE 754, section 6.3). The constant folder was incorrectly folding
these operations when the rounding mode is dynamic, because opOK was
interpreted as "result is rounding-mode-independent" -- which is true for
the magnitude but not for the sign of zero.
Fix by passing the result to mayFoldConstrained and returning false when
the result is zero and the rounding mode is dynamic. Also remove the
unnecessary const_cast at the call site.
Fixes https://github.com/llvm/llvm-project/issues/177467
---
llvm/lib/Analysis/ConstantFolding.cpp | 23 +++++++-----
.../InstSimplify/constfold-constrained.ll | 37 +++++++++++++++++++
2 files changed, 51 insertions(+), 9 deletions(-)
diff --git a/llvm/lib/Analysis/ConstantFolding.cpp b/llvm/lib/Analysis/ConstantFolding.cpp
index 8e447b84d3149..5ba2ddc3062b6 100644
--- a/llvm/lib/Analysis/ConstantFolding.cpp
+++ b/llvm/lib/Analysis/ConstantFolding.cpp
@@ -2316,15 +2316,22 @@ static bool getConstIntOrUndef(Value *Op, const APInt *&C) {
///
/// \param CI Constrained intrinsic call.
/// \param St Exception flags raised during constant evaluation.
-static bool mayFoldConstrained(ConstrainedFPIntrinsic *CI,
- APFloat::opStatus St) {
+/// \param Res Result of constant evaluation (if available).
+static bool mayFoldConstrained(const ConstrainedFPIntrinsic *CI,
+ APFloat::opStatus St,
+ std::optional<APFloat> Res = std::nullopt) {
std::optional<RoundingMode> ORM = CI->getRoundingMode();
std::optional<fp::ExceptionBehavior> EB = CI->getExceptionBehavior();
// If the operation does not change exception status flags, it is safe
- // to fold.
- if (St == APFloat::opStatus::opOK)
+ // to fold. However, if the result is zero and the rounding mode is dynamic,
+ // the sign of zero depends on the rounding mode (IEEE 754, section 6.3),
+ // so we cannot fold.
+ if (St == APFloat::opStatus::opOK) {
+ if (Res && Res->isZero() && ORM == RoundingMode::Dynamic)
+ return false;
return true;
+ }
// If evaluation raised FP exception, the result can depend on rounding
// mode. If the latter is unknown, folding is not possible.
@@ -3170,7 +3177,7 @@ static Constant *evaluateCompare(const APFloat &Op1, const APFloat &Op2,
St = APFloat::opInvalidOp;
}
bool Result = FCmpInst::compare(Op1, Op2, Cond);
- if (mayFoldConstrained(const_cast<ConstrainedFPCmpIntrinsic *>(FCmp), St))
+ if (mayFoldConstrained(FCmp, St))
return ConstantInt::get(Call->getType()->getScalarType(), Result);
return nullptr;
}
@@ -3337,8 +3344,7 @@ static Constant *ConstantFoldIntrinsicCall2(Intrinsic::ID IntrinsicID, Type *Ty,
case Intrinsic::experimental_constrained_fcmps:
return evaluateCompare(Op1V, Op2V, ConstrIntr);
}
- if (mayFoldConstrained(const_cast<ConstrainedFPIntrinsic *>(ConstrIntr),
- St))
+ if (mayFoldConstrained(ConstrIntr, St, Res))
return ConstantFP::get(Ty, Res);
return nullptr;
}
@@ -3917,8 +3923,7 @@ static Constant *ConstantFoldScalarCall3(StringRef Name,
St = Res.fusedMultiplyAdd(C2, C3, RM);
break;
}
- if (mayFoldConstrained(
- const_cast<ConstrainedFPIntrinsic *>(ConstrIntr), St))
+ if (mayFoldConstrained(ConstrIntr, St))
return ConstantFP::get(Ty, Res);
return nullptr;
}
diff --git a/llvm/test/Transforms/InstSimplify/constfold-constrained.ll b/llvm/test/Transforms/InstSimplify/constfold-constrained.ll
index a9ef7f6a765d1..f5c7015a62534 100644
--- a/llvm/test/Transforms/InstSimplify/constfold-constrained.ll
+++ b/llvm/test/Transforms/InstSimplify/constfold-constrained.ll
@@ -535,6 +535,43 @@ entry:
}
+; The sign of 0.0 - 0.0 depends on the rounding mode (IEEE 754, section 6.3).
+; With round.dynamic, this must not be folded.
+define double @fsub_zero_zero_dynamic() #0 {
+; CHECK-LABEL: @fsub_zero_zero_dynamic(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[RESULT:%.*]] = call double @llvm.experimental.constrained.fsub.f64(double 0.000000e+00, double 0.000000e+00, metadata !"round.dynamic", metadata !"fpexcept.strict") #[[ATTR0]]
+; CHECK-NEXT: ret double [[RESULT]]
+;
+entry:
+ %result = call double @llvm.experimental.constrained.fsub.f64(double 0.0, double 0.0, metadata !"round.dynamic", metadata !"fpexcept.strict") #0
+ ret double %result
+}
+
+; With a known rounding mode, 0.0 - 0.0 can be folded.
+define double @fsub_zero_zero_tonearest() #0 {
+; CHECK-LABEL: @fsub_zero_zero_tonearest(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[RESULT:%.*]] = call double @llvm.experimental.constrained.fsub.f64(double 0.000000e+00, double 0.000000e+00, metadata !"round.tonearest", metadata !"fpexcept.strict") #[[ATTR0]]
+; CHECK-NEXT: ret double 0.000000e+00
+;
+entry:
+ %result = call double @llvm.experimental.constrained.fsub.f64(double 0.0, double 0.0, metadata !"round.tonearest", metadata !"fpexcept.strict") #0
+ ret double %result
+}
+
+; 1.0 + 1.0 = 2.0 exactly, no sign-of-zero issue, safe to fold even with round.dynamic.
+define double @fadd_one_one_dynamic() #0 {
+; CHECK-LABEL: @fadd_one_one_dynamic(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[RESULT:%.*]] = call double @llvm.experimental.constrained.fadd.f64(double 1.000000e+00, double 1.000000e+00, metadata !"round.dynamic", metadata !"fpexcept.strict") #[[ATTR0]]
+; CHECK-NEXT: ret double 2.000000e+00
+;
+entry:
+ %result = call double @llvm.experimental.constrained.fadd.f64(double 1.0, double 1.0, metadata !"round.dynamic", metadata !"fpexcept.strict") #0
+ ret double %result
+}
+
attributes #0 = { strictfp }
declare double @llvm.experimental.constrained.nearbyint.f64(double, metadata, metadata)
>From fb7ebd853f4d76feb453ccf03053d082f0039294 Mon Sep 17 00:00:00 2001
From: Tushar Sariya <tushar.97 at hotmail.com>
Date: Sat, 18 Apr 2026 11:26:52 -0230
Subject: [PATCH 2/2] address comments
---
llvm/lib/Analysis/ConstantFolding.cpp | 28 +++++++++++--------
.../InstSimplify/constfold-constrained.ll | 12 ++++----
2 files changed, 23 insertions(+), 17 deletions(-)
diff --git a/llvm/lib/Analysis/ConstantFolding.cpp b/llvm/lib/Analysis/ConstantFolding.cpp
index 5ba2ddc3062b6..411828b9f4524 100644
--- a/llvm/lib/Analysis/ConstantFolding.cpp
+++ b/llvm/lib/Analysis/ConstantFolding.cpp
@@ -2316,22 +2316,15 @@ static bool getConstIntOrUndef(Value *Op, const APInt *&C) {
///
/// \param CI Constrained intrinsic call.
/// \param St Exception flags raised during constant evaluation.
-/// \param Res Result of constant evaluation (if available).
static bool mayFoldConstrained(const ConstrainedFPIntrinsic *CI,
- APFloat::opStatus St,
- std::optional<APFloat> Res = std::nullopt) {
+ APFloat::opStatus St) {
std::optional<RoundingMode> ORM = CI->getRoundingMode();
std::optional<fp::ExceptionBehavior> EB = CI->getExceptionBehavior();
// If the operation does not change exception status flags, it is safe
- // to fold. However, if the result is zero and the rounding mode is dynamic,
- // the sign of zero depends on the rounding mode (IEEE 754, section 6.3),
- // so we cannot fold.
- if (St == APFloat::opStatus::opOK) {
- if (Res && Res->isZero() && ORM == RoundingMode::Dynamic)
- return false;
+ // to fold.
+ if (St == APFloat::opStatus::opOK)
return true;
- }
// If evaluation raised FP exception, the result can depend on rounding
// mode. If the latter is unknown, folding is not possible.
@@ -2348,6 +2341,19 @@ static bool mayFoldConstrained(const ConstrainedFPIntrinsic *CI,
return false;
}
+/// Like mayFoldConstrained, but also checks that the sign of a zero result is
+/// not rounding-mode-dependent (IEEE 754, section 6.3).
+static bool mayFoldConstrained(const ConstrainedFPIntrinsic *CI,
+ APFloat::opStatus St, const APFloat &Res) {
+ if (St == APFloat::opStatus::opOK) {
+ std::optional<RoundingMode> ORM = CI->getRoundingMode();
+ if (Res.isZero() && ORM == RoundingMode::Dynamic)
+ return false;
+ return true;
+ }
+ return mayFoldConstrained(CI, St);
+}
+
/// Returns the rounding mode that should be used for constant evaluation.
static RoundingMode
getEvaluationRoundingMode(const ConstrainedFPIntrinsic *CI) {
@@ -3923,7 +3929,7 @@ static Constant *ConstantFoldScalarCall3(StringRef Name,
St = Res.fusedMultiplyAdd(C2, C3, RM);
break;
}
- if (mayFoldConstrained(ConstrIntr, St))
+ if (mayFoldConstrained(ConstrIntr, St, Res))
return ConstantFP::get(Ty, Res);
return nullptr;
}
diff --git a/llvm/test/Transforms/InstSimplify/constfold-constrained.ll b/llvm/test/Transforms/InstSimplify/constfold-constrained.ll
index f5c7015a62534..9849777e1a38b 100644
--- a/llvm/test/Transforms/InstSimplify/constfold-constrained.ll
+++ b/llvm/test/Transforms/InstSimplify/constfold-constrained.ll
@@ -540,11 +540,11 @@ entry:
define double @fsub_zero_zero_dynamic() #0 {
; CHECK-LABEL: @fsub_zero_zero_dynamic(
; CHECK-NEXT: entry:
-; CHECK-NEXT: [[RESULT:%.*]] = call double @llvm.experimental.constrained.fsub.f64(double 0.000000e+00, double 0.000000e+00, metadata !"round.dynamic", metadata !"fpexcept.strict") #[[ATTR0]]
+; CHECK-NEXT: [[RESULT:%.*]] = call double @llvm.experimental.constrained.fsub.f64(double 0.000000e+00, double 0.000000e+00, metadata !"round.dynamic", metadata !"fpexcept.strict")
; CHECK-NEXT: ret double [[RESULT]]
;
entry:
- %result = call double @llvm.experimental.constrained.fsub.f64(double 0.0, double 0.0, metadata !"round.dynamic", metadata !"fpexcept.strict") #0
+ %result = call double @llvm.experimental.constrained.fsub.f64(double 0.0, double 0.0, metadata !"round.dynamic", metadata !"fpexcept.strict")
ret double %result
}
@@ -552,11 +552,11 @@ entry:
define double @fsub_zero_zero_tonearest() #0 {
; CHECK-LABEL: @fsub_zero_zero_tonearest(
; CHECK-NEXT: entry:
-; CHECK-NEXT: [[RESULT:%.*]] = call double @llvm.experimental.constrained.fsub.f64(double 0.000000e+00, double 0.000000e+00, metadata !"round.tonearest", metadata !"fpexcept.strict") #[[ATTR0]]
+; CHECK-NEXT: [[RESULT:%.*]] = call double @llvm.experimental.constrained.fsub.f64(double 0.000000e+00, double 0.000000e+00, metadata !"round.tonearest", metadata !"fpexcept.strict")
; CHECK-NEXT: ret double 0.000000e+00
;
entry:
- %result = call double @llvm.experimental.constrained.fsub.f64(double 0.0, double 0.0, metadata !"round.tonearest", metadata !"fpexcept.strict") #0
+ %result = call double @llvm.experimental.constrained.fsub.f64(double 0.0, double 0.0, metadata !"round.tonearest", metadata !"fpexcept.strict")
ret double %result
}
@@ -564,11 +564,11 @@ entry:
define double @fadd_one_one_dynamic() #0 {
; CHECK-LABEL: @fadd_one_one_dynamic(
; CHECK-NEXT: entry:
-; CHECK-NEXT: [[RESULT:%.*]] = call double @llvm.experimental.constrained.fadd.f64(double 1.000000e+00, double 1.000000e+00, metadata !"round.dynamic", metadata !"fpexcept.strict") #[[ATTR0]]
+; CHECK-NEXT: [[RESULT:%.*]] = call double @llvm.experimental.constrained.fadd.f64(double 1.000000e+00, double 1.000000e+00, metadata !"round.dynamic", metadata !"fpexcept.strict")
; CHECK-NEXT: ret double 2.000000e+00
;
entry:
- %result = call double @llvm.experimental.constrained.fadd.f64(double 1.0, double 1.0, metadata !"round.dynamic", metadata !"fpexcept.strict") #0
+ %result = call double @llvm.experimental.constrained.fadd.f64(double 1.0, double 1.0, metadata !"round.dynamic", metadata !"fpexcept.strict")
ret double %result
}
More information about the llvm-commits
mailing list