[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