[llvm] [SimplifyLibCalls] Don't set nnan on synthesized frem in optimizeFMod (PR #199284)
Justin Lebar via llvm-commits
llvm-commits at lists.llvm.org
Fri May 22 15:29:30 PDT 2026
https://github.com/jlebar updated https://github.com/llvm/llvm-project/pull/199284
>From e2de399b17f4cafa5994ab9699bb00f821aafaba Mon Sep 17 00:00:00 2001
From: Justin Lebar <justin.lebar at gmail.com>
Date: Fri, 22 May 2026 00:22:25 -0700
Subject: [PATCH] [SimplifyLibCalls] Don't set nnan on synthesized frem in
optimizeFMod
LibCallSimplifier can fold a libcall to fmod to an frem instruction if
the fmod call doesn't set errno.
fmod(x, y) sets errno if x == +/-Inf or y == 0. The old code assumed
that this was also a sufficient condition to prove that the result is
not NaN, and so unconditionally set the nnan fmf on the new frem
instruction. That's not sound; e.g. fmod(x, NaN) == frem(x, NaN) ==
NaN.
We don't actually have to worry about propagating the `nnan` flag;
B.CreateFRemFMF does it for us automagically.
This bug was found by a large run of Opus 4.7 looking for bugs in LLVM.
---
.../lib/Transforms/Utils/SimplifyLibCalls.cpp | 19 ++++++---------
llvm/test/Transforms/InstCombine/fmod.ll | 24 +++++++++++++++----
2 files changed, 26 insertions(+), 17 deletions(-)
diff --git a/llvm/lib/Transforms/Utils/SimplifyLibCalls.cpp b/llvm/lib/Transforms/Utils/SimplifyLibCalls.cpp
index b60f0c7021d51..9584e0e1c825f 100644
--- a/llvm/lib/Transforms/Utils/SimplifyLibCalls.cpp
+++ b/llvm/lib/Transforms/Utils/SimplifyLibCalls.cpp
@@ -2853,11 +2853,10 @@ Value *LibCallSimplifier::optimizeSqrt(CallInst *CI, IRBuilderBase &B) {
Value *LibCallSimplifier::optimizeFMod(CallInst *CI, IRBuilderBase &B) {
- // fmod(x,y) can set errno if y == 0 or x == +/-inf, and returns Nan in those
- // case. If we know those do not happen, then we can convert the fmod into
- // frem.
- bool IsNoNan = CI->hasNoNaNs();
- if (!IsNoNan) {
+ // fmod(x,y) sets errno if y == 0 or x == +/-inf. frem does not set errno,
+ // so the fold is valid only when we can prove fmod wouldn't either.
+ bool IsNoErrno = CI->hasNoNaNs();
+ if (!IsNoErrno) {
SimplifyQuery SQ(DL, TLI, DT, AC, CI, true, true, DC);
KnownFPClass Known0 = computeKnownFPClass(CI->getOperand(0), fcInf, SQ);
if (Known0.isKnownNeverInfinity()) {
@@ -2866,16 +2865,12 @@ Value *LibCallSimplifier::optimizeFMod(CallInst *CI, IRBuilderBase &B) {
Function *F = CI->getParent()->getParent();
const fltSemantics &FltSem =
CI->getType()->getScalarType()->getFltSemantics();
- IsNoNan = Known1.isKnownNeverLogicalZero(F->getDenormalMode(FltSem));
+ IsNoErrno = Known1.isKnownNeverLogicalZero(F->getDenormalMode(FltSem));
}
}
- if (IsNoNan) {
- Value *FRem = B.CreateFRemFMF(CI->getOperand(0), CI->getOperand(1), CI);
- if (auto *FRemI = dyn_cast<Instruction>(FRem))
- FRemI->setHasNoNaNs(true);
- return FRem;
- }
+ if (IsNoErrno)
+ return B.CreateFRemFMF(CI->getOperand(0), CI->getOperand(1), CI);
return nullptr;
}
diff --git a/llvm/test/Transforms/InstCombine/fmod.ll b/llvm/test/Transforms/InstCombine/fmod.ll
index 804812abadbbb..a1ef7d6b2c61a 100644
--- a/llvm/test/Transforms/InstCombine/fmod.ll
+++ b/llvm/test/Transforms/InstCombine/fmod.ll
@@ -9,7 +9,7 @@ define float @test_inf_const(float %f) {
; CHECK-NEXT: [[ISINF:%.*]] = fcmp oeq float [[ABS]], +inf
; CHECK-NEXT: br i1 [[ISINF]], label [[RETURN:%.*]], label [[IF_END:%.*]]
; CHECK: if.end:
-; CHECK-NEXT: [[CALL:%.*]] = frem nnan float [[F]], 2.000000e+00
+; CHECK-NEXT: [[CALL:%.*]] = frem float [[F]], 2.000000e+00
; CHECK-NEXT: ret float [[CALL]]
; CHECK: return:
; CHECK-NEXT: ret float 0.000000e+00
@@ -34,7 +34,7 @@ define float @test_const_zero(float %f) {
; CHECK-NEXT: [[ISZERO:%.*]] = fcmp oeq float [[F]], 0.000000e+00
; CHECK-NEXT: br i1 [[ISZERO]], label [[RETURN:%.*]], label [[IF_END:%.*]]
; CHECK: if.end:
-; CHECK-NEXT: [[CALL:%.*]] = frem nnan float 2.000000e+00, [[F]]
+; CHECK-NEXT: [[CALL:%.*]] = frem float 2.000000e+00, [[F]]
; CHECK-NEXT: ret float [[CALL]]
; CHECK: return:
; CHECK-NEXT: ret float 0.000000e+00
@@ -67,7 +67,7 @@ define float @test_noinf_nozero(float nofpclass(inf) %f, float nofpclass(zero) %
; CHECK-LABEL: define float @test_noinf_nozero(
; CHECK-SAME: float nofpclass(inf) [[F:%.*]], float nofpclass(zero) [[G:%.*]]) {
; CHECK-NEXT: entry:
-; CHECK-NEXT: [[CALL:%.*]] = frem nnan float [[F]], [[G]]
+; CHECK-NEXT: [[CALL:%.*]] = frem float [[F]], [[G]]
; CHECK-NEXT: ret float [[CALL]]
;
entry:
@@ -79,7 +79,7 @@ define double @test_double(double nofpclass(inf) %f, double nofpclass(zero) %g)
; CHECK-LABEL: define double @test_double(
; CHECK-SAME: double nofpclass(inf) [[F:%.*]], double nofpclass(zero) [[G:%.*]]) {
; CHECK-NEXT: entry:
-; CHECK-NEXT: [[CALL:%.*]] = frem nnan double [[F]], [[G]]
+; CHECK-NEXT: [[CALL:%.*]] = frem double [[F]], [[G]]
; CHECK-NEXT: ret double [[CALL]]
;
entry:
@@ -91,7 +91,7 @@ define fp128 @test_fp128(fp128 nofpclass(inf) %f, fp128 nofpclass(zero) %g) {
; CHECK-LABEL: define fp128 @test_fp128(
; CHECK-SAME: fp128 nofpclass(inf) [[F:%.*]], fp128 nofpclass(zero) [[G:%.*]]) {
; CHECK-NEXT: entry:
-; CHECK-NEXT: [[CALL:%.*]] = frem nnan fp128 [[F]], [[G]]
+; CHECK-NEXT: [[CALL:%.*]] = frem fp128 [[F]], [[G]]
; CHECK-NEXT: ret fp128 [[CALL]]
;
entry:
@@ -123,6 +123,20 @@ entry:
ret float %call
}
+define float @test_nan_input_noerrno(float nofpclass(zero) %g, i1 %c) {
+; CHECK-LABEL: define float @test_nan_input_noerrno(
+; CHECK-SAME: float nofpclass(zero) [[G:%.*]], i1 [[C:%.*]]) {
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[F:%.*]] = select i1 [[C]], float 0x7FF8000000000000, float 1.000000e+00
+; CHECK-NEXT: [[CALL:%.*]] = frem float [[F]], [[G]]
+; CHECK-NEXT: ret float [[CALL]]
+;
+entry:
+ %f = select i1 %c, float 0x7FF8000000000000, float 1.0
+ %call = tail call float @fmodf(float %f, float %g)
+ ret float %call
+}
+
define float @test_nnan(float %f, float %g) {
; CHECK-LABEL: define float @test_nnan(
; CHECK-SAME: float [[F:%.*]], float [[G:%.*]]) {
More information about the llvm-commits
mailing list