Skip to content

Commit

Permalink
[InstCombine] Fold fmod to frem if we know it does not set errno. (#1…
Browse files Browse the repository at this point in the history
…07912)

fmod will be folded to frem in clang under -fno-math-errno and can be constant
folded in llvm if the operands are known. It can be relatively common to have
fp code that handles special values before doing some calculation:
```
if (isnan(f))
  return handlenan;
if (isinf(f))
  return handleinf;
..
fmod(f, 2.0)
```

This patch enables the folding of fmod to frem in instcombine if the first
parameter is not inf and the second is not zero. Other combinations do not set
errno.

The same transform is performed for fmod with the nnan flag, which implies the
input is known to not be inf/zero.
  • Loading branch information
davemgreen authored Sep 18, 2024
1 parent d2d947b commit 112aac4
Show file tree
Hide file tree
Showing 3 changed files with 55 additions and 9 deletions.
1 change: 1 addition & 0 deletions llvm/include/llvm/Transforms/Utils/SimplifyLibCalls.h
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,7 @@ class LibCallSimplifier {
Value *optimizeFMinFMax(CallInst *CI, IRBuilderBase &B);
Value *optimizeLog(CallInst *CI, IRBuilderBase &B);
Value *optimizeSqrt(CallInst *CI, IRBuilderBase &B);
Value *optimizeFMod(CallInst *CI, IRBuilderBase &B);
Value *mergeSqrtToExp(CallInst *CI, IRBuilderBase &B);
Value *optimizeSinCosPi(CallInst *CI, bool IsSin, IRBuilderBase &B);
Value *optimizeTrigInversionPairs(CallInst *CI, IRBuilderBase &B);
Expand Down
33 changes: 33 additions & 0 deletions llvm/lib/Transforms/Utils/SimplifyLibCalls.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2796,6 +2796,35 @@ Value *LibCallSimplifier::optimizeSqrt(CallInst *CI, IRBuilderBase &B) {
return copyFlags(*CI, FabsCall);
}

Value *LibCallSimplifier::optimizeFMod(CallInst *CI, IRBuilderBase &B) {
SimplifyQuery SQ(DL, TLI, DT, AC, CI, true, true, DC);

// 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) {
KnownFPClass Known0 = computeKnownFPClass(CI->getOperand(0), fcInf,
/*Depth=*/0, SQ);
if (Known0.isKnownNeverInfinity()) {
KnownFPClass Known1 =
computeKnownFPClass(CI->getOperand(1), fcZero | fcSubnormal,
/*Depth=*/0, SQ);
Function *F = CI->getParent()->getParent();
if (Known1.isKnownNeverLogicalZero(*F, CI->getType()))
IsNoNan = true;
}
}

if (IsNoNan) {
Value *FRem = B.CreateFRemFMF(CI->getOperand(0), CI->getOperand(1), CI);
if (auto *FRemI = dyn_cast<Instruction>(FRem))
FRemI->setHasNoNaNs(true);
substituteInParent(CI, FRem);
}
return nullptr;
}

