[llvm] [InstCombine] Fold fmod to frem if we know it does not set errno. (PR #107912)
David Green via llvm-commits
llvm-commits at lists.llvm.org
Mon Sep 16 04:06:25 PDT 2024
https://github.com/davemgreen updated https://github.com/llvm/llvm-project/pull/107912
>From 3b5837932c16b8f6aefe1e331f7125ba399bb322 Mon Sep 17 00:00:00 2001
From: David Green <david.green at arm.com>
Date: Fri, 13 Sep 2024 08:37:10 +0100
Subject: [PATCH 1/2] [InstCombine] Fold fmod to frem if we know it does not
set errno.
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 (via
LibCallSimplifier) if the first parameter is not inf and the second is not
zero. Other combinations do not set errno.
---
.../llvm/Transforms/Utils/SimplifyLibCalls.h | 1 +
.../lib/Transforms/Utils/SimplifyLibCalls.cpp | 32 +++++++++++++++++++
llvm/test/Transforms/InstCombine/fmod.ll | 10 +++---
3 files changed, 38 insertions(+), 5 deletions(-)
diff --git a/llvm/include/llvm/Transforms/Utils/SimplifyLibCalls.h b/llvm/include/llvm/Transforms/Utils/SimplifyLibCalls.h
index 2e7a0ec29ed999..2d3d2ada6183a7 100644
--- a/llvm/include/llvm/Transforms/Utils/SimplifyLibCalls.h
+++ b/llvm/include/llvm/Transforms/Utils/SimplifyLibCalls.h
@@ -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);
diff --git a/llvm/lib/Transforms/Utils/SimplifyLibCalls.cpp b/llvm/lib/Transforms/Utils/SimplifyLibCalls.cpp
index 917f81863cf673..6fd15659bc8ce2 100644
--- a/llvm/lib/Transforms/Utils/SimplifyLibCalls.cpp
+++ b/llvm/lib/Transforms/Utils/SimplifyLibCalls.cpp
@@ -2796,6 +2796,34 @@ 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);
+
+ if (CI->hasNoNaNs()) {
+ Value *FRem = B.CreateFRemFMF(CI->getOperand(0), CI->getOperand(1), CI);
+ substituteInParent(CI, FRem);
+ return nullptr;
+ }
+
+ // fmod(x,y) can set errno if y == 0 or x == +/-inf. Otherwise we can change
+ // it to a frem instruction.
+ 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())) {
+ Value *FRem = B.CreateFRemFMF(CI->getOperand(0), CI->getOperand(1), CI);
+ substituteInParent(CI, FRem);
+ return nullptr;
+ }
+ }
+
+ return nullptr;
+}
+
Value *LibCallSimplifier::optimizeTrigInversionPairs(CallInst *CI,
IRBuilderBase &B) {
Module *M = CI->getModule();
@@ -3945,6 +3973,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:
diff --git a/llvm/test/Transforms/InstCombine/fmod.ll b/llvm/test/Transforms/InstCombine/fmod.ll
index c021d27e95fa5e..b9ad1571090578 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]], 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 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:%.*]] = tail call float @fmodf(float 2.000000e+00, float [[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:%.*]] = tail call nnan float @fmodf(float [[F]], float [[G]])
+; CHECK-NEXT: [[CALL:%.*]] = frem nnan 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:%.*]] = tail call double @fmod(double [[F]], double [[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:%.*]] = tail call fp128 @fmodl(fp128 [[F]], fp128 [[G]])
+; CHECK-NEXT: [[CALL:%.*]] = frem fp128 [[F]], [[G]]
; CHECK-NEXT: ret fp128 [[CALL]]
;
entry:
>From 695c8953d7ff999f471a3eedb8b88711e5cb5d53 Mon Sep 17 00:00:00 2001
From: David Green <david.green at arm.com>
Date: Mon, 16 Sep 2024 12:01:48 +0100
Subject: [PATCH 2/2] Change to test and set NoNan
---
.../lib/Transforms/Utils/SimplifyLibCalls.cpp | 39 ++++++++++---------
llvm/test/Transforms/InstCombine/fmod.ll | 28 +++++++++----
2 files changed, 40 insertions(+), 27 deletions(-)
diff --git a/llvm/lib/Transforms/Utils/SimplifyLibCalls.cpp b/llvm/lib/Transforms/Utils/SimplifyLibCalls.cpp
index 6fd15659bc8ce2..4933b5bf60eea8 100644
--- a/llvm/lib/Transforms/Utils/SimplifyLibCalls.cpp
+++ b/llvm/lib/Transforms/Utils/SimplifyLibCalls.cpp
@@ -2799,28 +2799,29 @@ Value *LibCallSimplifier::optimizeSqrt(CallInst *CI, IRBuilderBase &B) {
Value *LibCallSimplifier::optimizeFMod(CallInst *CI, IRBuilderBase &B) {
SimplifyQuery SQ(DL, TLI, DT, AC, CI, true, true, DC);
- if (CI->hasNoNaNs()) {
- Value *FRem = B.CreateFRemFMF(CI->getOperand(0), CI->getOperand(1), CI);
- substituteInParent(CI, FRem);
- return nullptr;
- }
-
- // fmod(x,y) can set errno if y == 0 or x == +/-inf. Otherwise we can change
- // it to a frem instruction.
- 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())) {
- Value *FRem = B.CreateFRemFMF(CI->getOperand(0), CI->getOperand(1), CI);
- substituteInParent(CI, FRem);
- return nullptr;
+ // 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;
}
diff --git a/llvm/test/Transforms/InstCombine/fmod.ll b/llvm/test/Transforms/InstCombine/fmod.ll
index b9ad1571090578..10cff189b8dfca 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]], 0x7FF0000000000000
; CHECK-NEXT: br i1 [[ISINF]], label [[RETURN:%.*]], label [[IF_END:%.*]]
; CHECK: if.end:
-; CHECK-NEXT: [[CALL:%.*]] = frem float [[F]], 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
@@ -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 float 2.000000e+00, [[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
@@ -71,7 +71,7 @@ define float @test_noinf_nozero(float nofpclass(inf) %f, float nofpclass(zero) %
; 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
}
@@ -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 double [[F]], [[G]]
+; CHECK-NEXT: [[CALL:%.*]] = frem nnan 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 fp128 [[F]], [[G]]
+; CHECK-NEXT: [[CALL:%.*]] = frem nnan fp128 [[F]], [[G]]
; CHECK-NEXT: ret fp128 [[CALL]]
;
entry:
@@ -103,11 +103,11 @@ 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
}
@@ -115,7 +115,19 @@ define float @test_noinf_nozero_dazdynamic(float nofpclass(inf) %f, float nofpcl
; 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:
More information about the llvm-commits
mailing list