[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 9 13:41:36 PDT 2024
https://github.com/davemgreen created https://github.com/llvm/llvm-project/pull/107912
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.
>From 1f9640839fce81143a143cf03ce5576365217a07 Mon Sep 17 00:00:00 2001
From: David Green <david.green at arm.com>
Date: Mon, 9 Sep 2024 17:31:30 +0100
Subject: [PATCH 1/2] [InstCombine] Test for fmod -> frem folding. NFC
---
llvm/test/Transforms/InstCombine/fmod.ll | 104 +++++++++++++++++++++++
1 file changed, 104 insertions(+)
create mode 100644 llvm/test/Transforms/InstCombine/fmod.ll
diff --git a/llvm/test/Transforms/InstCombine/fmod.ll b/llvm/test/Transforms/InstCombine/fmod.ll
new file mode 100644
index 00000000000000..80c42d546e6c9c
--- /dev/null
+++ b/llvm/test/Transforms/InstCombine/fmod.ll
@@ -0,0 +1,104 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 4
+; RUN: opt -S -passes=instcombine < %s | FileCheck %s
+
+define float @test_inf_const(float %f) {
+; CHECK-LABEL: define float @test_inf_const(
+; CHECK-SAME: float [[F:%.*]]) {
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[ABS:%.*]] = tail call float @llvm.fabs.f32(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: ret float [[CALL]]
+; CHECK: return:
+; CHECK-NEXT: ret float 0.000000e+00
+;
+entry:
+ %abs = tail call float @llvm.fabs.f32(float %f)
+ %isinf = fcmp oeq float %abs, 0x7FF0000000000000
+ br i1 %isinf, label %return, label %if.end
+
+if.end:
+ %call = tail call float @fmodf(float %f, float 2.0)
+ ret float %call
+
+return:
+ ret float 0.0
+}
+
+define float @test_const_zero(float %f) {
+; CHECK-LABEL: define float @test_const_zero(
+; CHECK-SAME: float [[F:%.*]]) {
+; CHECK-NEXT: entry:
+; 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: ret float [[CALL]]
+; CHECK: return:
+; CHECK-NEXT: ret float 0.000000e+00
+;
+entry:
+ %iszero = fcmp oeq float %f, 0.0
+ br i1 %iszero, label %return, label %if.end
+
+if.end:
+ %call = tail call float @fmodf(float 2.0, float %f)
+ ret float %call
+
+return:
+ ret float 0.0
+}
+
+define float @test_unknown_const(float %f) {
+; CHECK-LABEL: define float @test_unknown_const(
+; CHECK-SAME: float [[F:%.*]]) {
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[CALL:%.*]] = tail call float @fmodf(float [[F]], float 2.000000e+00)
+; CHECK-NEXT: ret float [[CALL]]
+;
+entry:
+ %call = tail call float @fmodf(float %f, float 2.000000e+00)
+ ret float %call
+}
+
+define float @test_noinf_nozero(float nofpclass(inf) %f, float nofpclass(zero) %g) {
+; 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: ret float [[CALL]]
+;
+entry:
+ %call = tail call nnan 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: ret double [[CALL]]
+;
+entry:
+ %call = tail call double @fmod(double %f, double %g)
+ ret double %call
+}
+
+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: ret fp128 [[CALL]]
+;
+entry:
+ %call = tail call fp128 @fmodl(fp128 %f, fp128 %g)
+ ret fp128 %call
+}
+
+declare float @fmodf(float, float)
+declare double @fmod(double, double)
+declare fp128 @fmodl(fp128, fp128)
>From aad5ab2a4bbed126e637adae3d8dea5809b5a90c Mon Sep 17 00:00:00 2001
From: David Green <david.green at arm.com>
Date: Mon, 9 Sep 2024 18:16:22 +0100
Subject: [PATCH 2/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 if the first
parameter is not inf and the second is not zero. Other combinations do not set
errno.
---
.../InstCombine/InstCombineCalls.cpp | 24 +++++++++++++++++++
llvm/test/Transforms/InstCombine/fmod.ll | 10 ++++----
2 files changed, 29 insertions(+), 5 deletions(-)
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp
index eb94e894b57b06..12e9cd7ba5c73f 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp
@@ -1526,6 +1526,30 @@ Instruction *InstCombinerImpl::visitCallInst(CallInst &CI) {
return &CI;
}
+ LibFunc Func = NotLibFunc;
+ if (TLI.getLibFunc(CI, Func)) {
+ switch (Func) {
+ default:
+ break;
+ case LibFunc_fmod:
+ case LibFunc_fmodf:
+ case LibFunc_fmodl: {
+ // fmod(x,y) can set errno if y == 0 or x == +/-inf.
+ KnownFPClass Known0 = computeKnownFPClass(CI.getOperand(0), fcInf, &CI);
+ if (Known0.isKnownNeverInfinity()) {
+ KnownFPClass Known1 =
+ computeKnownFPClass(CI.getOperand(1), fcZero, &CI);
+ if (Known1.isKnownNeverZero()) {
+ CI.replaceAllUsesWith(
+ Builder.CreateFRemFMF(CI.getOperand(0), CI.getOperand(1), &CI));
+ return eraseInstFromFunction(CI);
+ }
+ }
+ break;
+ }
+ }
+ }
+
IntrinsicInst *II = dyn_cast<IntrinsicInst>(&CI);
if (!II) return visitCallBase(CI);
diff --git a/llvm/test/Transforms/InstCombine/fmod.ll b/llvm/test/Transforms/InstCombine/fmod.ll
index 80c42d546e6c9c..d62976c4b2dd9b 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:
More information about the llvm-commits
mailing list