Value *LibCallSimplifier::optimizeTrigInversionPairs(CallInst *CI,
IRBuilderBase &B) {
Module *M = CI->getModule();
Expand Down Expand Up @@ -3945,6 +3974,10 @@ Value *LibCallSimplifier::optimizeFloatingPointLibCall(CallInst *CI,
case LibFunc_sqrt:
case LibFunc_sqrtl:
return optimizeSqrt(CI, Builder);
case LibFunc_fmod:
case LibFunc_fmodf:
case LibFunc_fmodl:
return optimizeFMod(CI, Builder);
case LibFunc_logf:
case LibFunc_log:
case LibFunc_logl:
Expand Down
30 changes: 21 additions & 9 deletions llvm/test/Transforms/InstCombine/fmod.ll
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ define float @test_inf_const(float %f) {
; CHECK-NEXT: [[ISINF:%.*]] = fcmp oeq float [[ABS]], 0x7FF0000000000000
; CHECK-NEXT: br i1 [[ISINF]], label [[RETURN:%.*]], label [[IF_END:%.*]]
; CHECK: if.end:
; CHECK-NEXT: [[CALL:%.*]] = tail call float @fmodf(float [[F]], float 2.000000e+00)
; CHECK-NEXT: [[CALL:%.*]] = frem nnan float [[F]], 2.000000e+00
; CHECK-NEXT: ret float [[CALL]]
; CHECK: return:
; CHECK-NEXT: ret float 0.000000e+00
Expand All @@ -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:%.*]] = tail call float @fmodf(float 2.000000e+00, float [[F]])
; CHECK-NEXT: [[CALL:%.*]] = frem nnan float 2.000000e+00, [[F]]
; CHECK-NEXT: ret float [[CALL]]
; CHECK: return:
; CHECK-NEXT: ret float 0.000000e+00
Expand Down Expand Up @@ -67,19 +67,19 @@ 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:%.*]] = tail call nnan float @fmodf(float [[F]], float [[G]])
; CHECK-NEXT: [[CALL:%.*]] = frem nnan float [[F]], [[G]]
; CHECK-NEXT: ret float [[CALL]]
;
entry:
%call = tail call nnan float @fmodf(float %f, float %g)
%call = tail call float @fmodf(float %f, float %g)
ret float %call
}

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:%.*]] = tail call double @fmod(double [[F]], double [[G]])
; CHECK-NEXT: [[CALL:%.*]] = frem nnan double [[F]], [[G]]
; CHECK-NEXT: ret double [[CALL]]
;
entry:
Expand All @@ -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:%.*]] = tail call fp128 @fmodl(fp128 [[F]], fp128 [[G]])
; CHECK-NEXT: [[CALL:%.*]] = frem nnan fp128 [[F]], [[G]]
; CHECK-NEXT: ret fp128 [[CALL]]
;
entry:
Expand All @@ -103,19 +103,31 @@ define float @test_noinf_nozero_dazpreservesign(float nofpclass(inf) %f, float n
; CHECK-LABEL: define float @test_noinf_nozero_dazpreservesign(
; CHECK-SAME: float nofpclass(inf) [[F:%.*]], float nofpclass(zero) [[G:%.*]]) #[[ATTR0:[0-9]+]] {
; CHECK-NEXT: entry:
; CHECK-NEXT: [[CALL:%.*]] = tail call nnan float @fmodf(float [[F]], float [[G]])
; CHECK-NEXT: [[CALL:%.*]] = tail call float @fmodf(float [[F]], float [[G]])
; CHECK-NEXT: ret float [[CALL]]
;
entry:
%call = tail call nnan float @fmodf(float %f, float %g)
%call = tail call float @fmodf(float %f, float %g)
ret float %call
}

define float @test_noinf_nozero_dazdynamic(float nofpclass(inf) %f, float nofpclass(zero) %g) "denormal-fp-math"="dynamic,dynamic" {
; CHECK-LABEL: define float @test_noinf_nozero_dazdynamic(
; CHECK-SAME: float nofpclass(inf) [[F:%.*]], float nofpclass(zero) [[G:%.*]]) #[[ATTR1:[0-9]+]] {
; CHECK-NEXT: entry:
; CHECK-NEXT: [[CALL:%.*]] = tail call nnan float @fmodf(float [[F]], float [[G]])
; CHECK-NEXT: [[CALL:%.*]] = tail call float @fmodf(float [[F]], float [[G]])
; CHECK-NEXT: ret float [[CALL]]
;
entry:
%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:%.*]]) {
; CHECK-NEXT: entry:
; CHECK-NEXT: [[CALL:%.*]] = frem nnan float [[F]], [[G]]
; CHECK-NEXT: ret float [[CALL]]
;
entry:
Expand Down

0 comments on commit 112aac4

Please sign in to comment.