[llvm] [InstCombine] Fold fmod to frem if we know it does not set errno. (PR #107912)
via llvm-commits
llvm-commits at lists.llvm.org
Mon Sep 9 13:42:09 PDT 2024
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-llvm-transforms
Author: David Green (davemgreen)
<details>
<summary>Changes</summary>
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.
---
Full diff: https://github.com/llvm/llvm-project/pull/107912.diff
2 Files Affected:
- (modified) llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp (+24)
- (added) llvm/test/Transforms/InstCombine/fmod.ll (+104)
``````````diff
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
new file mode 100644
index 00000000000000..d62976c4b2dd9b
--- /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:%.*]] = frem float [[F]], 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:%.*]] = frem float 2.000000e+00, [[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:%.*]] = frem nnan float [[F]], [[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:%.*]] = frem double [[F]], [[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:%.*]] = frem fp128 [[F]], [[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)
``````````
</details>
https://github.com/llvm/llvm-project/pull/107912
More information about the llvm-commits
mailing list