[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